为什么我的饼图在 dc.js 和 crossfilter.js 中使用堆积条形图过滤时显示不正确的组?
Why is my pie chart showing incorrect groups when filtered with stacked bar chart in dc.js & crossfilter.js?
当我单击 dc.js 堆叠条形图时,同一页面上其他地方的饼图未显示正确的组。
我是 dc.js 的新手,所以我创建了一个简单的数据集来演示我需要的功能:Alice 和 Bob 写了关于水果的文章,并用 标记每篇文章 标签。我将这些数据绘制成如下图表:
- 显示每天文章数量的折线图
- 显示每个标签使用总数的饼图
- 显示作者使用的标签数量的堆积条形图
数据集如下:
rawData = [
{"ID":"00000001","User":"Alice","Date":"20/02/2019","Tag":"apple"},
{"ID":"00000002","User":"Bob","Date":"17/02/2019","Tag":"dragonfruit"},
{"ID":"00000003","User":"Alice","Date":"21/02/2019","Tag":"banana"},
{"ID":"00000004","User":"Alice","Date":"22/02/2019","Tag":"cherry"},
{"ID":"00000005","User":"Bob","Date":"23/02/2019","Tag":"cherry"},
];
说明性 JSFiddle 在这里:https://jsfiddle.net/hv8sw6km/ 和下面的代码片段:
/* Prepare data */
rawData = [
{"ID":"00000001","User":"Alice","Date":"20/02/2019","Tag":"apple"},
{"ID":"00000002","User":"Bob","Date":"17/02/2019","Tag":"dragonfruit"},
{"ID":"00000003","User":"Alice","Date":"21/02/2019","Tag":"banana"},
{"ID":"00000004","User":"Alice","Date":"22/02/2019","Tag":"cherry"},
{"ID":"00000005","User":"Bob","Date":"23/02/2019","Tag":"cherry"},
];
var data = [];
var parseDate = d3.timeParse("%d/%m/%Y");
rawData.forEach(function(d) {
d.Date = parseDate(d.Date);
data.push(d);
});
var ndx = crossfilter(data);
/* Set up dimensions, groups etc. */
var dateDim = ndx.dimension(function(d) {return d.Date;});
var dateGrp = dateDim.group();
var tagsDim = ndx.dimension(function(d) {return d.Tag;});
var tagsGrp = tagsDim.group();
var authorDim = ndx.dimension(function(d) { return d.User; });
/* Following stacked bar chart example at
https://dc-js.github.io/dc.js/examples/stacked-bar.html
adapted for context. */
var authorGrp = authorDim.group().reduce(
function reduceAdd(p,v) {
p[v.Tag] = (p[v.Tag] || 0) + 1;
p.total += 1;
return p;
},
function reduceRemove(p,v) {
p[v.Tag] = (p[v.Tag] || 0) - 1;
p.total -= 1;
return p;
},
function reduceInit() { return { total: 0 } }
);
var minDate = dateDim.bottom(1)[0].Date;
var maxDate = dateDim.top(1)[0].Date;
var fruitColors = d3
.scaleOrdinal()
.range(["#00CC00","#FFFF33","#CC0000","#CC00CC"])
.domain(["apple","banana","cherry","dragonfruit"]);
/* Create charts */
var articlesByDay = dc.lineChart("#chart-articlesperday");
articlesByDay
.width(500).height(200)
.dimension(dateDim)
.group(dateGrp)
.x(d3.scaleTime().domain([minDate,maxDate]));
var tagsPie = dc.pieChart("#chart-article-tags");
tagsPie
.width(150).height(150)
.dimension(tagsDim)
.group(tagsGrp)
.colors(fruitColors)
.ordering(function (d) { return d.key });
var reviewerOrdering = authorGrp
.all()
// .sort(function (a, b) { return a.value.total - b.value.total })
.map(function (d) { return d.key });
var tagsByAuthor = dc.barChart("#chart-tags-by-reviewer");
tagsByAuthor
.width(600).height(400)
.x(d3.scaleBand().domain(reviewerOrdering))
.xUnits(dc.units.ordinal)
.dimension(authorDim)
.colors(fruitColors)
.elasticY(true)
.title(function (d) { return d.key + ": " + this.layer + ": " + d.value[this.layer] });
function sel_stack(i) {
return function(d) {
return d.value[i];
};
}
var tags = tagsGrp
.all()
.sort(function(a,b) { return b.value - a.value})
.map(function (d) { return d.key });
tagsByAuthor.group(authorGrp, tags[0]);
tagsByAuthor.valueAccessor(sel_stack(tags[0]));
tags.shift(); // drop the first, as already added as .group()
tags.forEach(function (tag) {
tagsByAuthor.stack(authorGrp, tag, sel_stack(tag));
});
dc.renderAll();
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter2/1.4.7/crossfilter.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.1.1/dc.min.js"></script>
<div id="chart-articlesperday"></div>
<div id="chart-article-tags"></div>
<div id="chart-tags-by-reviewer"></div>
如您所见,Alice 制作了三篇文章,每篇文章分别用 "apple"、"banana" 和 "cherry" 标记,她的堆叠条形图显示了这一点。
然而,只要点击她的条形图列,饼图就会显示她有 1 "apple" 和 2 "cherry"。
我花了很长时间才达到这一点,所以可能我没有了解交叉过滤器分组的一些基本知识,所以非常欢迎任何见解、提示或评论。
确实,这是很奇怪的行为,除了遇到过几次,我不知道怎么想的。
如果您查看 documentation for group.all(),它会警告:
This method is faster than top(Infinity) because the entire group array is returned as-is rather than selecting into a new array and sorting. Do not modify the returned array!
我想否则它可能会在聚合时开始修改错误的 bin。 (只是猜测,我没有追查代码。)
你有:
var tags = tagsGrp
.all()
.sort(function(a,b) { return b.value - a.value})
.map(function (d) { return d.key });
添加.slice()
,复制数组,修复它:
var tags = tagsGrp
.all().slice()
.sort(function(a,b) { return b.value - a.value})
.map(function (d) { return d.key });
我们实际上 an open bug 图书馆自己做这件事。啊! (很容易修复,但需要一些工作来生成测试用例。)
当我单击 dc.js 堆叠条形图时,同一页面上其他地方的饼图未显示正确的组。
我是 dc.js 的新手,所以我创建了一个简单的数据集来演示我需要的功能:Alice 和 Bob 写了关于水果的文章,并用 标记每篇文章 标签。我将这些数据绘制成如下图表:
- 显示每天文章数量的折线图
- 显示每个标签使用总数的饼图
- 显示作者使用的标签数量的堆积条形图
数据集如下:
rawData = [
{"ID":"00000001","User":"Alice","Date":"20/02/2019","Tag":"apple"},
{"ID":"00000002","User":"Bob","Date":"17/02/2019","Tag":"dragonfruit"},
{"ID":"00000003","User":"Alice","Date":"21/02/2019","Tag":"banana"},
{"ID":"00000004","User":"Alice","Date":"22/02/2019","Tag":"cherry"},
{"ID":"00000005","User":"Bob","Date":"23/02/2019","Tag":"cherry"},
];
说明性 JSFiddle 在这里:https://jsfiddle.net/hv8sw6km/ 和下面的代码片段:
/* Prepare data */
rawData = [
{"ID":"00000001","User":"Alice","Date":"20/02/2019","Tag":"apple"},
{"ID":"00000002","User":"Bob","Date":"17/02/2019","Tag":"dragonfruit"},
{"ID":"00000003","User":"Alice","Date":"21/02/2019","Tag":"banana"},
{"ID":"00000004","User":"Alice","Date":"22/02/2019","Tag":"cherry"},
{"ID":"00000005","User":"Bob","Date":"23/02/2019","Tag":"cherry"},
];
var data = [];
var parseDate = d3.timeParse("%d/%m/%Y");
rawData.forEach(function(d) {
d.Date = parseDate(d.Date);
data.push(d);
});
var ndx = crossfilter(data);
/* Set up dimensions, groups etc. */
var dateDim = ndx.dimension(function(d) {return d.Date;});
var dateGrp = dateDim.group();
var tagsDim = ndx.dimension(function(d) {return d.Tag;});
var tagsGrp = tagsDim.group();
var authorDim = ndx.dimension(function(d) { return d.User; });
/* Following stacked bar chart example at
https://dc-js.github.io/dc.js/examples/stacked-bar.html
adapted for context. */
var authorGrp = authorDim.group().reduce(
function reduceAdd(p,v) {
p[v.Tag] = (p[v.Tag] || 0) + 1;
p.total += 1;
return p;
},
function reduceRemove(p,v) {
p[v.Tag] = (p[v.Tag] || 0) - 1;
p.total -= 1;
return p;
},
function reduceInit() { return { total: 0 } }
);
var minDate = dateDim.bottom(1)[0].Date;
var maxDate = dateDim.top(1)[0].Date;
var fruitColors = d3
.scaleOrdinal()
.range(["#00CC00","#FFFF33","#CC0000","#CC00CC"])
.domain(["apple","banana","cherry","dragonfruit"]);
/* Create charts */
var articlesByDay = dc.lineChart("#chart-articlesperday");
articlesByDay
.width(500).height(200)
.dimension(dateDim)
.group(dateGrp)
.x(d3.scaleTime().domain([minDate,maxDate]));
var tagsPie = dc.pieChart("#chart-article-tags");
tagsPie
.width(150).height(150)
.dimension(tagsDim)
.group(tagsGrp)
.colors(fruitColors)
.ordering(function (d) { return d.key });
var reviewerOrdering = authorGrp
.all()
// .sort(function (a, b) { return a.value.total - b.value.total })
.map(function (d) { return d.key });
var tagsByAuthor = dc.barChart("#chart-tags-by-reviewer");
tagsByAuthor
.width(600).height(400)
.x(d3.scaleBand().domain(reviewerOrdering))
.xUnits(dc.units.ordinal)
.dimension(authorDim)
.colors(fruitColors)
.elasticY(true)
.title(function (d) { return d.key + ": " + this.layer + ": " + d.value[this.layer] });
function sel_stack(i) {
return function(d) {
return d.value[i];
};
}
var tags = tagsGrp
.all()
.sort(function(a,b) { return b.value - a.value})
.map(function (d) { return d.key });
tagsByAuthor.group(authorGrp, tags[0]);
tagsByAuthor.valueAccessor(sel_stack(tags[0]));
tags.shift(); // drop the first, as already added as .group()
tags.forEach(function (tag) {
tagsByAuthor.stack(authorGrp, tag, sel_stack(tag));
});
dc.renderAll();
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter2/1.4.7/crossfilter.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.1.1/dc.min.js"></script>
<div id="chart-articlesperday"></div>
<div id="chart-article-tags"></div>
<div id="chart-tags-by-reviewer"></div>
如您所见,Alice 制作了三篇文章,每篇文章分别用 "apple"、"banana" 和 "cherry" 标记,她的堆叠条形图显示了这一点。
然而,只要点击她的条形图列,饼图就会显示她有 1 "apple" 和 2 "cherry"。
我花了很长时间才达到这一点,所以可能我没有了解交叉过滤器分组的一些基本知识,所以非常欢迎任何见解、提示或评论。
确实,这是很奇怪的行为,除了遇到过几次,我不知道怎么想的。
如果您查看 documentation for group.all(),它会警告:
This method is faster than top(Infinity) because the entire group array is returned as-is rather than selecting into a new array and sorting. Do not modify the returned array!
我想否则它可能会在聚合时开始修改错误的 bin。 (只是猜测,我没有追查代码。)
你有:
var tags = tagsGrp
.all()
.sort(function(a,b) { return b.value - a.value})
.map(function (d) { return d.key });
添加.slice()
,复制数组,修复它:
var tags = tagsGrp
.all().slice()
.sort(function(a,b) { return b.value - a.value})
.map(function (d) { return d.key });
我们实际上 an open bug 图书馆自己做这件事。啊! (很容易修复,但需要一些工作来生成测试用例。)