hhw h
hhw h

Reputation: 39

Draw multiple line chart using d3.entries key value pair

I want to draw a multi-line chart using d3.js. I print my data structure in console and it look like this:

0:
  key: student1
  value: Array(50)
       0: {date : 2017-09-11 11:51,score:50}
       1: {date : 2017-09-11 12:53,score:90}
       ...

0:
  key: student2
  value: Array(50)
       0: {date : 2017-09-11 11:51,score:20}
       1: {date : 2017-09-11 12:53,score:30}
       ...

And put all student in same chart represented by different line. X axis is date, Y axis is student score.

But seems I have problem define my X and Y domain, I can't get the date and score value by d.value.date.

Upvotes: 1

Views: 864

Answers (1)

anya
anya

Reputation: 667

You can't get date and value score by saying d.value.date because those values are in nested in another object. You could flatten these object to try to get the domains. Going off of this example, if you represent you structure like this:

data = [
    {student: 'student1', date : '2017-09-11 11:51', score: 50},
    {student: 'student1', date : '2017-09-11 12:53', score: 90},
    ...
    {student: 'student2', date : '2017-09-11 11:51', score: 20},
    {student: 'student2', date : '2017-09-11 12:53', score: 30},
    ...
]

You could try define the axis like this:

var parseDate = d3.timeParse("%Y-%m-%d %H:%M");
var x = d3.scaleTime().range([0, width]);  
var y = d3.scaleLinear().range([height, 0]);

data.forEach(function(d) {
    d.date = parseDate(d.date);
    d.score = +d.score;
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.score; })]);

I've modified d3noob's example a bit to go with this data (added a few more datapoints):

// Set the dimensions of the canvas / graph
var margin = {
    top: 30,
    right: 20,
    bottom: 70,
    left: 50
  },
  width = 600 - margin.left - margin.right,
  height = 300 - margin.top - margin.bottom;

// Parse the date / time
var parseDate = d3.timeParse("%Y-%m-%d %H:%M");

// Set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);

// Define the line
var priceline = d3.line()
  .x(function(d) {
    return x(d.date);
  })
  .y(function(d) {
    return y(d.score);
  });

// Adds the svg canvas
var svg = d3.select("body")
  .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 + ")");

// Get the data
var data = [
  {
    student: 'student1',
    date: '2017-09-11 11:45',
    score: 60
  },
  {
    student: 'student1',
    date: '2017-09-11 11:51',
    score: 50
  },
  {
    student: 'student1',
    date: '2017-09-11 12:53',
    score: 90
  },
  {
    student: 'student1',
    date: '2017-09-11 12:57',
    score: 97
  },
  {
    student: 'student2',
    date: '2017-09-11 11:22',
    score: 10
  },
  {
    student: 'student2',
    date: '2017-09-11 11:31',
    score: 15
  },
  {
    student: 'student2',
    date: '2017-09-11 11:33',
    score: 20
  },
  {
    student: 'student2',
    date: '2017-09-11 11:38',
    score: 30
  },
  {
    student: 'student2',
    date: '2017-09-11 12:51',
    score: 45
  },
  {
    student: 'student2',
    date: '2017-09-11 12:59',
    score: 40
  }
]

data.forEach(function(d) {
  d.date = parseDate(d.date);
  d.price = +d.price;
});

// Scale the range of the data
x.domain(d3.extent(data, function(d) {
  return d.date;
}));
y.domain([0, d3.max(data, function(d) {
  return d.score;
})]);

// Nest the entries by symbol
var dataNest = d3.nest()
  .key(function(d) {
    return d.student;
  })
  .entries(data);

// set the colour scale
var color = d3.scaleOrdinal(d3.schemeCategory10);

legendSpace = width / dataNest.length; // spacing for the legend

// Loop through each symbol / key
dataNest.forEach(function(d, i) {

  svg.append("path")
    .attr("class", "line")
    .style("stroke", function() { // Add the colours dynamically
      return d.color = color(d.key);
    })
    .attr("id", 'tag' + d.key.replace(/\s+/g, '')) // assign an ID
    .attr("d", priceline(d.values));

  // Add the Legend
  svg.append("text")
    .attr("x", (legendSpace / 2) + i * legendSpace) // space legend
    .attr("y", height + (margin.bottom / 2) + 5)
    .attr("class", "legend") // style the legend
    .style("fill", function() { // Add the colours dynamically
      return d.color = color(d.key);
    })
    .on("click", function() {
      // Determine if current line is visible 
      var active = d.active ? false : true,
        newOpacity = active ? 0 : 1;
      // Hide or show the elements based on the ID
      d3.select("#tag" + d.key.replace(/\s+/g, ''))
        .transition().duration(100)
        .style("opacity", newOpacity);
      // Update whether or not the elements are active
      d.active = active;
    })
    .text(d.key);

});

// Add the X Axis
svg.append("g")
  .attr("class", "axis")
  .attr("transform", "translate(0," + height + ")")
  .call(d3.axisBottom(x));

// Add the Y Axis
svg.append("g")
  .attr("class", "axis")
  .call(d3.axisLeft(y));
body { font: 12px Arial;}

path { 
    stroke: steelblue;
    stroke-width: 2;
    fill: none;
}

.axis path,
.axis line {
    fill: none;
    stroke: grey;
    stroke-width: 1;
    shape-rendering: crispEdges;
}

.legend {
    font-size: 16px;
    font-weight: bold;
    text-anchor: middle;
}
<script src="https://d3js.org/d3.v5.min.js"></script>

Hope this helps

Upvotes: 2

Related Questions