javascript - D3.js 分组条形图图例缺失数据

标签 javascript d3.js charts legend

我有一个带有交互式图例的分组条形图。一切似乎都显示和工作正常,但图例缺少一个值。我知道问题在于图例偏离了 x0 轴上的项目索引,而不是条形图(x1 轴)的索引。但是我不知道如何解决这个问题。

我的数据包含以下内容:

[{
    "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 是产品的名称。因此,每个月内的每种产品都会有一个栏。 Value 是确定图表高度的 Y 值。

因此,在我的传奇中,我希望有:

延长线
LED灯
USB
USB socket

然而,我实际看到的是:

延长线
LED灯
USB

这告诉我它渲染正确,只是它使用月份计数而不是产品来确定图例。但是我不确定为什么会这样以及我缺少什么。

这是显示问题的图像。正如您所看到的,粉红色条未在图例中表示:

enter image description here

我的图例代码如下:

       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);
                }
            });

作为引用,这是我的完整代码:

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();
}

我希望这是足够的信息,如果有人可以提供任何帮助,我们将不胜感激。

编辑

我不知道哪个更容易解决,所以我把它放上来。

如果我使用 legendData 作为图例数据,则会显示所有图例选项。然而,问题是图例不再具有交互性。这是因为 legendData 只是包含产品名称的字符串,因此当它尝试使用 d.active 来切换栏的可见性时,它会失败,因为它是字符串而不是对象。

最佳答案

我会进行以下修改:

  1. 将图例作为对象数组(而不是字符串)获取,每个对象都包含字符串形式的名称和表示事件/非事件的 bool 值。

    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};
    });
    
  2. legendData 映射到图例对象

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

    并删除以前的绑定(bind):

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

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

  • 在适用的情况下使用数据的 name 字段:对于图例部分中的任何 legendData[i],您应该具有 d.name 相反(请注意,您不再显式引用 legendData,因为您之前已经完成了绑定(bind))。对于 active 字段似乎没有什么可做的(您已经在使用 d.active)。
  • 关于javascript - D3.js 分组条形图图例缺失数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35748190/

    相关文章:

    javascript - Angular 2 路由器动画 - 找不到模块 '@angular/platform-browser/animations'

    javascript - 通过javascript/D3加载json

    javascript - HIGHCHARTS:drilldown.js 更改饼图标签颜色

    javascript - highchart 中的动画系列

    javascript - c3js d3 dash-stroke 仅在 rect 元素的一侧

    javascript - 保持 d3 图表中轴刻度之间的距离

    javascript - 添加行时,jQuery 数据表滚动到底部

    javascript - 使用更改功能

    javascript - 将 HTML 值与 jquery 输入值进行比较

    javascript - 在可拖动点与控制点之间绘制曲线以调整曲线 SVG 和 d3.js