如何在 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_);   
  1. 我们需要一个普通组和一个 groupall 作为总输入
  2. “假组”是一个实现 .all() 和 returns {key, value} 对数组的对象
  3. 我们需要所有类别的当前总数,以便计算每个类别的贡献
  4. 我们会从左到右累积贡献
  5. 我们将采用原始组的 .all(),使用 .slice(0)
  6. 复制数组
  7. 按值降序排列
  8. ... 并生成一个新数组,具有相同的键,但值增加了个人和累积贡献

初始化图表需要一些晦涩的解决方法。我不会对此进行深入探讨,只是要说是的,这比您想象的要复杂。

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.

截图如下:

here is a demo fiddle.