使用 dc.js 堆叠占总数的百分比

Stacking percentages out of total using dc.js

我有一个类似于

的数据

 Month      Material     Sales
   2            A         500
   2            A         300
   5            A         700
   1            B         400
   2            B         300
   4            C        1200
   2            C         500

我想在 dc.rowChart 的月份维度下显示每个 material 销售额占总销售额的百分比。

第 2 个月 material 的百分比将为 %50。因为第 2 个月的总销售额为 1600,而 A 的销售额为 800。对于 material,B 百分比将为 %18,75,因为 B 在第 2 个月的销售额为 300。等等。

到目前为止,我所做的符合逻辑。但是它不显示任何数据

var monthDim=ndx.dimension(function (d) {return +d.Month;});                                       

   var totalGroup = monthDim.group().reduce(
    /* callback for when data is added to the current filter results */
    (p, v) => {
        ++p.count;       
        p.Sales += v.Sales;          
        return p;
    },
    /* callback for when data is removed from the current filter results */
    (p, v) => {
        --p.count;       
        p.Sales -= v.Sales;           
        return p;
    },
    /* initialize p */
    () => ({
        count: 0,
        Sales: 0,      
    })
);

然后求总销售额:

var salesTotal= ndx.dimension(function (d) { return d.Sales; });
var salesTotalGroup = salesTotal.groupAll().reduceSum(function (d) { return d.Sales; });

现在我想将这些组合成条形图上的变量。我知道他们似乎不能一起工作。但这就是我想出的。

var chart= dc.rowChart('#salespercentagechart')
                 .width(400)
                 .height(350)
                 .elasticX(true)
                 .dimension(monthDim)
                 .group(totalGroup )      
                 .valueAccessor(function(p) { return p.value.Sales / salesTotalGroup;} )                
                 .ordering(function (d) { return -d.key; })

任何想法对我来说都是完美的。谢谢。

统计每个类别的总和和总数

您可以使用 crossfilter 组自定义缩减来计算每个 material 的总数,同时计算总总数:

var totalGroup = monthDim.group().reduce(
    /* callback for when data is added to the current filter results */
    (p, v) => {
        p.byMaterial[v.Material] = (p.byMaterial[v.Material] || 0) + v.Sales;  
        p.total += v.Sales;
        return p;
    },
    /* callback for when data is removed from the current filter results */
    (p, v) => {
        p.byMaterial[v.Material] -= v.Sales;  
        p.total -= v.Sales;
        return p;
    },
    /* initialize p */
    () => ({
        byMaterial: {},
        total: 0,      
    })
);

这是一次聚合多个堆栈的规范方法1

  • 在堆栈名称上保留一个对象
  • 添加时,使用|| 0
  • 将undefined视为0
  • 删除时,p.byMaterial[v.Material] 将始终被定义,因此 -= 是安全的

现在totalGroup.all()会产生

[
  {
    "key": 1,
    "value": {
      "byMaterial": {
        "B": 400
      },
      "total": 400
    }
  },
  {
    "key": 2,
    "value": {
      "byMaterial": {
        "A": 800,
        "B": 300,
        "C": 500
      },
      "total": 1600
    }
  },
  {
    "key": 4,
    "value": {
      "byMaterial": {
        "C": 1200
      },
      "total": 1200
    }
  },
  {
    "key": 5,
    "value": {
      "byMaterial": {
        "A": 700
      },
      "total": 700
    }
  }
]

循环初始化堆栈

在循环中定义图表堆栈很方便:

var materials = d3.set(data, d => d.Material);
materials.values().sort().forEach((material, i) => {
  const accessor = d => (d.value.byMaterial[material] || 0) / d.value.total * 100;
  if(i === 0)
    chart.group(totalGroup, material, accessor);
  else
    chart.stack(totalGroup, material, accessor);  
});

我们使用d3.set找到d.Material的所有唯一值,然后循环遍历它们。 dc.js 有 an annoying design bug,您必须在第一次调用 .group(),即使它具有与 .stack() 相同的参数,因此 if(i === 0).

存取器计算百分比

const accessor = d => (d.value.byMaterial[material] || 0) / d.value.total * 100;

它读取 byMaterial,如果当月 material 不存在,再次默认 undefined 为 0,然后除以总数并乘以 100 得到百分比。

图表定义的其余部分

var chart= dc.lineChart('#salespercentagechart')
    .width(400)
    .height(350)
    .renderArea(true)
    .elasticX(true)
    .dimension(monthDim)
    .x(d3.scaleLinear()).elasticX(true)
    .legend(dc.legend().x(300).y(50))
    //.ordering(function (d) { return -d.key; });

Fiddle demo.