简体   繁体   English

向D3堆叠条形图添加图例

[英]Adding a legend to D3 stacked bar graph

I've already read Adding a legend to D3 graph V4 and the questions linked within. 我已经阅读了向D3图形V4添加图例和其中的链接问题。 Also, after a lot of searching... I'm surprised that D3 doesn't appear to have a standardized way of creating legends. 此外,经过大量搜索之后,我很惊讶D3似乎没有创建图例的标准化方法。

Here is my code: 这是我的代码:

 var dataAsCsv = `date,col_1,col_2 11/1/2012,1977652,1802851 12/1/2012,1128739,948687 1/1/2013,1201944,1514667 2/1/2013,1863148,1834006 3/1/2013,1314851,1906060 4/1/2013,1283943,1978702 5/1/2013,1127964,1195606 6/1/2013,1773254,977214 7/1/2013,1929574,1127450 8/1/2013,1980411,1808161 9/1/2013,1405691,1182788 10/1/2013,1336790,937890 11/1/2013,1851053,1358400 12/1/2013,1472623,1214610 1/1/2014,1155116,1757052 2/1/2014,1571611,1935038 3/1/2014,1898348,1320348 4/1/2014,1444838,1934789 5/1/2014,1235087,950194 6/1/2014,1272040,1580656 7/1/2014,980781,1680164 8/1/2014,1391291,1115999 9/1/2014,1211125,1542148 10/1/2014,1020824,1782795 11/1/2014,1685081,926612 12/1/2014,1469254,1767071 1/1/2015,1168523,935897 2/1/2015,1602610,1450541 3/1/2015,1830278,1354876 4/1/2015,1275158,1412555 5/1/2015,1560961,1839718 6/1/2015,949948,1587130 7/1/2015,1413765,1494446 8/1/2015,1166141,1305105 9/1/2015,958975,1202219 10/1/2015,902696,1023987 11/1/2015,961441,1865628 12/1/2015,1363145,1954046 1/1/2016,1862878,1470741 2/1/2016,1723891,1042760 3/1/2016,1906747,1169012 4/1/2016,1963364,1927063 5/1/2016,1899735,1936915 6/1/2016,1300369,1430697 7/1/2016,1777108,1401210 8/1/2016,1597045,1566763 9/1/2016,1558287,1140057 10/1/2016,1965665,1953595 11/1/2016,1800438,937551 12/1/2016,1689152,1221895 1/1/2017,1607824,1963282 2/1/2017,1878431,1415658 3/1/2017,1730296,1947106 4/1/2017,1956756,1696780 5/1/2017,1746673,1662892 6/1/2017,989702,1537646 7/1/2017,1098812,1592064 8/1/2017,1861973,1892987 9/1/2017,1129596,1406514 10/1/2017,1528632,1725020 11/1/2017,925850,1795575`; var margin = {top: 20, right: 20, bottom: 50, left: 80}, width = 1300 - margin.left - margin.right, height = 700 - margin.top - margin.bottom; var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom); var g = svg.append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // parse the date / time // look at the .csv in Notepad! DO NOT LOOK AT EXCEL! var parseDate = d3.timeParse("%m/%d/%Y"); var x = d3.scaleTime() .range([0, width - margin.left - margin.right]); var y = d3.scaleLinear().range([height, 0]); var z = d3.scaleOrdinal() .range(["#CE1126", "#00B6D0"]); // red and blue var xMonthAxis = d3.axisBottom(x) .ticks(d3.timeMonth.every(1)) .tickFormat(d3.timeFormat("%b")); // label every month var xYearAxis = d3.axisBottom(x) .ticks(d3.timeYear.every(1)) .tickFormat(d3.timeFormat("%Y")); // label every year var yAxis = d3.axisLeft(y).tickFormat(d3.format('.2s')); var formatNum = d3.format(",") var data = d3.csvParse(dataAsCsv, function(d, i, columns) { for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]]; d.total = t; return d; }) data.forEach(function(d) { //console.log(parseDate(d.date)); d.date = parseDate(d.date); }); var keys = data.columns.slice(1); var barWidth = (width - margin.right- margin.left)/(data.length+1); data.sort(function(a, b) { return b.date - a.date; }); x.domain(d3.extent( data, function(d){ return d.date }) ); var max = x.domain()[1]; var min = x.domain()[0]; var datePlusOneMonth = d3.timeDay.offset(d3.timeMonth.offset(max, 1), -1); // last day of current month: move up one month, back one day x.domain([min,datePlusOneMonth]); y.domain([0, d3.max(data, function(d) { return d.total; })]).nice(); z.domain(keys); // the bars g.append("g") .selectAll("g") .data(d3.stack().keys(keys)(data)) .enter().append("g") .attr('class', function(d) { return d.key; }) .attr("fill", function(d) { return z(d.key); }) .selectAll("rect") .data(function(d) { return d; }) .enter() .append("rect") .attr("x", function(d) { return x(d.data.date); }) .attr("y", function(d) { return y(d[1]); }) .attr("height", function(d) { return y(d[0]) - y(d[1]); }) .attr("width", barWidth) .on("mouseover", function(d) { //Get this bar's x/y values, then augment for the tooltip var xPosition = parseFloat(d3.mouse(this)[0]) + barWidth * 4.75; var yPosition = parseFloat(d3.mouse(this)[1]) - 30; var value = d.data[d3.select(this.parentNode).attr('class')]; // differentiating between col_1 and col_2 values //Update the tooltip position and value d3.select("#tooltip") .style("left", xPosition + "px") .style("top", yPosition + "px") .select("#value") .text(formatNum(value)); // return the value d3.select("#tooltip") .style("left", xPosition + "px") .style("top", yPosition + "px") .select("#month") .text(d3.timeFormat("%B %Y")(d.data.date)); // return the date //Show the tooltip d3.select("#tooltip").classed("hidden", false); }) .on("mouseout", function() { //Hide the tooltip d3.select("#tooltip").classed("hidden", true); }); // x-axis var monthAxis = g.append("g") .attr("class", "axis") .attr("transform", "translate(0," + height + ")") .call(xMonthAxis); const firstDataYear = x.domain()[0]; xYearAxis.tickValues([firstDataYear].concat(x.ticks())); var yearAxis = g.append("g") .attr("class", "axis") .attr("transform", "translate(0," + (height + 25) + ")") .call(xYearAxis); var sessionAxis = g.append("g") .attr("class", "axis") .call(yAxis); monthAxis.selectAll("g").select("text") .attr("transform","translate(" + barWidth/2 + ",0)"); 
 #tooltip { position: absolute; width: 200px; height: auto; padding: 10px; background-color: white; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); pointer-events: none; } #tooltip.hidden { display: none; } #tooltip p { margin: 0; font-family: sans-serif; font-size: 16px; line-height: 20px; } rect:hover { fill:orange; } 
 <script src="https://d3js.org/d3.v4.js"></script> <div id="tooltip" class="hidden"> <p><strong>Month: </strong><span id="month"></span><p> <p><strong>Value: </strong><span id="value"></span></p> </div> 

I'm trying to add a legend to this graph, and have tried a few options already, eg: http://bl.ocks.org/louking/6491d18c9cef56011971be76a85cde8e , http://d3-legend.susielu.com/ , none of which seem to work well for my situation. 我想一个图例添加该图形,并尝试了一些选项已经,例如: http://bl.ocks.org/louking/6491d18c9cef56011971be76a85cde8ehttp://d3-legend.susielu.com/ ,无其中似乎适合我的情况。

All I need in this legend is a rectangular box with a blue square with label Column 2 and a rectangular box with a red square with label Column 1 , with customizable positioning for the entire legend (ie, I need to be able to control where the legend is placed). 在此图例中,我需要的是一个带有标签为Column 2的蓝色正方形的矩形框和带有标签为Column 1的红色正方形的矩形框,并且整个图例的位置都可以自定义(即,我需要能够控制图例放置)。

Apparently React can do something like this as well... but I haven't had time to learn React yet. 显然,React也可以做类似的事情……但是我还没有时间学习React。

Insert the following lines: 插入以下行:

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.7.0/d3-legend.min.js"></script>

as well as 以及

var options = d3.keys(data[0]).filter(function(key) { return key !== "date"; }).reverse();
    var legend = svg.selectAll(".legend")
                    .data(options.slice().filter(function(type){ return type != "total"}))
                    .enter().append("g")
                    .attr("class", "legend")
                    .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

    legend.append("rect")
          .attr("x", width - 18)
          .attr("width", 18)
          .attr("height", 18)
          .style("fill", z);

    function capitalizeFirstLetter(string) {
        return string.charAt(0).toUpperCase() + string.slice(1);
    }

    legend.append("text")
          .attr("x", width - 24)
          .attr("y", 9)
          .attr("dy", ".35em")
          .style("text-anchor", "end")
          .text(function(d) { return d;});

near the bottom. 在底部附近。 In addition, some CSS: 另外,一些CSS:

.legend {
        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
        font-size: 60%;
    }

 var dataAsCsv = `date,col_1,col_2 11/1/2012,1977652,1802851 12/1/2012,1128739,948687 1/1/2013,1201944,1514667 2/1/2013,1863148,1834006 3/1/2013,1314851,1906060 4/1/2013,1283943,1978702 5/1/2013,1127964,1195606 6/1/2013,1773254,977214 7/1/2013,1929574,1127450 8/1/2013,1980411,1808161 9/1/2013,1405691,1182788 10/1/2013,1336790,937890 11/1/2013,1851053,1358400 12/1/2013,1472623,1214610 1/1/2014,1155116,1757052 2/1/2014,1571611,1935038 3/1/2014,1898348,1320348 4/1/2014,1444838,1934789 5/1/2014,1235087,950194 6/1/2014,1272040,1580656 7/1/2014,980781,1680164 8/1/2014,1391291,1115999 9/1/2014,1211125,1542148 10/1/2014,1020824,1782795 11/1/2014,1685081,926612 12/1/2014,1469254,1767071 1/1/2015,1168523,935897 2/1/2015,1602610,1450541 3/1/2015,1830278,1354876 4/1/2015,1275158,1412555 5/1/2015,1560961,1839718 6/1/2015,949948,1587130 7/1/2015,1413765,1494446 8/1/2015,1166141,1305105 9/1/2015,958975,1202219 10/1/2015,902696,1023987 11/1/2015,961441,1865628 12/1/2015,1363145,1954046 1/1/2016,1862878,1470741 2/1/2016,1723891,1042760 3/1/2016,1906747,1169012 4/1/2016,1963364,1927063 5/1/2016,1899735,1936915 6/1/2016,1300369,1430697 7/1/2016,1777108,1401210 8/1/2016,1597045,1566763 9/1/2016,1558287,1140057 10/1/2016,1965665,1953595 11/1/2016,1800438,937551 12/1/2016,1689152,1221895 1/1/2017,1607824,1963282 2/1/2017,1878431,1415658 3/1/2017,1730296,1947106 4/1/2017,1956756,1696780 5/1/2017,1746673,1662892 6/1/2017,989702,1537646 7/1/2017,1098812,1592064 8/1/2017,1861973,1892987 9/1/2017,1129596,1406514 10/1/2017,1528632,1725020 11/1/2017,925850,1795575`; var margin = {top: 20, right: 20, bottom: 50, left: 80}, width = 1300 - margin.left - margin.right, height = 700 - margin.top - margin.bottom; var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom); var g = svg.append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // parse the date / time // look at the .csv in Notepad! DO NOT LOOK AT EXCEL! var parseDate = d3.timeParse("%m/%d/%Y"); var x = d3.scaleTime() .range([0, width - margin.left - margin.right]); var y = d3.scaleLinear().range([height, 0]); var z = d3.scaleOrdinal() .range(["#CE1126", "#00B6D0"]); // red and blue var xMonthAxis = d3.axisBottom(x) .ticks(d3.timeMonth.every(1)) .tickFormat(d3.timeFormat("%b")); // label every month var xYearAxis = d3.axisBottom(x) .ticks(d3.timeYear.every(1)) .tickFormat(d3.timeFormat("%Y")); // label every year var yAxis = d3.axisLeft(y).tickFormat(d3.format('.2s')); var formatNum = d3.format(",") var data = d3.csvParse(dataAsCsv, function(d, i, columns) { for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]]; d.total = t; return d; }) data.forEach(function(d) { //console.log(parseDate(d.date)); d.date = parseDate(d.date); }); var keys = data.columns.slice(1); var barWidth = (width - margin.right- margin.left)/(data.length+1); data.sort(function(a, b) { return b.date - a.date; }); x.domain(d3.extent( data, function(d){ return d.date }) ); var max = x.domain()[1]; var min = x.domain()[0]; var datePlusOneMonth = d3.timeDay.offset(d3.timeMonth.offset(max, 1), -1); // last day of current month: move up one month, back one day x.domain([min,datePlusOneMonth]); y.domain([0, d3.max(data, function(d) { return d.total; })]).nice(); z.domain(keys); // the bars g.append("g") .selectAll("g") .data(d3.stack().keys(keys)(data)) .enter().append("g") .attr('class', function(d) { return d.key; }) .attr("fill", function(d) { return z(d.key); }) .selectAll("rect") .data(function(d) { return d; }) .enter() .append("rect") .attr("x", function(d) { return x(d.data.date); }) .attr("y", function(d) { return y(d[1]); }) .attr("height", function(d) { return y(d[0]) - y(d[1]); }) .attr("width", barWidth) .on("mouseover", function(d) { //Get this bar's x/y values, then augment for the tooltip var xPosition = parseFloat(d3.mouse(this)[0]) + barWidth * 4.75; var yPosition = parseFloat(d3.mouse(this)[1]) - 30; var value = d.data[d3.select(this.parentNode).attr('class')]; // differentiating between col_1 and col_2 values //Update the tooltip position and value d3.select("#tooltip") .style("left", xPosition + "px") .style("top", yPosition + "px") .select("#value") .text(formatNum(value)); // return the value d3.select("#tooltip") .style("left", xPosition + "px") .style("top", yPosition + "px") .select("#month") .text(d3.timeFormat("%B %Y")(d.data.date)); // return the date //Show the tooltip d3.select("#tooltip").classed("hidden", false); }) .on("mouseout", function() { //Hide the tooltip d3.select("#tooltip").classed("hidden", true); }); // x-axis var monthAxis = g.append("g") .attr("class", "axis") .attr("transform", "translate(0," + height + ")") .call(xMonthAxis); const firstDataYear = x.domain()[0]; xYearAxis.tickValues([firstDataYear].concat(x.ticks())); var yearAxis = g.append("g") .attr("class", "axis") .attr("transform", "translate(0," + (height + 25) + ")") .call(xYearAxis); var sessionAxis = g.append("g") .attr("class", "axis") .call(yAxis); monthAxis.selectAll("g").select("text") .attr("transform","translate(" + barWidth/2 + ",0)"); var options = d3.keys(data[0]).filter(function(key) { return key !== "date"; }).reverse(); var legend = svg.selectAll(".legend") .data(options.slice().filter(function(type){ return type != "total"})) .enter().append("g") .attr("class", "legend") .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; }); legend.append("rect") .attr("x", width - 18) .attr("width", 18) .attr("height", 18) .style("fill", z); function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } legend.append("text") .attr("x", width - 24) .attr("y", 9) .attr("dy", ".35em") .style("text-anchor", "end") .text(function(d) { return d;}); 
 #tooltip { position: absolute; width: 200px; height: auto; padding: 10px; background-color: white; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); pointer-events: none; } #tooltip.hidden { display: none; } #tooltip p { margin: 0; font-family: sans-serif; font-size: 16px; line-height: 20px; } rect:hover { fill:orange; } .legend { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 60%; } 
 <script src="https://d3js.org/d3.v4.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.7.0/d3-legend.min.js"></script> <div id="tooltip" class="hidden"> <p><strong>Month: </strong><span id="month"></span><p> <p><strong>Value: </strong><span id="value"></span></p> </div> 

The code creates a legend on the top right. 该代码在右上方创建了一个图例。

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM