如何写d3正负对数刻度

Ale*_*ail 3 javascript d3.js

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

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

-1000, -100, -10, -1, 0, 1, 10, 100, 1000
Run Code Online (Sandbox Code Playgroud)

我希望0在那里,它被定义为-Inf对数,使这进一步复杂化.

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

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

思考

d3.scaleLog()如果你使用像这样的技术,可以用2 秒或3个刻度巧妙地做到这一点.

我希望有可能是在一个合适的这样的一个简单的方法d3.scalePow().exponent(0.1),但除非我有混了我的日志规则,你不能让一个.scaleLog()出来的.scalePow()(尽管你可能接近它好一些的范围).

And*_*eid 5

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

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

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.
}
Run Code Online (Sandbox Code Playgroud)

一个例子:

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) );
Run Code Online (Sandbox Code Playgroud)
path {
  fill: none;
  stroke: steelblue;
  stroke-width: 2px;
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
Run Code Online (Sandbox Code Playgroud)


创建单一比例

以上是概念证明.

现在更棘手的部分是制作一个轴.我们可以为上面的两个刻度制作轴,通过某种手动校正留下零.但是使用我们自己的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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

d3.scaleLinear().interpolate(interpolator)...
Run Code Online (Sandbox Code Playgroud)

这会将域中的数字插入到我们指定的范围内.它在很大程度上是鉴于上述,采用它用作D3内插器:a,b是域的限制,t是0和1之间的归一化结构域,和k限定了zeroish值.更多内容k如下.

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

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

应用此我们得到:

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

// 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);
Run Code Online (Sandbox Code Playgroud)
.line {
  fill: none;
  stroke: steelblue;
  stroke-width: 2px;
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
Run Code Online (Sandbox Code Playgroud)


修改k值

k,怎么了k.需要设置零值.k也会改变图形的形状.如果显示规则间隔的蜱,增加k十倍会使最小幅度蜱(除了零)的幅度增加十倍.在上面的我的exmaples中,乘以k10可以推动得到一个超过零滴答的幅度.将它除以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 = b-a;
  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);
Run Code Online (Sandbox Code Playgroud)
.line {
  fill: none;
  stroke: steelblue;
  stroke-width: 2px;
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

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

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

最后,

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

在此输入图像描述

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

在此输入图像描述