javascriptd3.jslinechartlinegraph

Line not appearing in d3.js linechart (only axes are visible)


I am trying to create a line graph in d3.js but only my axes are appearing; the line doesn't show.

Things that are working:1. My axes are labelled correctly 2. Looking at the elements of the page in Chrome it seems the x and y attributes for the line are 'working' (i.e. the data for coordinates are defined for the line/are not 'NaN' values). I think there must be something wrong with attributes associated with my line (end of the Javascript code).

Are there any reasons this might be happening?

This is what my plot/graph output currently looks like:

Current state of plot

Here is my HTML, Javascript and the data I've used for the plot:

HTML:

<html>
    <head>
        <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    <body>
        <div id="merit-order-chart"></div>
    </body>
    <script type="text/javascript" src="/src.js"></script>

</html>

JAVASCRIPT:

 // create a SVG element
 let svg2 = d3.select("#merit-order-chart").append("svg");

 // sizing parameters
 let margin2 = {top: 20, right: 50, bottom: 40, left: 80};
 let width2 = 800;
 let height2 = 400;
 let chartWidth2 = width2 - margin2.left - margin2.right;
 let chartHeight2 = height2 - margin2.top - margin2.bottom;

 // sizing the SVG
 svg2.attr("width", width2 + "px")
   .attr("height", height2 + "px");

 // creating the x and y scales
 let y2 = d3.scaleLinear()
   .clamp(true)
   .range([chartHeight2, 0]);
 let x2 = d3.scaleTime()
   .clamp(true)
   .range([0, chartWidth2]);

 // formatting of the x and y axes
 let xAxis2 = d3.axisBottom()
   .scale(x2)
   .tickFormat(d3.timeFormat("%Y-%m-%d %H:%M:%S"))
   .ticks(4);
 let yAxis2 = d3.axisLeft()
   .scale(y2)
   .ticks(8);


 // adding a 'group' element for all the things attached to the chart
 let chart2 = svg2.append("g")
   .attr("transform", `translate(${margin2.left},${margin2.top})`);


 // adding the x and y axis elements to the chart group (g)
 const xg2 = chart2.append("g")
   .classed("x axis", true)
   .attr("transform", `translate(0,${chartHeight2})`)
   .call(xAxis2);
 const yg2 = chart2.append("g")
   .classed("y axis", true)
   .call(yAxis2);


 d3.csv("/price-data.csv", (err, csv) => {
   const clean2 = csv.map(d2 => {
     // clean up number formats
     d2.p = parseFloat(d2.p); 
     d2.settlementdate = Date.parse(d2.settlementdate)
     d2.index = parseFloat(d2.index);
     return d2;
   });

 // re-sizing the x and y axes

   x2.domain([d3.min(clean2, d2 => d2.settlementdate), d3.max(clean2, d2 => d2.settlementdate)]);
   xg2.call(xAxis2);  
   y2.domain([-1000, 14125]); 
   yg2.call(yAxis2);


 chart2.selectAll(".prices")
   .data(clean2)
      .enter()
     .append("line")
       .attr("x", d2 => x2(d2.settlementdate))
       .attr("y", d2 => y2(d2.p))
       .attr("stroke-width", 5)
       .attr("stroke", "black")
       //.style("stroke", "rgb(6,120,155)");
  });

DATA (.csv):

settlementdate,p,index
1/1/2017 0:00,50,1
1/1/2017 0:05,35,2
1/1/2017 0:10,100,3
1/1/2017 0:15,5000,4

Solution

  • You need to use a line generator, currently you are passing an array of objects representing each point, and appending a line for each one - this approach won’t work (partly because lines don’t have x and y attributes, but x1,x2,y1,y2 attributes).

    You need to use a line generator:

     let line = d3.line()
       .x(function(d) { return x2(d.settlementdate); }) // x value for each point
       .y(function(d) { return y2(d.p); })              // y value for each point
    

    This will return a path with one vertex for every coordinate fed to it. Consequently you’ll want to append a path rather than a line, and the drawing instructions for a path are contained in the d attribute, so you can use .attr("d", line).

    Lastly, since you want one path per dataset, rather than one path per datapoint, nest your data into an array. By doing so you are getting one line with many points, rather than many lines with no points.

    I changed the scale to show the curve, but it cuts out the peak as a result:

     chart2.selectAll(“.prices”)
       .data([clean2])
          .enter()
          .append(“path”)
          .attr(“d”,line)
          .attr(“stroke-width”, 5)
          .attr(“stroke”, “black”)
          .attr(“fill”,”none”)
    

    var csv = [
      { settlementdate: "1/1/2017 0:00",p:50,index:1 },
      { settlementdate: "1/1/2017 0:05",p:35,index:2 },
      { settlementdate: "1/1/2017 0:10",p:100,index:3 },
      { settlementdate: "1/1/2017 0:15",p:5000,index:4 }
    ]
    
    
    // create a SVG element
     let svg2 = d3.select(“#merit-order-chart”).append(“svg”);
    
     // sizing parameters
     let margin2 = {top: 20, right: 50, bottom: 40, left: 80};
     let width2 = 800;
     let height2 = 400;
     let chartWidth2 = width2 - margin2.left - margin2.right;
     let chartHeight2 = height2 - margin2.top - margin2.bottom;
    
     // sizing the SVG
     svg2.attr(“width”, width2 + “px”)
       .attr(“height”, height2 + “px”);
    
     // creating the x and y scales
     let y2 = d3.scaleLinear()
       .clamp(true)
       .range([chartHeight2, 0]);
     let x2 = d3.scaleTime()
       .clamp(true)
       .range([0, chartWidth2]);
    
     // formatting of the x and y axes
     let xAxis2 = d3.axisBottom()
       .scale(x2)
       .tickFormat(d3.timeFormat(“%Y-%m-%d %H:%M:%S”))
       .ticks(4);
     let yAxis2 = d3.axisLeft()
       .scale(y2)
       .ticks(8);
    
    
     // adding a ‘group’ element for all the things attached to the chart
     let chart2 = svg2.append(“g”)
       .attr(“transform”, `translate(${margin2.left},${margin2.top})`);
    
    
     // adding the x and y axis elements to the chart group (g)
     const xg2 = chart2.append(“g”)
       .classed(“x axis”, true)
       .attr(“transform”, `translate(0,${chartHeight2})`)
       .call(xAxis2);
     const yg2 = chart2.append("g")
       .classed("y axis", true)
       .call(yAxis2);
       
     let line = d3.line()
       .x(function(d) { return x2(d.settlementdate); })
       .y(function(d) { return y2(d.p); })
    
    
    
       const clean2 = csv.map(d2 => {
         // clean up number formats
         d2.p = parseFloat(d2.p); 
         d2.settlementdate = Date.parse(d2.settlementdate)
         d2.index = parseFloat(d2.index);
         return d2;
       });
    
     // re-sizing the x and y axes
    
       x2.domain([d3.min(clean2, d2 => d2.settlementdate), d3.max(clean2, d2 => d2.settlementdate)]);
       xg2.call(xAxis2);  
       y2.domain([0, 200]); 
       yg2.call(yAxis2);
    
    
     chart2.selectAll(“.prices”)
       .data([clean2])
          .enter()
         .append(“path”)
           .attr(“d”,line)
           .attr(“stroke-width”, 5)
           .attr(“stroke”, “black”)
           .attr(“fill”,”none”)
           //.style(“stroke”, “rgb(6,120,155)”);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
     <div id="merit-order-chart"></div>