Multi Line Ordinal d3 chart

孤人 提交于 2019-12-07 14:54:34

问题


Apologies if this seems like a duplicate D3 question. I've spent 2 days trying to figure out how to do this.

I'm trying to create a multi-line chart with the x-axis as an ordinal scale, and the y axis as a normal linear scale. Everything I've seen involves using time and linear scales combined and I can't seem to convert the examples to make them work how I want.

Here's my sample JSON data:

var data = 
[
    { "Supplier": "Supplier1", "Half": "2013 2H", "Value": 99.86047786 },
    { "Supplier": "Supplier1", "Half": "2013 1H", "Value": 93.86047786 },
    { "Supplier": "Supplier1", "Half": "2012 2H", "Value": 98.86047786 },
    { "Supplier": "Supplier1", "Half": "2012 1H", "Value": 96.86047786 },
    { "Supplier": "Supplier2", "Half": "2013 2H", "Value": 97.86047786 },
    { "Supplier": "Supplier2", "Half": "2013 1H",  "Value": 91.86047786 },
    { "Supplier": "Supplier2", "Half": "2012 2H","Value": 93.86047786 },
    { "Supplier": "Supplier2", "Half": "2012 1H", "Value": 94.86047786 },
    { "Supplier": "Supplier3", "Half": "2013 2H", "Value": 92.86047786 },
    { "Supplier": "Supplier3", "Half": "2013 1H", "Value": 91.86047786 },
    { "Supplier": "Supplier3", "Half": "2012 2H", "Value": 88.86047786 },
    { "Supplier": "Supplier3", "Half": "2012 1H", "Value": 87.86047786 },   
    { "Supplier": "Supplier4", "Half": "2013 2H", "Value": 88.86047786 },
    { "Supplier": "Supplier4", "Half": "2013 1H", "Value": 86.86047786 },
    { "Supplier": "Supplier4", "Half": "2012 2H", "Value": 83.86047786 },
    { "Supplier": "Supplier4", "Half": "2012 1H", "Value": 81.86047786 },   
];

And here's where I've gotten so far in trying to create the chart:

//Width and height
var margin = {top: 20, right: 20, bottom: 30, left: 40};           
var width = 600 - margin.left - margin.right;
var height= 500-margin.top -margin.bottom;
var w = width;
var h = height;

var xScale = d3.scale.ordinal()
    .domain(data.map(function (d) {return d.Half; }))
    .rangeRoundBands([margin.left, width], 0.05);

var xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickSize(height - margin.bottom);

var yScale = d3.scale.linear()
    .domain([0, d3.max(data, function(d) {return d.Value; })])
    .range([h,0]);


//Create SVG element
var svg = d3.select("body")
    .append("svg")
    .attr("width", w)
    .attr("height", h);


svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + 0 + ")")
    .call(xAxis);

data = d3.nest().key(function(d) { return d.Supplier; }).entries(data); 

What I can't figure out is how to create the lines for the 4 different "suppliers" with the "Half" date buckets as the X coordinates and the "Value" as the Y coordinates. Ideally I would have 4 lines on the graph, one for each supplier, each with a different color.

Any help/direction would be greatly appreciated.


回答1:


This should get you started: http://jsfiddle.net/hU6r6/

Setting the domain of the xScale properly

// Find all the unique x-axis values
// Fortunately, alphabetical sorting of the values also yields
// the correct order
var xValues = d3.set(data.map(function (d) { return d.Half; })).values().sort();

// Set up the domain using only the unique values for the xAxis
var xScale = d3.scale.ordinal()
    .domain(xValues)
    // Setting the rangePoints instead of rangeBands
    .rangePoints([0, width], 0.5);

Append DOM elements tied to the data

// This is the same data as you have created
var supplierData = d3.nest().key(function(d) { return d.Supplier; }).entries(data); 

// Create a line object which will set the 'd' attributes for the paths
var line = d3.svg.line()
                  .interpolate("linear")
                  .x(function (d) { return xScale(d.Half); })
                  .y(function (d) { return yScale(d.Value); });

// To choose different colors
var colors = d3.scale.category10();

// The chart container
var gLines = svg.selectAll('g.chart-area').data([ supplierData ]);

gLines.enter()
  .append('g')
  .classed('chart-area', true)
  .attr('transform', 'translate(' + margin.left + ',' + margin.top + ")");

// Our 'paths' which are the lines
var lines = gLines.selectAll('path.supplier').data(function (d) { return d; });

// Our 'paths' which are the lines
lines.enter()
  .append('path')
  .classed('supplier', true)
  // The data is of the form { key: 'SupplierX', values: [ ... ] }
  .attr('d', function (d) { return line(d.values); })
  .attr('fill', 'none')
  .attr('stroke', function (d, i) { return colors(i); })

Next steps

If you followed the tutorial, then you will see that the jsFiddle only implements the enter phase of the data. Perhaps that is enough for your purposes and update and exit phases are not needed. Nevertheless, you should follow the tutorial completely.

Apart from that,this graph is still bare-bone and lacks proper y-axis and labels for the colors, etc.




回答2:


You'll probably want to use an SVG path to represent each line. D3 has a line generator that can help with the line definition.

In it's simplest form it could look something like this with your data:

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

svg.selectAll("path")
    .data(data)
    .enter().append("path")
        .attr("d", function(d) { return line(d.values); });

You'll probably also want to set the fill and stroke styles to get the lines to look good and probably to color them individually using an ordinal color scale with Supplier as a domain.



来源:https://stackoverflow.com/questions/19125559/multi-line-ordinal-d3-chart

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!