D3 堆栈折线图

D3 Stack Line Chart

我现在正在使用 D3(版本 4)制作多线折线图,遇到了一些麻烦。这是我当前代码的结果:

我认为这与我的 x 比例有关,但我无法弄清楚哪里出了问题。我一直在遵循教程 here,并稍作修改,因为我使用的是 V4。任何帮助将不胜感激。

 var parseDate = d3.timeParse("%Y");

var color = d3.scaleOrdinal(["#969FFD","#7173BF","#4B4C7F","#262640", "red"]);

var mySvg = d3.select("#chart8").append('svg')
   .attr('width', width + margin.right + margin.left)
   .attr('height', height + margin.bottom + margin.top)

var chartGroup = mySvg.append("g")
   .attr("class","fullGroup")
   .attr("transform", "translate("+margin.left+","+margin.top+")");

var parseDate = d3.timeParse("%Y");

d3.csv("../Assets/datasets/allDrugs3.csv", function(error, data){
if (error) throw error;

// FORMAT THE DATA //
var labelVar = "year";
var varNames = d3.keys(data[0]).slice(1);
    // Alternatively --> .filter(function(key){ return key !== labelVar;})
console.log(varNames); // <-- Names of drugs, used for color array

// Add color domain of names
color.domain(varNames);

var seriesData = varNames.map(function(name){
   return {
       name: name,
       values: data.map(function(d) {
            return { name: name, label: parseDate(d[labelVar]), value: +d[name]};
       })
   };
});

console.log(seriesData);

// Y-SCALE //

var yScale = d3.scaleLinear()
    .domain([
        d3.min(seriesData, function(c){ 
        return d3.min(c.values, function(d){ return d.value; });
        }), 
        d3.max(seriesData, function(c){
            return d3.max(c.values, function(d){ return d.value;});
        })
    ])
    .range([height, 0]);

console.log(
    "The y domain is",
    d3.min(seriesData, function(c){ 
        return d3.min(c.values, function(d){ return d.value; })}),
    "to", 
    d3.max(seriesData, function(c){
            return d3.max(c.values, function(d){ return d.value;});
        }));

// X-SCALE // 

var xYears = data.map(function(d){return parseDate(d.year);});    
var xScale = d3.scaleTime()
    .domain(xYears)
    .range([0, width]);

console.log(
    "The x domain is",
    xYears
)

var series = chartGroup.selectAll(".series")
    .data(seriesData)
    .enter().append("g")
        .attr("class","series");

var line = d3.line()
    .x(function(d){return xScale(d.label)})
    .y(function(d){return yScale(d.value); })
    .curve(d3.curveCardinal);

series.append("path")
    .attr("d", function(d){ return line(d.values); })
    .style("stroke", function (d) { return color(d.name); })
    .style("stroke-width", "4px")
    .style("fill", "none");

// Axes

var yAxis = d3.axisLeft(yScale).ticks(5).tickPadding(5);
var xAxis = d3.axisBottom(xScale).ticks(10);

chartGroup.append("g")
   .attr("class","Xaxis")
   .call(xAxis)
   .attr("transform","translate(0,"+height+")")
.selectAll("text")
    .style("text-anchor", "end")
    .attr("dx", "-.8em")
    .attr("dy", ".15em")
    .attr("transform", "rotate(-65)");

chartGroup.append("g")
    .attr("class","Yaxis")
    .call(yAxis)
    .selectAll("text")
    .style("text-anchor", "middle")
    .attr("dy", "-1em")
    .attr("transform", "rotate(-90)");
});

这是数据:

key,deaths,year
heroin,289,2007
heroin,360,2008
heroin,238,2009
heroin,247,2010
heroin,392,2011
heroin,399,2012
heroin,464,2013
heroin,578,2014
heroin,748,2015
heroin,1212,2016
opiods,280,2007
opiods,251,2008
opiods,311,2009
opiods,342,2010
opiods,311,2011
opiods,302,2012
opiods,316,2013
opiods,330,2014
opiods,351,2015
opiods,418,2016
alchohol,175,2007
alchohol,162,2008
alchohol,160,2009
alchohol,161,2010
alchohol,195,2011
alchohol,187,2012
alchohol,238,2013
alchohol,270,2014
alchohol,310,2015
alchohol,582,2016
benzodiazepine,48,2007
benzodiazepine,52,2008
benzodiazepine,58,2009
benzodiazepine,68,2010
benzodiazepine,73,2011
benzodiazepine,37,2012
benzodiazepine,69,2013
benzodiazepine,103,2014
benzodiazepine,91,2015
benzodiazepine,126,2016
cocaine,157,2007
cocaine,162,2008
cocaine,135,2009
cocaine,148,2010
cocaine,153,2011
cocaine,248,2012
cocaine,154,2013
cocaine,198,2014
cocaine,221,2015
cocaine,463,2016

这里有很多,只 post 一些工作代码可能会更容易。你的想法很好,但有几个地方有问题:

var varNames = d3.keys(data[0]).slice(1);
// Alternatively --> .filter(function(key){ return key !== labelVar;})
console.log(varNames); // <-- Names of drugs, used for color array

如果您查看控制台,您会发现它没有为您提供药物名称;它为您提供了第一行数据的一部分,这并不是很有用。

对于您拥有的格式的数据,一种选择是使用 d3.nest()。这将获取您的数组并提供一个按您选择的键分组的新数组。在这种情况下,我们可以像这样按药物分组:

 var nested = d3.nest()
    .key(d => d.key)
    .map(data)

现在 nested 将是一个对象数组,例如:

[{alchohol: [{key: "alchohol", deaths: "175", year: "2007"},...}, 
 {benzodiazepine: [{key: "benzodiazepine", deaths: "48", year: "2007"}...
]

这让休息变得很自然。您可以使用 nested.keys() 获取键,使用 .entries()

获取数组

关于比例尺要记住的一件事是 d3.max() returns 自然顺序中的最大值 — 您需要使用 .domain([0 , d3.max(data, d => +d.deaths) ])[= 强制比例尺中的数字顺序23=]

一旦这一切都理顺了,你就可以将 nested 传递给 data() 函数,然后将 .entries() 传递给你的线生成器,一切都运行良好。

这是一个应该有所帮助的工作示例:

<html>
<head>
    <script src="d3/d3.min.js"></script>
    <script src="d3-selection-multi/d3-selection-multi.min.js"></script>
    <style>
    #chart div {
            background-color: steelblue;
            color: white;
            padding: 8px;
            text-align: right;
            margin:1px;
            font: 10px sans-serf
        }
        div#graphic {
            display: inline-block;
        /* border: 1px solid #333;*/
        }
        .title {
            fill: #666;
            font-family: Arial, Helvetica, sans-serif;
            text-anchor: middle;
            font-size: 24px;
        }
        .axis .domain, .axis .tick{
            stroke: #000;
            fill: none;
        }
        .bar {
            fill:steelblue;
            stroke: #444;
        }
    </style>
</head>
<body>
<div id ="chart"></div>
<div id="chart8"></div>
<button onclick="refresh()">refresh</button>
<script>
data = [{"key":"heroin","deaths":"289","year":"2007"},{"key":"heroin","deaths":"360","year":"2008"},{"key":"heroin","deaths":"238","year":"2009"},{"key":"heroin","deaths":"247","year":"2010"},{"key":"heroin","deaths":"392","year":"2011"},{"key":"heroin","deaths":"399","year":"2012"},{"key":"heroin","deaths":"464","year":"2013"},{"key":"heroin","deaths":"578","year":"2014"},{"key":"heroin","deaths":"748","year":"2015"},{"key":"heroin","deaths":"1212","year":"2016"},{"key":"opiods","deaths":"280","year":"2007"},{"key":"opiods","deaths":"251","year":"2008"},{"key":"opiods","deaths":"311","year":"2009"},{"key":"opiods","deaths":"342","year":"2010"},{"key":"opiods","deaths":"311","year":"2011"},{"key":"opiods","deaths":"302","year":"2012"},{"key":"opiods","deaths":"316","year":"2013"},{"key":"opiods","deaths":"330","year":"2014"},{"key":"opiods","deaths":"351","year":"2015"},{"key":"opiods","deaths":"418","year":"2016"},{"key":"alchohol","deaths":"175","year":"2007"},{"key":"alchohol","deaths":"162","year":"2008"},{"key":"alchohol","deaths":"160","year":"2009"},{"key":"alchohol","deaths":"161","year":"2010"},{"key":"alchohol","deaths":"195","year":"2011"},{"key":"alchohol","deaths":"187","year":"2012"},{"key":"alchohol","deaths":"238","year":"2013"},{"key":"alchohol","deaths":"270","year":"2014"},{"key":"alchohol","deaths":"310","year":"2015"},{"key":"alchohol","deaths":"582","year":"2016"},{"key":"benzodiazepine","deaths":"48","year":"2007"},{"key":"benzodiazepine","deaths":"52","year":"2008"},{"key":"benzodiazepine","deaths":"58","year":"2009"},{"key":"benzodiazepine","deaths":"68","year":"2010"},{"key":"benzodiazepine","deaths":"73","year":"2011"},{"key":"benzodiazepine","deaths":"37","year":"2012"},{"key":"benzodiazepine","deaths":"69","year":"2013"},{"key":"benzodiazepine","deaths":"103","year":"2014"},{"key":"benzodiazepine","deaths":"91","year":"2015"},{"key":"benzodiazepine","deaths":"126","year":"2016"},{"key":"cocaine","deaths":"157","year":"2007"},{"key":"cocaine","deaths":"162","year":"2008"},{"key":"cocaine","deaths":"135","year":"2009"},{"key":"cocaine","deaths":"148","year":"2010"},{"key":"cocaine","deaths":"153","year":"2011"},{"key":"cocaine","deaths":"248","year":"2012"},{"key":"cocaine","deaths":"154","year":"2013"},{"key":"cocaine","deaths":"198","year":"2014"},{"key":"cocaine","deaths":"221","year":"2015"},{"key":"cocaine","deaths":"463","year":"2016"}]

var margin = {
    top: 20,
    bottom: 20,
    left: 20,
    right: 20
}
var width = 800 - margin.left - margin.right,
    height = 600  - margin.top - margin.bottom

var parseDate = d3.timeParse("%Y");

var color = d3.scaleOrdinal(["#969FFD","#7173BF","#4B4C7F","#262640", "red"]);

var mySvg = d3.select("#chart8").append('svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.bottom + margin.top)

var chartGroup = mySvg.append("g")
.attr("class","fullGroup")
.attr("transform", "translate("+margin.left+","+margin.top+")");

var parseDate = d3.timeParse("%Y");

d3.csv("./allDrugs3.csv", function(error, data){
    console.log(JSON.stringify(data))
    if (error) throw error;

    // FORMAT THE DATA //
    var nested = d3.nest()
        .key(d => d.key)
        .map(data)
    console.log("nest", nested.keys()) // <-- Names of drugs, used for color array
    console.log(nested) // <-- the actual data

    color.domain(nested);

    // Y-SCALE //
    var yScale = d3.scaleLinear()
        .domain([0 , d3.max(data, d => +d.deaths) ])
        .range([height, 0]);

    // X-SCALE // 
    var xScale = d3.scaleTime()
        .domain(d3.extent(data, d => d.year))
        .range([0, width])

    var line = d3.line()
        .x(d => xScale(d.year))
        .y(d => yScale(d.deaths))
        .curve(d3.curveCardinal);


    var series = chartGroup.selectAll(".series")
        .data(nested.entries())
        .enter().append("g")
            .attr("class","series")
        .append("path")
        .attr("d", d => {console.log(d); return line(d.value)})
        .style("stroke", function (d) { return color(d.key); })
        .style("stroke-width", "4px")
        .style("fill", "none");

    // Axes

    var yAxis = d3.axisLeft(yScale).ticks(5).tickPadding(5);
    var xAxis = d3.axisBottom(xScale).ticks(10);

    chartGroup.append("g")
    .attr("class","Xaxis")
    .call(xAxis)
    .attr("transform","translate(0,"+height+")")
    .selectAll("text")
        .style("text-anchor", "end")
        .attr("dx", "-.8em")
        .attr("dy", ".15em")
        .attr("transform", "rotate(-65)");

    chartGroup.append("g")
        .attr("class","Yaxis")
        .call(yAxis)
        .selectAll("text")
        .style("text-anchor", "middle")
        .attr("dy", "-1em")
        .attr("transform", "rotate(-90)");
});

</script>    
</body>
</html>