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

问题描述 投票:1回答:1

我的数据类似于

enter image description here

 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在“月”维度下显示每种物料销售额占总销售额的百分比。

[对于物料的第二个月,百分比为%50。因为在第二个月的总销售额是1600,而A的销售额是800。对于物料B的百分比将为%18,75,因为B在第二个月的销售额为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; })

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

dc.js crossfilter
1个回答
1
投票

每个类别和总计的总和

您可以使用交叉过滤器组自定义归约来同时计算每种材料的总数与总数:

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;

[按物料读取,如果该物料在当月不存在,则再次将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; });

screenshot of stacked area/line chart

Fiddle demo

© www.soinside.com 2019 - 2024. All rights reserved.