D3.js分组条形图图例缺失数据 [英] D3.js grouped bar chart legend missing data

查看:198
本文介绍了D3.js分组条形图图例缺失数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有交互式图例的分组条形图。所有似乎显示和工作确定,但图例缺少一个值。我知道问题是,图例是从x0轴上的项目索引,而不是条的索引(x1轴)。

I have a grouped bar chart with an interactive legend. All seems to display and work ok but the legend is missing a value. I know that the issue is that the legend is going off the index of items on the x0 axis rather than the index of the bars (x1 axis). However i do not know how to resolve this.

我的数据包含以下内容:

My data consists of the following:

[{
    "Group": 1,
    "DataPoints": [{
        "BarValue": "Extension Cable",
        "Value": 1
    }]
},
{
    "Group": 2,
    "DataPoints": [{
        "BarValue": "Extension Cable",
        "Value": 1
    },
    {
        "BarValue": "LED Light",
        "Value": 2
    },
    {
        "BarValue": "USB",
        "Value": 4
    },
    {
        "BarValue": "USB Socket",
        "Value": 2
    }]
},
{
    "Group": 3,
    "DataPoints": [{
        "BarValue": "Extension Cable",
        "Value": 2
    },
    {
        "BarValue": "USB",
        "Value": 1
    }]
}]

在此示例中,组是一年中的一个月,即1-12。 BarValue是产品的名称。因此,每个月内每个产品将有一个栏。值是Y值,用于确定图表的高度。

In this example, the group is the number of the month of the year i.e. 1-12. The BarValue is the name of a product. Therefore there will be a bar for each product within each month. And Value is the Y Value to determine the height of the chart.

因此,在我的传说中,我预期会有:

延伸电缆\\
LED灯

USB_LINK
USB Socket

Therefore in my legend i would expect to have:

Extension Cable
LED Light
USB
USB Socket

然而,我实际看到的是: br />

延伸线缆\\
LED灯@
USB

However, what i actually see is:

Extension Cable
LED Light
USB

这告诉我它正在渲染正确,除非它使用月数而不是产品的数量来确定图例。但是我不知道为什么会是这样,我失踪了。

This tells me that it is rendering correctly except it is using the count of the months rather than the products in order to determine the legend. However I am not sure why this is and what i am missing.

这是一个显示问题的图片。如您所见,图例中未显示粉红色的条形图:

Here is an image displaying the issue. As you can see, the pink bar is not represented in the legend:

我的图例代码如下:

       var li = {
            w: 120, h: 30, s: 3, r: 3
        };

        var legendData = d3.set(data.reduce(function (previousValue, currentValue) {
            return previousValue.concat(currentValue.DataPoints.map(function (d) {
                return d.BarValue;   
            }))
        }, [])).values();

        var legend = legend.append("svg:svg")
            .attr("width", li.w)
            .attr("height", height)
            .attr('class', 'legend');

        var g = legend.selectAll("g")
            .data(data.slice())
            .enter().append("svg:g")
            .attr("transform", function (d, i) {
                return "translate(0," + ((i * (li.h + li.s)) + 20) + ")";
            });

        g.append("svg:rect")
            .datum(function (d) { return d.DataPoints;})
            .attr("rx", li.r)
            .attr("ry", li.r)
            .attr("width", li.w)
            .attr("height", li.h)
            .attr('class', function (d, i) { return 'bartag' + legendData[i].replace(/\s+/g, '') + 'rect' })
            .style("fill", function (d, i) { return color(legendData[i]); })
            .on('mouseover', function () {
                $(this).css('cursor', 'pointer')
            })
            .on('click', function (d, i) {
                var active = d.active ? false : true,
                    newOpacity = active ? 1 : 0,
                    id = '.bartag' + legendData[i];
                d3.selectAll('.bartag' + legendData[i])
                        .transition().duration(100)
                        .style('opacity', newOpacity);
                d.active = active;
                if (active) {
                    var test = '.bartag' + legendData[i] + 'rect';
                    d3.selectAll('.bartag' + legendData[i] + 'rect').style("opacity", newOpacity);
                }
                else {
                    var test = d3.selectAll('.bartag' + legendData[i] + 'rect');
                    test.style('opacity', newOpacity);
                }
            });

        g.append("svg:text")
            .attr("x", 5)
            .attr("y", li.h / 2)
            .attr("dy", "0.35em")
            .attr("text-anchor", "start")
            .text(function (d, i) { return legendData[i]; })
            .on('mouseover', function () {
                $(this).css('cursor', 'pointer')
            })
            .on('click', function (d, i) {
                var active = d.active ? false : true,
                    newOpacity = active ? 1 : 0,
                    id = '.bartag' + legendData[i].replace(/\s+/g, '');
                d3.selectAll('.bartag' + legendData[i].replace(/\s+/g, ''))
                        .transition().duration(100)
                        .style('opacity', newOpacity);
                d.active = active;
                if (active) {
                    var test = '.bartag' + legendData[i].replace(/\s+/g, '') + 'rect';
                    d3.selectAll('.bartag' + legendData[i].replace(/\s+/g, '') + 'rect').style("opacity", newOpacity);
                }
                else {
                    var test = d3.selectAll('.bartag' + legendData[i].replace(/\s+/g, '') + 'rect');
                    test.style('opacity', newOpacity);
                }
            });

为了参考,这里是我完整的代码:

And for reference, here is my complete code:

var margin = { top: 20, right: 0, bottom: 40, left: 50 },
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom,
    tooltipTextColour = "white",
    color = d3.scale.ordinal().range(["#FF9797", "#86BCFF", "#33FDC0", "#EFA9FE", "#7BCAE1", "#8C8CFF", "#80B584", "#C88E8E", "#DD597D", "#D8F0F8", "#DD597D", "#D6C485", "#990099", "#5B5BFF", "#1FCB4A", "#000000", "#00BFFF", "#BE81F7", "#BDBDBD", "#F79F81"]);

if (data.length > 0) {
    var legendData = d3.set(data.reduce(function (previousValue, currentValue) {
        return previousValue.concat(currentValue.DataPoints.map(function (d) {
            return d.BarValue;   
        }))
    }, [])).values();

    var x0 = d3.scale.ordinal()
                    .rangeRoundBands([0, width], .1);

    var x1 = d3.scale.ordinal();

    var y = d3.scale.linear().range([height, 0]);

    var xAxis = d3.svg.axis()
                        .scale(x0)
                        .orient("bottom");

    var yAxis = d3.svg.axis()
                        .scale(y)
                        .orient("left")
                        .tickFormat(d3.format(".2s"));

    var svg = placeholder.append("svg")
                            .attr('width', width + margin.left)
                            .attr('height', height + margin.top + margin.bottom)
                            .attr('class', 'chart')
                            .append('g')
                                .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

    x0.domain(data.map(function (d) { return d.Group; }));

    x1.domain(d3.set(data.reduce(function (previousValue, currentValue) {
        return previousValue.concat(currentValue.DataPoints.map(function (d) {
            return d.BarValue;
        }))
    }, [])).values()).rangeRoundBands([0, x0.rangeBand()]);

    y.domain([0, d3.max(data, function(d){
        return d3.max(d.DataPoints, function (d) {
            return d.Value;
        })
    })]);

    svg.append('g')
        .attr('class', 'x axis')
        .attr('transform', 'translate(0,' + height + ')')
        .call(xAxis);

    svg.append('g')
        .attr('class', 'y axis')
        .call(yAxis)
        .append('text')
          .attr("transform", "rotate(-90)")
          .attr("y", 6)
          .attr("dy", ".71em")
          .style("text-anchor", "end")
          .text("Complaints");

    var type = svg.selectAll(".type")
                    .data(data)
                    .enter()
                    .append('g')
                        .attr('class', 'type')
                        .attr('transform', function (d) { return 'translate(' + x0(d.Group) + ',0)'; });

    var rect = type.selectAll("rect")
            .data(function (d) { return d.DataPoints; })
            .enter()
            .append('rect')
            .attr('width', x1.rangeBand())
            .attr('x', function (d) { return x1(d.BarValue); })
            .attr('y', function (d) { return y(d.Value); })
            .attr('class', function (d) { return 'bartag' + d.BarValue.replace(/\s+/g, '') })
            .attr('height', function (d) { return height - y(d.Value); })
            .style('fill', function (d, i) { return color(d.BarValue); });

    rect.append('text')
            .attr('x', function (d) { return x1(d.BarValue); })
            .attr('y', function (d) { return y(d.Value); })
            .attr("dy", "0.35em")
            .attr("text-anchor", "middle")
            .text(function (d, i) { return legendData[i]; });

    if (legend != null) {
        var li = {
            w: 120, h: 30, s: 3, r: 3
        };

        var legend = legend.append("svg:svg")
            .attr("width", li.w)
            .attr("height", height)
            .attr('class', 'legend');

        var g = legend.selectAll("g")
            .data(data.slice())
            .enter().append("svg:g")
            .attr("transform", function (d, i) {
                return "translate(0," + ((i * (li.h + li.s)) + 20) + ")";
            });

        g.append("svg:rect")
            .datum(function (d) { return d.DataPoints;})
            .attr("rx", li.r)
            .attr("ry", li.r)
            .attr("width", li.w)
            .attr("height", li.h)
            .attr('class', function (d, i) { return 'bartag' + legendData[i].replace(/\s+/g, '') + 'rect' })
            .style("fill", function (d, i) { return color(legendData[i]); })
            .on('mouseover', function () {
                $(this).css('cursor', 'pointer')
            })
            .on('click', function (d, i) {
                var active = d.active ? false : true,
                    newOpacity = active ? 1 : 0,
                    id = '.bartag' + legendData[i];
                d3.selectAll('.bartag' + legendData[i])
                        .transition().duration(100)
                        .style('opacity', newOpacity);
                d.active = active;
                if (active) {
                    var test = '.bartag' + legendData[i] + 'rect';
                    d3.selectAll('.bartag' + legendData[i] + 'rect').style("opacity", newOpacity);
                }
                else {
                    var test = d3.selectAll('.bartag' + legendData[i] + 'rect');
                    test.style('opacity', newOpacity);
                }
            });

        g.append("svg:text")
            .attr("x", 5)
            .attr("y", li.h / 2)
            .attr("dy", "0.35em")
            .attr("text-anchor", "start")
            .text(function (d, i) { return legendData[i]; })
            .on('mouseover', function () {
                $(this).css('cursor', 'pointer')
            })
            .on('click', function (d, i) {
                var active = d.active ? false : true,
                    newOpacity = active ? 1 : 0,
                    id = '.bartag' + legendData[i].replace(/\s+/g, '');
                d3.selectAll('.bartag' + legendData[i].replace(/\s+/g, ''))
                        .transition().duration(100)
                        .style('opacity', newOpacity);
                d.active = active;
                if (active) {
                    var test = '.bartag' + legendData[i].replace(/\s+/g, '') + 'rect';
                    d3.selectAll('.bartag' + legendData[i].replace(/\s+/g, '') + 'rect').style("opacity", newOpacity);
                }
                else {
                    var test = d3.selectAll('.bartag' + legendData[i].replace(/\s+/g, '') + 'rect');
                    test.style('opacity', newOpacity);
                }
            });
    }
}
else {
    placeholder.append('p').text('No Data to Display').style('font-weight', 'bold');
}

if (callback) {
    callback();
}



我希望这是足够的信息,如果有任何人可以提供任何帮助,非常感谢。

I hope that is enough information and if there is any help anyone could provide, it would be greatly appreciated.

编辑

不知道哪个更容易解决,所以我会把这个。

EDIT

I dont know which is easier to solve, so i will put this up.

如果我使用legendData作为我的数据的图例,所有图例选项出现。然而,问题是,传说不再是互动的。这是因为legendData只是包含产品名称的字符串,所以当它试图获得d.active切换bar的可见性时,它失败,因为它是一个字符串而不是一个对象。

All legend options appear if i use legendData as my data for the legend. However, the problem then is that the legend is no longer interactive. This is because legendData is simply strings containing the product name so when it attempts to get d.active to toggle the visibility of the bar, it fails as it is a string and not an object.

推荐答案

我会进行以下修改:


  1. 图例作为对象数组(而不是字符串),每个对象包含名称作为字符串和活动/非活动的bollean。

  1. get your legend as an array of objects (instead of strings), each one containing the name as a string and a bollean for active/inactive.

var legendData = d3.set(data.reduce(function (previousValue, currentValue) {
    return previousValue.concat(currentValue.DataPoints.map(function (d) {
        return d.BarValue;   
    }))
}, [])).values();
//Add this:
legendData=legendData.map(function(s){
   return {name:s, active:true};
});


  • legendData 映射到图例对象

    legend.selectAll("g")
        .data(legendData)
        .enter().append("svg:g")
    

    并删除前面的绑定:

    g.append("svg:rect")
        //.datum(function (d) { return d.DataPoints;}) //remove this
        .attr("rx", li.r)
    


  • 现在每个图例项都知道对应的字符串及其状态。

    now each legend item knows the corresponding string and its state.


      <数据的 name 字段:对于图例部分中的任何 legendData [i] code> d.name (注意,你没有明确引用 legendData 了,因为你之前已经完成绑定) 。对于活动字段似乎没有任何操作(您已经在使用 d.active )。
    1. use the name field of the data where applicable: for any legendData[i] within the legend part, you should have d.nameinstead (notice that you're not explicitly referencing legendData anymore, since you've done the binding earlier). There seems to be nothing to do for the active field (you are already using d.active).

    这篇关于D3.js分组条形图图例缺失数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆