以对数刻度计算段的长度
Calculating the length of a segment in a logarithmic scale
我想计算一系列事件的直线长度。
我正在使用以下代码执行此操作。
var maxLineLength = 20;
var lineLen = function(x, max) {
return maxLineLength * (x / max);
}
var events = [0.1, 1, 5, 20, 50];
var max = Math.max.apply(null, events);
events.map(function (x) {
console.log(lineLen(x, max));
});
这可行,但我使用线性缩放,而我想使用对数,因为我不希望小事件在出现大事件时变得太小。
我修改了 lineLen 函数,如下所示,但是 - 显然 - 它不适用于等于 1 的事件,因为 1 的对数为零。我想显示事件等于一(适当缩放)而不是让它们变为零。我还需要正数才能保持正数(0.1变成负数)
我应该如何修改 lineLen 以使用对数刻度?
var maxLineLength = 20;
var lineLen = function(x, max) {
return maxLineLength * (Math.log(x) / Math.log(max));
}
var events = [0.1, 1, 5, 20, 50];
var max = Math.max.apply(null, events);
events.map(function (x) {
console.log(lineLen(x, max));
});
您可以在计算结束时减 maxLineLength
并加一。
对于小于 1 的值,您可以使用一个因子来标准化所有相对于第一个值的值。起始值始终为一,或者在对数视图中为零。
var maxLineLength = 20,
lineLen = function(max) {
return function (x) {
return (maxLineLength - 1) * Math.log(x) / Math.log(max) + 1;
};
},
events = [0.1, 1, 5, 20, 50],
normalized = events.map((v, _, a) => v / a[0]),
max = Math.max.apply(null, normalized),
result = normalized.map(lineLen(max));
console.log(result);
console.log(normalized);
.as-console-wrapper { max-height: 100% !important; top: 0; }
更短但又不会太短;更长但不会太长。
其实我几年前也遇到过同样的问题,为此放弃了(也许吧?)。
我刚刚在这里和现在阅读了您的问题,我想我刚刚找到了解决方案:shifting.
const log = (base, value) => (Math.log(value) / Math.log(base));
const weights = [0, 0.1, 1, 5, 20, 50, 100];
const base = Math.E; // Setting
const shifted = weights.map(x => x + base);
const logged = shifted.map(x => log(base, x));
const unshifted = logged.map(x => x - 1);
const total = unshifted.reduce((a, b) => a + b, 0);
const ratio = unshifted.map(x => x / total);
const percents = ratio.map(x => x * 100);
console.log(percents);
// [
// 0,
// 0.35723375538333857,
// 3.097582209424984,
// 10.3192042142806,
// 20.994247877004888,
// 29.318026542735115,
// 35.91370540117108
// ]
可视化
对数底越小,越调整;
反之亦然。
其实我也不知道是什么原因。 XD
<!DOCTYPE html>
<html>
<head>
<meta name="author" content="K.">
<title>Shorter but not too short; longer but not too long.</title>
<style>
canvas
{
background-color: whitesmoke;
}
</style>
</head>
<body>
<canvas id="canvas" height="5"></canvas>
<label>Weights: <input id="weights" type="text" value="[0, 0.1, 100, 1, 5, 20, 2.718, 50]">.</label>
<label>Base: <input id="base" type="number" value="2.718281828459045">.</label>
<button id="draw" type="button">Draw</button>
<script>
const input = new Proxy({}, {
get(_, thing)
{
return eval(document.getElementById(thing).value);
}
});
const color = ["tomato", "black"];
const canvas_element = document.getElementById("canvas");
const canvas_context = canvas_element.getContext("2d");
canvas_element.width = document.body.clientWidth;
document.getElementById("draw").addEventListener("click", _ => {
const log = (base, value) => (Math.log(value) / Math.log(base));
const weights = input.weights;
const base = input.base;
const orig_total = weights.reduce((a, b) => a + b, 0);
const orig_percents = weights.map(x => x / orig_total * 100);
const adjusted = weights.map(x => x + base);
const logged = adjusted.map(x => log(base, x));
const rebased = logged.map(x => x - 1);
const total = rebased.reduce((a, b) => a + b, 0);
const ratio = rebased.map(x => x / total);
const percents = ratio.map(x => x * 100);
const result = percents.map((percent, index) => `${weights[index]} | ${orig_percents[index]}% --> ${percent}% (${color[index & 1]})`);
console.info(result);
let position = 0;
ratio.forEach((rate, index) => {
canvas_context.beginPath();
canvas_context.moveTo(position, 0);
position += (canvas_element.width * rate);
canvas_context.lineTo(position, 0);
canvas_context.lineWidth = 10;
canvas_context.strokeStyle = color[index & 1];
canvas_context.stroke();
});
});
</script>
</body>
</html>
您可以使用 Math.pow(x, 0.35)
之类的表达式来代替 Math.log(x)
。它使所有值保持正值,并为小比率提供您想要的行为。您可以在 (0,1) 范围内试验不同的指数值,以找到适合您需要的那个。
var maxLineLength = 20;
var exponent = 0.35;
var lineLen = function(x, max) {
return maxLineLength * Math.pow(x/max, exponent);
}
var events = [0, 0.01, 0.1, 1, 5, 20, 50];
var max = Math.max.apply(null, events);
events.map(function (x) {
console.log(lineLen(x, max));
});
您可以使用 log(x+1)
而不是 log(x)
,这不会对值造成太大的影响,并且比率会保持较小的数字。
var maxLineLength = 20;
var lineLen = (x, max) => maxLineLength * Math.log(x+1)/Math.log(max+1);
var events = [ 0.1, 1, 5, 20, 50];
var visualizer = function(events){
var max = Math.max.apply(null, events);
return events.reduce((y, x) => {
y.push(lineLen(x, max));
return y;
}, []);
};
console.log(visualizer(events));
您正在缩放这些数字。您的起始集是域,最终得到的是范围。听起来,这种转变的形状要么是对数,要么是服从幂。
原来这在很多领域都是很普遍的问题,尤其是数据可视化。这就是为什么 D3.js - 数据可视化工具 - everything you need 可以轻松做到这一点。
const x = d3.scale.log().range([0, events]);
这是工作的权利。如果您需要做一些图表,一切就绪!
只要您的事件 > 0,您就可以通过缩放事件来避免移动,因此最小值大于 1。标量可以根据您已有的最大行长度之外的最小行长度来计算.
// generates an array of line lengths between minLineLength and maxLineLength
// assumes events contains only values > 0 and 0 < minLineLength < maxLineLength
function generateLineLengths(events, minLineLength, maxLineLength) {
var min = Math.min.apply(null, events);
var max = Math.max.apply(null, events);
//calculate scalar that sets line length for the minimum value in events to minLineLength
var mmr = minLineLength / maxLineLength;
var scalar = Math.pow(Math.pow(max, mmr) / min, 1 / (1 - mmr));
function lineLength(x) {
return maxLineLength * (Math.log(x * scalar) / Math.log(max * scalar));
}
return events.map(lineLength)
}
var events = [0.1, 1, 5, 20, 50];
console.log('Between 1 and 20')
generateLineLengths(events, 1, 20).forEach(function(x) {
console.log(x)
})
// 1
// 8.039722549519123
// 12.960277450480875
// 17.19861274759562
// 20
console.log('Between 5 and 25')
generateLineLengths(events, 5, 25).forEach(function(x) {
console.log(x)
})
// 5
// 12.410234262651711
// 17.58976573734829
// 22.05117131325855
// 25
我想计算一系列事件的直线长度。
我正在使用以下代码执行此操作。
var maxLineLength = 20;
var lineLen = function(x, max) {
return maxLineLength * (x / max);
}
var events = [0.1, 1, 5, 20, 50];
var max = Math.max.apply(null, events);
events.map(function (x) {
console.log(lineLen(x, max));
});
这可行,但我使用线性缩放,而我想使用对数,因为我不希望小事件在出现大事件时变得太小。
我修改了 lineLen 函数,如下所示,但是 - 显然 - 它不适用于等于 1 的事件,因为 1 的对数为零。我想显示事件等于一(适当缩放)而不是让它们变为零。我还需要正数才能保持正数(0.1变成负数)
我应该如何修改 lineLen 以使用对数刻度?
var maxLineLength = 20;
var lineLen = function(x, max) {
return maxLineLength * (Math.log(x) / Math.log(max));
}
var events = [0.1, 1, 5, 20, 50];
var max = Math.max.apply(null, events);
events.map(function (x) {
console.log(lineLen(x, max));
});
您可以在计算结束时减 maxLineLength
并加一。
对于小于 1 的值,您可以使用一个因子来标准化所有相对于第一个值的值。起始值始终为一,或者在对数视图中为零。
var maxLineLength = 20,
lineLen = function(max) {
return function (x) {
return (maxLineLength - 1) * Math.log(x) / Math.log(max) + 1;
};
},
events = [0.1, 1, 5, 20, 50],
normalized = events.map((v, _, a) => v / a[0]),
max = Math.max.apply(null, normalized),
result = normalized.map(lineLen(max));
console.log(result);
console.log(normalized);
.as-console-wrapper { max-height: 100% !important; top: 0; }
更短但又不会太短;更长但不会太长。
其实我几年前也遇到过同样的问题,为此放弃了(也许吧?)。 我刚刚在这里和现在阅读了您的问题,我想我刚刚找到了解决方案:shifting.
const log = (base, value) => (Math.log(value) / Math.log(base));
const weights = [0, 0.1, 1, 5, 20, 50, 100];
const base = Math.E; // Setting
const shifted = weights.map(x => x + base);
const logged = shifted.map(x => log(base, x));
const unshifted = logged.map(x => x - 1);
const total = unshifted.reduce((a, b) => a + b, 0);
const ratio = unshifted.map(x => x / total);
const percents = ratio.map(x => x * 100);
console.log(percents);
// [
// 0,
// 0.35723375538333857,
// 3.097582209424984,
// 10.3192042142806,
// 20.994247877004888,
// 29.318026542735115,
// 35.91370540117108
// ]
可视化
对数底越小,越调整; 反之亦然。 其实我也不知道是什么原因。 XD
<!DOCTYPE html>
<html>
<head>
<meta name="author" content="K.">
<title>Shorter but not too short; longer but not too long.</title>
<style>
canvas
{
background-color: whitesmoke;
}
</style>
</head>
<body>
<canvas id="canvas" height="5"></canvas>
<label>Weights: <input id="weights" type="text" value="[0, 0.1, 100, 1, 5, 20, 2.718, 50]">.</label>
<label>Base: <input id="base" type="number" value="2.718281828459045">.</label>
<button id="draw" type="button">Draw</button>
<script>
const input = new Proxy({}, {
get(_, thing)
{
return eval(document.getElementById(thing).value);
}
});
const color = ["tomato", "black"];
const canvas_element = document.getElementById("canvas");
const canvas_context = canvas_element.getContext("2d");
canvas_element.width = document.body.clientWidth;
document.getElementById("draw").addEventListener("click", _ => {
const log = (base, value) => (Math.log(value) / Math.log(base));
const weights = input.weights;
const base = input.base;
const orig_total = weights.reduce((a, b) => a + b, 0);
const orig_percents = weights.map(x => x / orig_total * 100);
const adjusted = weights.map(x => x + base);
const logged = adjusted.map(x => log(base, x));
const rebased = logged.map(x => x - 1);
const total = rebased.reduce((a, b) => a + b, 0);
const ratio = rebased.map(x => x / total);
const percents = ratio.map(x => x * 100);
const result = percents.map((percent, index) => `${weights[index]} | ${orig_percents[index]}% --> ${percent}% (${color[index & 1]})`);
console.info(result);
let position = 0;
ratio.forEach((rate, index) => {
canvas_context.beginPath();
canvas_context.moveTo(position, 0);
position += (canvas_element.width * rate);
canvas_context.lineTo(position, 0);
canvas_context.lineWidth = 10;
canvas_context.strokeStyle = color[index & 1];
canvas_context.stroke();
});
});
</script>
</body>
</html>
您可以使用 Math.pow(x, 0.35)
之类的表达式来代替 Math.log(x)
。它使所有值保持正值,并为小比率提供您想要的行为。您可以在 (0,1) 范围内试验不同的指数值,以找到适合您需要的那个。
var maxLineLength = 20;
var exponent = 0.35;
var lineLen = function(x, max) {
return maxLineLength * Math.pow(x/max, exponent);
}
var events = [0, 0.01, 0.1, 1, 5, 20, 50];
var max = Math.max.apply(null, events);
events.map(function (x) {
console.log(lineLen(x, max));
});
您可以使用 log(x+1)
而不是 log(x)
,这不会对值造成太大的影响,并且比率会保持较小的数字。
var maxLineLength = 20;
var lineLen = (x, max) => maxLineLength * Math.log(x+1)/Math.log(max+1);
var events = [ 0.1, 1, 5, 20, 50];
var visualizer = function(events){
var max = Math.max.apply(null, events);
return events.reduce((y, x) => {
y.push(lineLen(x, max));
return y;
}, []);
};
console.log(visualizer(events));
您正在缩放这些数字。您的起始集是域,最终得到的是范围。听起来,这种转变的形状要么是对数,要么是服从幂。
原来这在很多领域都是很普遍的问题,尤其是数据可视化。这就是为什么 D3.js - 数据可视化工具 - everything you need 可以轻松做到这一点。
const x = d3.scale.log().range([0, events]);
这是工作的权利。如果您需要做一些图表,一切就绪!
只要您的事件 > 0,您就可以通过缩放事件来避免移动,因此最小值大于 1。标量可以根据您已有的最大行长度之外的最小行长度来计算.
// generates an array of line lengths between minLineLength and maxLineLength
// assumes events contains only values > 0 and 0 < minLineLength < maxLineLength
function generateLineLengths(events, minLineLength, maxLineLength) {
var min = Math.min.apply(null, events);
var max = Math.max.apply(null, events);
//calculate scalar that sets line length for the minimum value in events to minLineLength
var mmr = minLineLength / maxLineLength;
var scalar = Math.pow(Math.pow(max, mmr) / min, 1 / (1 - mmr));
function lineLength(x) {
return maxLineLength * (Math.log(x * scalar) / Math.log(max * scalar));
}
return events.map(lineLength)
}
var events = [0.1, 1, 5, 20, 50];
console.log('Between 1 and 20')
generateLineLengths(events, 1, 20).forEach(function(x) {
console.log(x)
})
// 1
// 8.039722549519123
// 12.960277450480875
// 17.19861274759562
// 20
console.log('Between 5 and 25')
generateLineLengths(events, 5, 25).forEach(function(x) {
console.log(x)
})
// 5
// 12.410234262651711
// 17.58976573734829
// 22.05117131325855
// 25