D3.js Multiple Line Plot – Part 2

Working version of the plot, download here.

As mentioned before, the following code is presented “as it is” in its current version. Some functions and variables are presented out of order.

The SVG variable is created by a D3 selection. The “plotarea” <div> is selected as the element where the SVG plot will be created. A “g” element is appended to the plot. This will allow the time-series to be grouped together as a single object.

var svg = d3.select("#plotarea").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 + ")")
    .attr("id", "svgMain");

The source data is of course the most important piece in the puzzle. In order to create an SVG plot containing multiple lines which each share the same set of dates, make sure that the data is formatted and aligned correctly. For instance, create a tab-separated file which contains a header entry for each column. And make sure each row contains an entry for each column. Thus, if no data is available for a specific date and column, place a value of 0, ensuring no gaps in the data. However, depending on the structure of the data, this may not work for all applications.

An example header for the tsv file: “date 52 Days 104 Days 153 Days 256 Days 512 Days”, where each “# Days” entry is separated by a tab.

Example:

date    52 Days    104 Days
2015-05-01    52.0    102.0
2015-05-02    54.0    104.0

D3 has a function called “d3.tsv” which easily requests and loads a tab-separated file into the browser memory.

Once this raw data has been loaded, the “d3.tsv” function enters a callback. This is where the data is parsed, turned into SVG objects, and loaded into a DOM element.

Since there are multiple time-series, there will be a separate map for each column. The “indices” variable contains the separate time-series as a key-value pair mapping in memory. Each column header (i.e. 52 Days) is the key for a time-series. The value entry of the “indices” map is itself another map object. This additional map object will have a set of key-value pairs between dates and numerical values. Thus, if there are 5 column headers (excluding the date column) then the “indices” object will have 5 keys. And, if there are 252 date rows, each map associated with each one of the 5 keys will have 252 date keys.

Once the data is stored as a map, the D3 “domain” functions — in combination with the D3 max and min functions — will determine the numerical and time-line ranges of ALL the time-series data.

d3.tsv("data-location_name.tsv", function(error, data) {
    color.domain(d3.keys(data[0]).filter(function(key) {
        return key !== "date";
    }));

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

    indices = color.domain().map(function(name) {
        return {
            name: name.substring(0, name.indexOf(" Days")),
            values: data.map(function(d) {
                return {
                    date: d.date,
                    yvalue: +d[name]
                };
            })
        };
    });
    origData = data;
    x.domain(d3.extent(data, function(d) {
        return d.date;
    }));

    y.domain([
        d3.min(indices, function(c) {
            return d3.min(c.values, function(v) {
                return v.yvalue;
            });
        }),
        d3.max(indices, function(c) {
            return d3.max(c.values, function(v) {
                return v.yvalue;
            });
        })
    ]);