题
我有积极和消极的价值观,我想以"对数"的比例绘制它们.
想象一个具有均匀间隔的刻度的刻度,用于以下值:
-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()(尽管你可能接近它好一些的范围).
我们不能像这样拥有真正的对数标度,甚至不能像这样的两个对数标度的组合.我们需要为零值设置一个截止值,这可能是根据您的数据引入错误的地方.否则,要使这样的比例函数相当简单,只需为负和正调用不同的比例,同时将零值设置为零.
这种尺度组合可能如下所示:
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.理想情况下,这不会是数据集中的多个值,但如果是,则可能得到行如:
每个输入值都不同,但是有很多零输出值,由于我认为是零的边界而产生视觉伪像.如果在零度范围内只有一个值,就像我上面的例子(但不是上面的图片),我们得到了更好的: