如何解决如果没有数据,d3 断开折线图 但是孤立的点呢?
我正在使用 dc 创建一个折线图,其中容量在 y 轴上,周在 x 轴上。对于周,范围是 1-52,但没有第 2-40 周的数据。我只有第 1 周和第 41-52 周的数据,但是当没有数据时,我的折线图仍在创建一条线:
如果没有值,我如何获得它以便折线图会中断?所以它不会是一根连接线。这是我的代码供参考
let chart = dc.lineChart("#chart");
let ndx = crossfilter(results);
let weekDimension = ndx.dimension(function (d) {
return d.week = +d.week;
});
function reduceAdd(p,v) {
++p.count;
p.total += v.capacity;
p.average = p.total / p.count;
return p;
}
function reduceRemove(p,v) {
--p.count;
p.total -= v.capacity;
p.average = p.count ? p.total / p.count : 0;
return p;
}
function reduceInitial() {
return { count: 0,total: 0,average: 0 };
}
let capacityGroup = weekDimension.group().reduce(reduceAdd,reduceRemove,reduceInitial);
chart.width(360)
.height(200)
.margins({ top: 20,right: 20,bottom: 50,left: 30 })
.mouseZoomable(false)
.x(d3.scale.linear().domain([1,52]))
.renderHorizontalGridLines(true)
.brushOn(false)
.dimension(weekDimension)
.valueAccessor(function (d) {
return d.value.average;
})
.group(capacityGroup);
dc.renderAll('chart');
这就是 results
的样子
{month : "1",capacity: "48"}
{month : "1",capacity: "60"}
{month : "42",capacity: "67"}
{month : "42",capacity: "60"}
{month : "43",capacity: "66"}
{month : "44",capacity: "52"}
{month : "45",capacity: "63"}
{month : "46",capacity: "67"}
{month : "47",capacity: "80"}
{month : "48",capacity: "61"}
{month : "48",capacity: "66"}
{month : "49",capacity: "54"}
{month : "50",capacity: "69"}
我尝试添加 .defined(d => { return d.y != null; });
和 .defined(d => !isNaN(d.value));
但这没有任何作用...任何帮助将不胜感激
解决方法
正如我们在评论中所讨论的,重要的问题是 dc.js 只会绘制它接收到的数据。它不知道是否缺少数据,因此我们需要填充 null
以在线条中绘制间隙。
我链接到 a previous question,其中数据是时间戳。那里的答案使用 d3 time interval 来生成缺失的时间戳。
但是,您的数据使用整数作为键(即使它代表周),因此我们需要稍微更改函数:
function fill_ints(group,fillval,stride = 1) { // 1
return {
all: function() {
var orig = group.all();
var target = d3.range(orig[0].key,orig[orig.length-1].key,stride); // 2
var result = [];
for(var oi = 0,ti = 0; oi < orig.length && ti < target.length;) {
if(orig[oi].key <= target[ti]) {
result.push(orig[oi]);
if(orig[oi++].key === target[ti])
++ti;
} else {
result.push({key: target[ti],value: fillval});
++ti;
}
} // 3
if(oi<orig.length) // 4
Array.prototype.push.apply(result,orig.slice(oi));
if(ti<target.length) // 5
result = [...result,...target.slice(ti).map(t => ({key: t,value: fillval}))];
return result;
}
};
}
- 此函数需要一个组、要填充的值和一个步幅,即条目之间所需的间隙。
- 它读取当前数据,并使用 d3.range 生成所需的密钥。
- 它遍历两个数组,将任何缺失的条目添加到组数据的副本中。
- 如果原始组数据中有任何剩余条目,则将其附加。
- 如果还有剩余的目标,它会生成这些目标。
现在我们使用这个函数包装我们的原始组,创建一个 "fake group":
const filledGroup = fill_ints(capacityGroup,{average: null});
并将其传递给图表:
.group(filledGroup);
使用 LineChart.defined() 和底层 d3.line.defined 的一个弱点是需要两个点才能形成一条线。如果您有孤立的点,因为第 1 周在您的原始数据中是孤立的,那么它根本不会显示。
在 this demo fiddle 中,我通过添加第 2 周的数据避免了该问题。
但是孤立的点呢?
我很好奇如何解决“孤立点问题”,所以我尝试显示通常用于鼠标悬停效果的内置点:
chart.on('pretransition',chart => {
const all = chart.group().all();
isolated = all.reduce((p,kv,i) => {
return (kv.value.average !== null &&
(i==0 || all[i-1].value.average == null) &&
((i==all.length-1 || all[i+1].value.average == null))) ?
{...p,[kv.key]: true} : p;
},{});
chart.g().selectAll('circle.dot')
.filter(d => isolated[d.data.key])
.style('fill-opacity',0.75)
.on('mousemove mouseout',null)
})
This works 但它目前依赖于禁用这些点的交互性,以免它们消失。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。