如何在 dc.js 中绘制帕累托图
How to draw a Pareto chart in dc.js
我正在尝试使用来自 dc.js 的复合图表来实现 Pareto 图。如果输入的数据是线性的而不是有序的,复合排序就可以正常工作。我坚持执行以下内容。
我有以下代码,在这个过程中,我试图在 x 轴 reasons 所在的位置创建 pareto,并且有两个 y 轴。一个代表时间的总和,另一个代表时间
的总百分比贡献
total_time = sum(time)
contribution = time/total_time
假设一张图表按 ASC 顺序排序,其中 time 值,一张图表假设按 DESC 顺序排序,其中 contribution
这里的方法是什么?
var sample_data = [
{ reason: "A", time: 1 },
{ reason: "B", time: 6 },
{ reason: "C", time: 6 },
{ reason: "D", time: 5 },
{ reason: "A", time: 5 },
{ reason: "B", time: 5 },
{ reason: "C", time: 8 },
{ reason: "A", time: 8 },
{ reason: "B", time: 2 },
{ reason: "C", time: 2 },
{ reason: "D", time: 10 },
{ reason: "C", time: 7 },
{ reason: "A", time: 3 },
{ reason: "B", time: 4 },
{ reason: "C", time: 2 }];
var ndx_ = crossfilter(sample_data),
dim_ = ndx_.dimension( function(d) {return d.reason;} ),
grp1_ = dim_.group().reduceSum(function(d){ return d.time;});
grp2_ = dim_.group().reduce(
function(p,v){
p.reason = v.reason;
p.time = v.time;
p.total_time += +p.time;
p.contribution = p.time/p.total_time;
return p;
},
function(p,v){
p.reason = v.reason;
p.time = v.time;
p.total_time -= +p.time;
p.contribution = p.time/p.total_time;
return p;
},
function(p,v){
return {reason:'',time:0,total_time:0,contribution:0}
});
var sortByTime = sample_data.sort(function (a, b) { return a.time < b.time; });
var sampleDataSorted = sortByTime.map(function (d) { return d; });
chart
.width(768)
.height(480)
//.x(d3.scaleBand())
.x(d3.scaleOrdinal().domain(sampleDataSorted.map(function(d) {
console.log("asas",d);
return d.reason;
})))
.xUnits(dc.units.ordinal)
.yAxisLabel("The Y Axis")
.legend(dc.legend().x(80).y(20).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
.compose([
dc.barChart(chart)
.dimension(dim_)
.barPadding(20)
.clipPadding(20)
.outerPadding(100)
.group(grp1_, "Bars")
.centerBar(true) ,
dc.lineChart(chart)
.dimension(dim_)
.colors('red')
.group(grp2_, "Dots")
.dashStyle([2,2])
.valueAccessor(function(d){return d.value.contribution})
])
.ordering(function(d) { return +d.time; })
.brushOn(false)
chart.render();
PS: 我这里也有设置 link here
所以我们需要一个组来计算每个类别(“原因”)的总时间,对计算每个项目的贡献进行排序,并累积折线图的贡献。
我们可以将此逻辑放入一次计算所有内容的 fake group 中:
function pareto_group(group, groupall) { // 1
return {
all: function() { // 2
var total = groupall.value(), // 3
cumulate = 0; // 4
return group.all().slice(0) // 5
.sort((a,b) => d3.descending(a.value, b.value)) // 6
.map(({key,value}) => ({ // 7
key,
value: {
value,
contribution: value/total,
cumulative: (cumulate += value/total)
}
}))
}
};
}
var pg = pareto_group(grp1_, allTime_);
- 我们需要一个普通组和一个 groupall 作为总输入
- “假组”是一个实现
.all()
和 returns {key, value}
对数组的对象
- 我们需要所有类别的当前总数,以便计算每个类别的贡献
- 我们会从左到右累积贡献
- 我们将采用原始组的
.all()
,使用 .slice(0)
复制数组
- 按值降序排列
- ... 并生成一个新数组,具有相同的键,但值增加了个人和累积贡献
初始化图表需要一些晦涩的解决方法。我不会对此进行深入探讨,只是要说是的,这比您想象的要复杂。
chart
.width(768)
.height(480)
.x(d3.scaleBand())
.elasticX(true)
.ordering(kv => -kv.value.value)
.xUnits(dc.units.ordinal)
.group(pg)
._rangeBandPadding(1)
.yAxisLabel("The Y Axis")
.legend(dc.legend().x(80).y(20).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
.compose([
dc.barChart(chart)
.dimension(dim_)
.barPadding(1)
.gap(1)
.centerBar(true)
.clipPadding(10)
.group(pg, "Contribution", kv => kv.value.value),
dc.lineChart(chart)
.dimension(dim_)
.colors('red')
.group(pg, "Cumulative", kv => Math.floor(kv.value.cumulative*100))
.useRightYAxis(true)
.dashStyle([2,2])
])
.brushOn(false);
chart.rightYAxis().tickFormat(d => d + '%')
请注意,我们打开 elasticX 是为了让图表在每次重绘时重新读取 X 比例域。
Most of the special cases involve ordinal charts.
截图如下:
我正在尝试使用来自 dc.js 的复合图表来实现 Pareto 图。如果输入的数据是线性的而不是有序的,复合排序就可以正常工作。我坚持执行以下内容。
我有以下代码,在这个过程中,我试图在 x 轴 reasons 所在的位置创建 pareto,并且有两个 y 轴。一个代表时间的总和,另一个代表时间
的总百分比贡献total_time = sum(time)
contribution = time/total_time
假设一张图表按 ASC 顺序排序,其中 time 值,一张图表假设按 DESC 顺序排序,其中 contribution
这里的方法是什么?
var sample_data = [
{ reason: "A", time: 1 },
{ reason: "B", time: 6 },
{ reason: "C", time: 6 },
{ reason: "D", time: 5 },
{ reason: "A", time: 5 },
{ reason: "B", time: 5 },
{ reason: "C", time: 8 },
{ reason: "A", time: 8 },
{ reason: "B", time: 2 },
{ reason: "C", time: 2 },
{ reason: "D", time: 10 },
{ reason: "C", time: 7 },
{ reason: "A", time: 3 },
{ reason: "B", time: 4 },
{ reason: "C", time: 2 }];
var ndx_ = crossfilter(sample_data),
dim_ = ndx_.dimension( function(d) {return d.reason;} ),
grp1_ = dim_.group().reduceSum(function(d){ return d.time;});
grp2_ = dim_.group().reduce(
function(p,v){
p.reason = v.reason;
p.time = v.time;
p.total_time += +p.time;
p.contribution = p.time/p.total_time;
return p;
},
function(p,v){
p.reason = v.reason;
p.time = v.time;
p.total_time -= +p.time;
p.contribution = p.time/p.total_time;
return p;
},
function(p,v){
return {reason:'',time:0,total_time:0,contribution:0}
});
var sortByTime = sample_data.sort(function (a, b) { return a.time < b.time; });
var sampleDataSorted = sortByTime.map(function (d) { return d; });
chart
.width(768)
.height(480)
//.x(d3.scaleBand())
.x(d3.scaleOrdinal().domain(sampleDataSorted.map(function(d) {
console.log("asas",d);
return d.reason;
})))
.xUnits(dc.units.ordinal)
.yAxisLabel("The Y Axis")
.legend(dc.legend().x(80).y(20).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
.compose([
dc.barChart(chart)
.dimension(dim_)
.barPadding(20)
.clipPadding(20)
.outerPadding(100)
.group(grp1_, "Bars")
.centerBar(true) ,
dc.lineChart(chart)
.dimension(dim_)
.colors('red')
.group(grp2_, "Dots")
.dashStyle([2,2])
.valueAccessor(function(d){return d.value.contribution})
])
.ordering(function(d) { return +d.time; })
.brushOn(false)
chart.render();
PS: 我这里也有设置 link here
所以我们需要一个组来计算每个类别(“原因”)的总时间,对计算每个项目的贡献进行排序,并累积折线图的贡献。
我们可以将此逻辑放入一次计算所有内容的 fake group 中:
function pareto_group(group, groupall) { // 1
return {
all: function() { // 2
var total = groupall.value(), // 3
cumulate = 0; // 4
return group.all().slice(0) // 5
.sort((a,b) => d3.descending(a.value, b.value)) // 6
.map(({key,value}) => ({ // 7
key,
value: {
value,
contribution: value/total,
cumulative: (cumulate += value/total)
}
}))
}
};
}
var pg = pareto_group(grp1_, allTime_);
- 我们需要一个普通组和一个 groupall 作为总输入
- “假组”是一个实现
.all()
和 returns{key, value}
对数组的对象 - 我们需要所有类别的当前总数,以便计算每个类别的贡献
- 我们会从左到右累积贡献
- 我们将采用原始组的
.all()
,使用.slice(0)
复制数组
- 按值降序排列
- ... 并生成一个新数组,具有相同的键,但值增加了个人和累积贡献
初始化图表需要一些晦涩的解决方法。我不会对此进行深入探讨,只是要说是的,这比您想象的要复杂。
chart
.width(768)
.height(480)
.x(d3.scaleBand())
.elasticX(true)
.ordering(kv => -kv.value.value)
.xUnits(dc.units.ordinal)
.group(pg)
._rangeBandPadding(1)
.yAxisLabel("The Y Axis")
.legend(dc.legend().x(80).y(20).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
.compose([
dc.barChart(chart)
.dimension(dim_)
.barPadding(1)
.gap(1)
.centerBar(true)
.clipPadding(10)
.group(pg, "Contribution", kv => kv.value.value),
dc.lineChart(chart)
.dimension(dim_)
.colors('red')
.group(pg, "Cumulative", kv => Math.floor(kv.value.cumulative*100))
.useRightYAxis(true)
.dashStyle([2,2])
])
.brushOn(false);
chart.rightYAxis().tickFormat(d => d + '%')
请注意,我们打开 elasticX 是为了让图表在每次重绘时重新读取 X 比例域。
Most of the special cases involve ordinal charts.
截图如下: