1

I am trying to build a line chart using a JSON data. The graph of the total price over time. Using the historical prices provided in the stock.json, need to compute the historical amount per date. I have tried but I am not able to do it for this JSON format in particular.

Below is the json format

{
"historical": {
    "KALE": {
        "_id": "KALE",
        "point": [
            { "date": "2015-06-24T00:00:00.000Z", "price": 1043.55 },
            { "date": "2015-06-25T00:00:00.000Z", "price": 1014.75 },
            { "date": "2015-06-26T00:00:00.000Z", "price": 1019.85 },
            { "date": "2015-06-29T00:00:00.000Z", "price": 999.05 },
            { "date": "2015-06-30T00:00:00.000Z", "price": 999.5 }
                 ]
    },

    "BRGR": {
        "_id": "BRGR",
        "point": [
            { "date": "2015-06-24T00:00:00.000Z", "price": 193.47 },
            { "date": "2015-06-25T00:00:00.000Z", "price": 194.06 },
            { "date": "2015-06-26T00:00:00.000Z", "price": 195.06 },
            { "date": "2015-06-29T00:00:00.000Z", "price": 192.92 },
            { "date": "2015-06-30T00:00:00.000Z", "price": 194.76 }
                 ]
    }
  }
}

Here is the plnkr for d3 code

halfer
  • 19,824
  • 17
  • 99
  • 186
jackD
  • 801
  • 2
  • 8
  • 26

1 Answers1

2

You are correct in that you are not parsing the JSON correctly. You can read this answer on parsing JSON data to get a better understanding. And then work to get an understanding of how it works within D3 functions. Often console.log is your friend to see where in the nesting you are and adjust from there.

Multiple lines on chart
Take a look at this block builder

The key was mapping the data to a more accessible format for D3

 var stocks = Object.keys(data).map(function(d){ ///map data to better fit our needs
    return {
      name: d,
      values: data[d].point.map(function(d){
        return {
          date: timeFormat(d.date), 
          price: d.price
        };
      })
    };
 });

The next task is to bind the elements being drawn to the data:

var stock = chart.selectAll(".stocks") //This creates a <g> tag as I put circles on the lines. You don't need this if you just want the line
  .data(stocks)
  .enter().append("g")
  .attr("class", "stocks");

var paths = stock.selectAll(".line") //Bind the paths to the data we mapped earlier
  .data(stocks)
  .enter()
  .append("path")
  .attr("class", "line")
  .attr("d", function(d){return line(d.values)});

You can use the newly mapped data to set the .domain() for each axis. In this case I updated the x-axis domain:

x.domain(
  [d3.min(stocks, function(s){ 
      return d3.min(s.values, function(v){ return v.date; }); 
  }), d3.max(stocks, function(s){ 
      return d3.max(s.values, function(v){ return v.date; }); 
  })] 
);

Passing ticker as argument
I updated the pen to provide data as if you were entering the stock ticker rather than charting both on the same chart.

To make it dynamic you can have a user <input> field with a submit button. Then have a .on('click') function that pass that value and runs the d3.json call each time you submit

$('#submit').on('click', function(){
  let stock = $('#stock-input').val().toUpperCase();
  paths(stock);
})

Within the D3 call you can set a var to equal data.historical[stock]; for example:

var data = data.historical[stock] //This will now give access to KALE's or whatever other stock's data that was passed as an argument.  

This way when you call a function that access that stocks data you can begin from that point, such as:

var line = d3.svg.line()
.x(function(d) { return x(timeFormat.parse(d.date)); }) // Notice we didn't have to use data.historical['stock'].date
.y(function(d) { return y(d.price); });

In addition, there's quite a bit missing from your code in the plnkr in order to 'draw' the chart. I'm not sure if you just haven't gotten there yet or are unsure, but that is outside the scope of the question.

Some useful links:


Initial Response
I don't think you're accessing the JSON properties correctly. For example, you have d3.values(historical), but it should be d3.values(data.historical).

Also, d.KALE and d.BRGR wouldn't return anything, you would need d.historical.point.KALE and d.historical.point.BRGR to access the date and price information for each item d.historical.point.BRGR.price.

you can do some 'pre-processing' and map the items and store them so you don't have to write d.historical.point.KALE... each time. Just some initial thoughts. I'll try and take a deeper look this weekend.

Joe B.
  • 1,124
  • 7
  • 14
  • Sure thanks mate. Actually I will have to make it dynamic not just specific to one or 2 stocks as there could be many. – jackD Jul 15 '17 at 07:13
  • Are you thinking of plotting all of the stocks within the dataset at one time, or are you looking to pass the ticker as an argument and pull the date for that ticker? – Joe B. Jul 15 '17 at 15:18
  • Hey thanks mate for your time.This is great. Yes I am looking to plot all the stocks at one time. – jackD Jul 16 '17 at 10:35