d3:将最小/最大/平均线添加到现有线图

时间:2016-03-31 15:09:15

标签: javascript d3.js

我有一个d3折线图,其中有一串显示每个网站随时间变化的趋势。我想添加另外3行,显示每个日期的所有网站的最小值,最大值和平均值。我尝试按日期嵌套数据并使用d3.mean / max / min创建一个新的数据数组,如下所示:

var minMaxMean = d3.nest()
    .key(function(d) { return d.starttime })
    .rollup(function(d) {
      return {
        mean: d3.mean(d, function(g) { return g.roll_pm25; }),
        max: d3.max(d, function(g) { return g.roll_pm25; }),
        min: d3.min(d, function(g) { return g.roll_pm25; })
      };
    })
    .entries(data);

但现在我被卡住了。如何将此数据添加到现有x轴和y轴的现有折线图中?嵌套数据是正确的方法吗? Here代码。我遇到的问题是在第118-139行。

1 个答案:

答案 0 :(得分:0)

很酷的问题;这令人惊讶地难以回答。你的巢和汇总你在正确的轨道上,但有几件事。首先,巢似乎强迫你追溯到一个字符串。虽然这对于嵌套很好,但是当它调用xScale 时会发生错误的事情。其次,嵌套似乎丢失了数据的排序。第三,你想要三条线但是试图一次性生成所有线。我就是这样做的:

var minMaxMean = d3.nest()
    .key(function(d) { return d.starttime; })
    .rollup(function(d) {
      return {
        starttime: d[0].starttime,
        mean: d3.mean(d, function(g) { return g.roll_pm25; }),
        max: d3.max(d, function(g) { return g.roll_pm25; }),
        min: d3.min(d, function(g) { return g.roll_pm25; })
      };
    })
    .entries(data)
    // all we want are the values now
    .map(function(d){
        return d.values;
    });

// have to re-sort
minMaxMean.sort(function(a,b){
    return a.starttime - b.starttime;
}) 

// 3 lines for mean, min, and max 
svg.append("path")
  .attr("d", 
    d3.svg.line()
        .x(function(d) { return xScale(d.starttime); })
      .y(function(d) { return yScale(d.mean); })(minMaxMean)
  )
  .style("stroke","green")
  .style("fill", "none");

svg.append("path")
  .attr("d", 
    d3.svg.line()
        .x(function(d) { return xScale(d.starttime); })
      .y(function(d) { return yScale(d.max); })(minMaxMean)
  )
  .style("stroke","red")
  .style("fill", "none");

svg.append("path")
  .attr("d",
    d3.svg.line()
        .x(function(d) { return xScale(d.starttime); })
      .y(function(d) { return yScale(d.min); })(minMaxMean)
  )
  .style("stroke","blue")
  .style("fill", "none");

更新了fiddle

var margin = {
  top: 20,
  right: 20,
  bottom: 20,
  left: 50
};

//var width = 900 - margin.left - margin.right,
//  height = 500 - margin.top - margin.bottom;


var graphic = d3.select("#chart");

var width = graphic.node().clientWidth - margin.left - margin.right,
  height = 300 - margin.top - margin.bottom;

xScale = d3.time.scale()
  .range([0, width]);

var yScale = d3.scale.linear()
  .range([height, 0]);
  
var xAxis = d3.svg.axis()
  .scale(xScale)
  .orient("bottom")
  .innerTickSize(-height)
  .tickPadding(10)
  .outerTickSize(3)
  .tickFormat(d3.time.format("%m/%d"));

var yAxis = d3.svg.axis()
  .scale(yScale)
  .orient("left")
  .innerTickSize(-width)
  .tickPadding(10)
  .outerTickSize(3)
  .tickFormat(d3.round);

var svg = d3.select("#chart").append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var parseDate = d3.time.format("%m/%d/%Y %H:%M").parse;

d3.csv("https://raw.githubusercontent.com/Kaz-A/pm25/master/data/RealTime_02112016.csv", ready);

function ready(error, data) {
  if (error) return console.warn(error);

  data.forEach(function(d) {
    d.roll_pm25 = +d.Roll_PM25;
    d.siteID = +d.SiteID;
    d.starttime = parseDate(d.StartTime);
  });

  xScale.domain(d3.extent(data, function(d) {
    return d.starttime;
  }));
  yScale.domain(d3.extent(data, function(d) {
    return d.roll_pm25;
  }));


  var nestedBySite = d3.nest()
    .key(function(d) {
      return d.SiteID;
    })
    .sortKeys(d3.ascending)
    .entries(data);  

  var lineGroup = svg.selectAll(".g-group")
    .data(nestedBySite)
    .enter()
    .append("g")
    .attr("class", "g-group");

  var line = d3.svg.line()
    .x(function(d) {
      return xScale(d.starttime);
    })
    .y(function(d) {
      return yScale(d.roll_pm25);
    });


  lineGroup.append("path")
    .attr("class", "g-line")
    .attr("d", function(d) {
      return line(d.values);
    });

  //add x and y axis
  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

  svg.append("g")
    .attr("class", "y axis")
    .call(yAxis);
 
 //add the mean, max and min lines to the linechart
	//nest data by starttime and add mean, max and min values to the array   
  var minMaxMean = d3.nest()
    .key(function(d) { return d.starttime; })
    .rollup(function(d) {
      return {
        starttime: d[0].starttime,
        mean: d3.mean(d, function(g) { return g.roll_pm25; }),
        max: d3.max(d, function(g) { return g.roll_pm25; }),
        min: d3.min(d, function(g) { return g.roll_pm25; })
      };
    })
    .entries(data)
    .map(function(d){
    	return d.values;
    });
    
minMaxMean.sort(function(a,b){
	return a.starttime - b.starttime;
}) 
   
svg.append("path")
  .attr("d", 
    d3.svg.line()
	    .x(function(d) { return xScale(d.starttime); })
      .y(function(d) { return yScale(d.mean); })(minMaxMean)
  )
  .style("stroke","green")
  .style("fill", "none");

svg.append("path")
  .attr("d", 
    d3.svg.line()
	    .x(function(d) { return xScale(d.starttime); })
      .y(function(d) { return yScale(d.max); })(minMaxMean)
  )
  .style("stroke","red")
  .style("fill", "none");
  
svg.append("path")
  .attr("d",
    d3.svg.line()
	    .x(function(d) { return xScale(d.starttime); })
      .y(function(d) { return yScale(d.min); })(minMaxMean)
  )
  .style("stroke","blue")
  .style("fill", "none");

  //resize
  d3.select(window).on("resize", resizeHandler);

  function resizeHandler() {
    console.log("resizing!!");
    width = graphic.node().clientWidth - margin.left - margin.right;

    xScale.range([0, width]);
    xAxisGroup.call(xAxis);

    lineGroup.attr("path", function(d) {
      return xScale(d.starttime);
    });

    console.log("updated width", width);
  };

}
body {
  font-family: arial, sans;
  font-size: 11px;
  width: 600px;
  margin: 40px auto;
}

#chart {
  height: 300px;
}

.axis line {
  fill: none;
  stroke: black;
  stroke-dasharray: 2px 3px;
  shape-rendering: crispEdges;
  stroke-width: 1px;
}

.axis path {
  display: none;
}


.g-line {
  stroke: #ccc;
  fill: none;
  stroke-width: 1px;
}

.g-color-line {
  stroke: steelblue;
  fill: none;
  stroke-width: 2px;
}

.g-line.g-secondary-line {
  stroke: #f0f0f0;
}

.tick line {
  opacity: 0.1;
}

.min-max-mean_line {
  stroke: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="chart">
</div>