简体   繁体   English

以对数标度计算段的长度

[英]Calculating the length of a segment in a logarithmic scale

I want to calculate the length of a line for a series of events. 我想计算一系列事件的一条线的长度。

I'm doing this with the following code. 我正在使用以下代码执行此操作。

 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)); }); 

This works, but I'm using linear scaling, while I'd like to use logarithms, because I don't want small events to become too small numbers when big ones are present. 这是有效的,但我正在使用线性缩放,而我想使用对数,因为我不希望小事件在存在大事件时变得太小。

I modified the lineLen function as you can see below, but - obviously - it doesn't work for events equals to one, because the log of one is zero. 我修改了lineLen函数,如下所示,但是 - 显然 - 它不适用于事件等于1,因为1的日志为零。 I want to show events equals to one (opportunely scaled) and not make them become zero. 我想显示事件等于一(适当缩放)而不是使它们变为零。 I also need positive numbers to remain positive (0.1 becomes a negative number) 我还需要正数来保持正数(0.1变为负数)

How should I modify lineLen to use a logarithmic scale? 我应该如何修改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)); }); 

You can use an expression like Math.pow(x, 0.35) instead of Math.log(x) . 您可以使用Math.pow(x, 0.35)类的表达式代替Math.log(x) It keeps all values positive, and gives the behavior that you want for small ratios. 它使所有值保持正值,并为小比率提供所需的行为。 You can experiment with different exponent values in the range (0,1) to find the one that fits your needs. 您可以在范围(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)); }); 

You can take log(x+1) instead of log(x) , that doesn't change the value too much and the ratios are maintained for smaller numbers. 您可以使用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)); 

You could decrement maxLineLength and add one at the end of the calculation. 您可以递减maxLineLength并在计算结束时添加一个。

For values smaller than one, you could use a factor which normalizes all values relative to the first value. 对于小于1的值,您可以使用相对于第一个值规范化所有值的因子。 The start value is always one, or in terms of logaritmic view, zero. 起始值始终为1,或者以logaritmic视图为零。

 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; } 

Shorter but not too short; 更短但不太短; longer but not too long. 更长但不太长。

Actually I met the same problem years ago, and gave up for this (maybe?). 实际上我几年前遇到了同样的问题,并放弃了这个(也许?)。 I've just read your question here and now, and I think I've just found the solution: 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
// ]

Visualization 可视化

The smaller the logarithmic base is, the more they are adjusted ; 对数基数越小, 调整得越多; and vice versa. 反之亦然。 Actually I don't know the reason. 其实我不知道原因。 XD 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> 

You are scaling these numbers. 您正在缩放这些数字。 Your starting set is the domain, what you end up with is the range. 您的起始集是域,您最终得到的是范围。 The shape of that transformation, it sounds like, will be either log or follow a power. 听起来,这种转变的形状将是记录或跟随力量。

It turns out this is a very common problem in many fields, especially data visualization. 事实证明,这是许多领域中非常常见的问题,尤其是数据可视化。 That is why D3.js - THE data visualization tool - has everything you need to do this easily. 这就是为什么D3.js - 数据可视化工具 - 拥有轻松完成此任务所需的一切

 const x = d3.scale.log().range([0, events]);

It's the right to for the job. 这是工作的权利。 And if you need to do some graphs, you're all set! 如果你需要做一些图表,那么你们都已经完成了!

As long as your events are > 0 you can avoid shifting by scaling the events so the minimum value is above 1. The scalar can be calculated based on a minimum line length in addition to the maximum line length you already have. 只要您的事件> 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 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM