繁体   English   中英

如何写d3正负对数刻度

[英]How to write a d3 positive and negative logarithmic scale

我有积极和消极的价值观,我想以“对数”的比例绘制它们。

想象一个具有均匀间隔的刻度的刻度,用于以下值:

-1000, -100, -10, -1, 0, 1, 10, 100, 1000

我希望在那里有0,它被定义为-Inf by logarithms,这进一步复杂化了。

但是,我不认为这个要求是不合理的。 这似乎是一个合理的规模,任何数据科学家可能想要绘制强烈不同的价值观。

你如何在d3中创建这样的比例和轴?

思考

如果您使用类似这样的技术,可以使用2 d3.scaleLog() s或3个刻度巧妙地执行此操作。

我希望有可能在适合这种简单的方法d3.scalePow().exponent(0.1)但除非我有我的日志规则混合起来,你不能得到一个.scaleLog()出来的.scalePow() (虽然你可以在一些范围内近似它)。

我们不能像这样拥有真正的对数标度,甚至不能像这样的两个对数标度的组合。 我们需要为零值设置一个截止值,这可能是根据您的数据引入错误的地方。 否则,要使这样的比例函数相当简单,只需为负和正调用不同的比例,同时将零值设置为零。

这种尺度组合可能如下所示:

var positive = d3.scaleLog()
  .domain([1e-6,1000])
  .range([height/2,0])

var negative = d3.scaleLog()
  .domain([-1000,-1e-6])
  .range([height,height/2])

var scale = function(x) {
  if (x > 1e-6) return positive(x);
  else if (x < -1e-6) return negative(x);
  else return height/2; // zero value.
}

一个例子:

 var width = 500; var height = 300; var positive = d3.scaleLog() .domain([1e-1,1000]) .range([height/2,0]) var negative = d3.scaleLog() .domain([-1000,-1e-1]) .range([height,height/2]) var scale = function(x) { if (x > 1e-6) return positive(x); else if (x < -1e-6) return negative(x); else return height/2; // zero value. } var line = d3.line() .y(function(d) { return scale(d) }) .x(function(d,i) { return (i); }) var svg = d3.select("body") .append("svg") .attr("width",width) .attr("height",height) var data = d3.range(width).map(function(d) { return (d - 250) * 4; }) svg.append("path") .attr("d", line(data) ); 
 path { fill: none; stroke: steelblue; stroke-width: 2px; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script> 


创建单一比例

以上是概念证明。

现在更棘手的部分是制作一个轴。 我们可以为上面的两个刻度制作轴,通过某种手动校正留下零。 但是使用我们自己的interpolotor创建一个比例将更容易使用上述作为例子。 这给了我们一个可以为其创建轴的比例。 我们的插补器可能如下所示:

// Interpolate an output value:
var interpolator = function(a,b) {
  var y0 = a;
  var y1 = b;
  var yd = b-a;
  var k = 0.0001; 

  var positive = d3.scaleLog()
    .domain([k,1])
    .range([(y0 + y1)/2 ,y1])

  var negative = d3.scaleLog()
    .domain([-1,-k])
    .range([y0, (y1 + y0)/2])

  return function(t) { 
    t = (t - 0.5) * 2; // for an easy range of -1 to 1.
    if (t > k) return positive(t);
    if (t < -1 + k) return y0;
    if (t < -k) return negative(t);
    else return (y0 + y1) /2;
  }
}

然后我们可以将它应用到常规的旧d3线性比例:

d3.scaleLinear().interpolate(interpolator)...

这会将域中的数字插入到我们指定的范围内。 它主要采用上述内容并将其用作d3插值器: ab是域限制, t是0和1之间的归一化域, k定义零值。 更多关于k以下。

为了获得滴答声,假设一个漂亮的圆形域只有很好的圆形十基数我们可以使用:

// Set the ticks:
var ticks = [0];
scale.domain().forEach(function(d) {
  while (Math.abs(d) >= 1) {
    ticks.push(d); d /= 10;
  }
})

应用此我们得到:

 var margin = {left: 40, top: 10, bottom: 10} var width = 500; var height = 300; var svg = d3.select("body") .append("svg") .attr("width",width+margin.left) .attr("height",height+margin.top+margin.bottom) .append("g").attr("transform","translate("+[margin.left,margin.top]+")"); var data = d3.range(width).map(function(d) { return (d - 250) * 4; }) // Interpolate an output value: var interpolator = function(a,b) { var y0 = a; var y1 = b; var yd = ba; var k = 0.0001; var positive = d3.scaleLog() .domain([k,1]) .range([(y0 + y1)/2 ,y1]) var negative = d3.scaleLog() .domain([-1,-k]) .range([y0, (y1 + y0)/2]) return function(t) { t = (t - 0.5) * 2; // for an easy range of -1 to 1. if (t > k) return positive(t); if (t < -1 + k) return y0; if (t < -k) return negative(t); else return (y0 + y1) /2; } } // Create a scale using it: var scale = d3.scaleLinear() .range([height,0]) .domain([-1000,1000]) .interpolate(interpolator); // Set the ticks: var ticks = [0]; scale.domain().forEach(function(d) { while (Math.abs(d) >= 1) { ticks.push(d); d /= 10; } }) // Apply the scale: var line = d3.line() .y(function(d) { return scale(d) }) .x(function(d,i) { return (i); }) // Draw a line: svg.append("path") .attr("d", line(data) ) .attr("class","line"); // Add an axis: var axis = d3.axisLeft() .scale(scale) .tickValues(ticks) svg.append("g").call(axis); 
 .line { fill: none; stroke: steelblue; stroke-width: 2px; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script> 


修改k值

Ø k ,这是怎么回事约k 需要设置零值。 k也会改变图形的形状。 如果显示规则间隔的蜱,增加k十倍会使最小幅度滴答(除零之外)的幅度增加十倍。 在我的上面的例子中,将k乘以10可以推动得分为1,超过零刻度。 将它除以10将为0.1刻度创建空间(当然,这需要修改刻度线生成器以显示刻度)。 k很难解释所以我希望我在那里做得很好。

我将展示尝试更好地沟通它。 让我们使用上面的方法将最小幅度刻度设置为0.1,我们要修改tick函数和k:

 var margin = {left: 40, top: 10, bottom: 10} var width = 500; var height = 300; var svg = d3.select("body") .append("svg") .attr("width",width+margin.left) .attr("height",height+margin.top+margin.bottom) .append("g").attr("transform","translate("+[margin.left,margin.top]+")"); var data = d3.range(width).map(function(d) { return (d - 250) * 4; }) // Interpolate an output value: var interpolator = function(a,b) { var y0 = a; var y1 = b; var yd = ba; var k = 0.00001; var positive = d3.scaleLog() .domain([k,1]) .range([(y0 + y1)/2 ,y1]) var negative = d3.scaleLog() .domain([-1,-k]) .range([y0, (y1 + y0)/2]) return function(t) { t = (t - 0.5) * 2; // for an easy range of -1 to 1. if (t > k) {return positive(t)}; if (t < -1 + k) return y0; if (t < -k) return negative(t); else return (y0 + y1) /2 //yd; } } // Create a scale using it: var scale = d3.scaleLinear() .range([height,0]) .domain([-1000,1000]) .interpolate(interpolator); // Set the ticks: var ticks = [0]; scale.domain().forEach(function(d) { while (Math.abs(d) >= 0.1) { ticks.push(d); d /= 10; } }) // Apply the scale: var line = d3.line() .y(function(d) { return scale(d) }) .x(function(d,i) { return (i); }) // Draw a line: svg.append("path") .attr("d", line(data) ) .attr("class","line"); // Add an axis: var axis = d3.axisLeft() .scale(scale) .tickValues(ticks) .ticks(10,".1f") svg.append("g").call(axis); 
 .line { fill: none; stroke: steelblue; stroke-width: 2px; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script> 

如果您的域名为+/- 1000,并且您希望最小幅度刻度为1(不包括零),则需要k为0.0001或0.1 / 1000。

如果域的正负极限不同,那么我们需要两个k值,一个用于负截止,一个用于正截止。

最后,

k设置为零的值,在我的示例中,介于-k和+ k之间的t值设置为相同 - 0.理想情况下,数据集中的数值不会很多,但如果是,则可能得到一行如:

在此输入图像描述

每个输入值都不同,但是有很多零输出值,由于我认为是零的边界而产生视觉伪像。 如果在零度范围内只有一个值,就像我上面的例子(但不是上面的图片),我们得到了更好的:

在此输入图像描述

暂无
暂无

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

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