【问题标题】:Elastic X axis for stacked barchart, removing the empty bins [dc.js]堆叠条形图的弹性 X 轴,删除空箱 [dc.js]
【发布时间】:2017-02-08 13:53:10
【问题描述】:

example 展示了如何使用 fake group 和 chart.elasticX(true) 方法制作一个弹性 X 轴来移除空箱。

我正在尝试使用堆叠的条形图来完成这项工作,但我遇到了一个问题。我稍微修改了上面示例的代码,以便为条形图使用堆叠组。 (我在数据中添加了一个 Earned 列,为其创建了一个假组并将其分配给 chart.stack 方法)。但是对于某些“Earned”值存在 d3.js 错误:

未捕获的类型错误:无法读取未定义的属性“1”。

#

更新: 此问题与此 answer 有关,该 answer 声明“堆栈方法期望您的数据具有相同的长度”。也与这个answer有关,它提议创建一个联合组来解决这个问题。

#

这是修改后的示例代码

<!DOCTYPE html>
<html lang="en">
<head>
    <title>dc.js - Filtering Example</title>
    <meta charset="UTF-8">
    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.0.0/dc.min.css"/>
</head>
<body>

<div class="container">
<script type="text/javascript" src="header.js"></script>
  <p>Example demonstrating using a "<a href="https://github.com/dc-js/dc.js/wiki/FAQ#fake-groups">Fake Group</a>" to remove
    the empty bars of an ordinal bar chart when their values drop to zero.</p>

  <p>(Note the use of <code><a href="https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.coordinateGridMixin+elasticX">.elasticX(true)</a></code>
    to force calculation of the X domain each round.)</p>

<div id="chart-ring-year"></div>
<div id="chart-hist-spend"></div>
<div id="chart-row-spenders"></div>

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.12/d3.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/dc/2.0.0/dc.min.js"></script>
<script type="text/javascript">
var yearRingChart   = dc.pieChart("#chart-ring-year"),
    spendHistChart  = dc.barChart("#chart-hist-spend"),
    spenderRowChart = dc.rowChart("#chart-row-spenders");
// use static or load via d3.csv("spendData.csv", function(error, spendData) {/* do stuff */});
var spendData = [
    {Name: 'Mr A', Spent: '$40', Earned: '$70', Year: 2011},
    {Name: 'Mr B', Spent: '$10', Earned: '$20', Year: 2011},
    {Name: 'Mr C', Spent: '$40', Earned: '$40', Year: 2011},
    {Name: 'Mr A', Spent: '$70', Earned: '$170', Year: 2012},
    {Name: 'Mr B', Spent: '$20', Earned: '$30', Year: 2012},
    {Name: 'Mr B', Spent: '$50', Earned: '$30', Year: 2013},
    {Name: 'Mr C', Spent: '$30', Earned: '$70', Year: 2013}
];
// normalize/parse data
spendData.forEach(function(d) {
    d.Spent = d.Spent.match(/\d+/);
    d.Earned = d.Earned.match(/\d+/);
});
function remove_empty_bins(source_group) {
    return {
        all:function () {
            return source_group.all().filter(function(d) {
                return d.value != 0;
            });
        }
    };
}
// set crossfilter
var ndx = crossfilter(spendData),
    yearDim  = ndx.dimension(function(d) {return +d.Year;}),
    spendDim = ndx.dimension(function(d) {return Math.floor(d.Spent/10);}),
    earnDim = ndx.dimension(function(d) {return Math.floor(d.Earned/10);}),
    nameDim  = ndx.dimension(function(d) {return d.Name;}),
    spendPerYear = yearDim.group().reduceSum(function(d) {return +d.Spent;}),
    spendPerName = nameDim.group().reduceSum(function(d) {return +d.Spent;}),
    spendHist    = spendDim.group().reduceCount(),
    earnHist    = earnDim.group().reduceCount(),
    nonEmptyHist = remove_empty_bins(spendHist)
    nonEmptyEarnHist = remove_empty_bins(earnHist)

yearRingChart
    .width(200).height(200)
    .dimension(yearDim)
    .group(spendPerYear)
    .innerRadius(50);

spendHistChart
    .width(300).height(200)
    .dimension(spendDim)
    .group(nonEmptyHist)
    .stack(nonEmptyEarnHist)
    .x(d3.scale.ordinal())
    .xUnits(dc.units.ordinal)
    .elasticX(true)
    .elasticY(true);
spendHistChart.xAxis().tickFormat(function(d) {return d*10}); // convert back to base unit
spendHistChart.yAxis().ticks(2);

spenderRowChart
    .width(350).height(200)
    .dimension(nameDim)
    .group(spendPerName)
    .elasticX(true);

dc.renderAll();
</script>

</div>
</body>
</html>

但是,如果您将挣值更改为

var spendData = [
        {Name: 'Mr A', Spent: '$40', Earned: '$70', Year: 2011},
        {Name: 'Mr B', Spent: '$10', Earned: '$20', Year: 2011},
        {Name: 'Mr C', Spent: '$40', Earned: '$40', Year: 2011},
        {Name: 'Mr A', Spent: '$70', Earned: '$170', Year: 2012},
        {Name: 'Mr B', Spent: '$20', Earned: '$30', Year: 2012},
        {Name: 'Mr B', Spent: '$50', Earned: '$50', Year: 2013},  // This is the only change Earned from '$30' to '$50'
        {Name: 'Mr C', Spent: '$30', Earned: '$70', Year: 2013}
    ]

然后它工作正常。

我的案例遇到了同样的 d3 错误,所以我尝试用一​​个简单的例子来重现它。

还有this相关问题

【问题讨论】:

    标签: d3.js dc.js crossfilter


    【解决方案1】:

    Welp,you found the answer already;这只是应用它的问题。

    这确实是同一个问题,可以通过一个组合来解决。

    combinedGroup = combine_groups(nonEmptyHist,nonEmptyEarnHist)
    
    function sel_stack(i) {
        return function(d) {
            return d.value[i];
        };
    }
    
    spendHistChart
        .group(combinedGroup, 'spend', sel_stack(0))
        .stack(combinedGroup, 'earn', sel_stack(1))
    

    我不知道如何处理这个问题; d3.stack 要求数组大小相同,dc.js 也会假设键/值数组相互对应。

    小提琴:http://jsfiddle.net/gordonwoodhull/dwkgud92/3/

    【讨论】:

    • 确实如此。我遇到了另一个导致此方法失败的问题,但现在一切正常。问题是组合的组键是字符串,因此它们在图表中的排序不正确。在您的小提琴中,请注意直方图 x 轴顺序:10 170 20 30 ...您必须将键转换为整数(在这种情况下)才能正确排序。在 combine_groups 函数中:ret.push({key: parseInt(k), value: gm[parseInt(k)]});谢谢!
    • 啊,谢谢你的提示!我将尝试概括该功能。很高兴它有效!
    • 一个快速的解决方案是在您想使用 combine_groups.all 方法并修改那里的键类型时对其进行修补。如果是整数:var original_method = combined_group.all; combined_group.all = function(){ var res = original_method.apply(this, arguments); for (var i=0; i&lt;res.length; i++){ res[i].key = parseInt(res[i].key) } return res };
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多