在d3v4堆叠条形图中使用JSON [英] Using JSON in d3v4 stacked bar chart

查看:95
本文介绍了在d3v4堆叠条形图中使用JSON的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我找到了一个要使用的d3v3 堆积的条形图示例使用json数据.

还有一个d3v4 规范条形图示例,该示例加载并使用了csv. /p>

我想制作一个d3v4堆叠的条形图,但我想使用json数据创建它,而不是从csv加载.我不确定如何升级v3版本或修改v4版本来实现此目的.

这是我的数据结构:

[{
     "hospitalName": "hospital1",
     "category": "Injury & Poisoning",        
     "Females": "0",
     "Males": "4",
     "Unknown": "0",
     "count": "4"
},
{
    "hospitalName": "hospital1",
    "category": "Symptoms, Signs, & Ill-Defined Conditions",
    "Females": "1",
    "Males": "1",
    "Unknown": "0",
    "count": "2"
},
{
    "hospitalName": "hospital2",
    "category": "Mental Disorders",
    "Females": "0",
    "Males": "1",
    "Unknown": "0",
    "count": "1"
}]

在给出两个示例的情况下,如何在d3v4堆叠条形图中使用此数据?

解决方案

在升级适合您的数据源类型的v3示例和修改v4示例以采用json而不是csv数据之间进行选择,即转换现有的规范的v4示例应该会赢.

d3.csv将csv文件转换为json. d3.csv从源csv创建的头文件看起来与您的json一样,其头文件等于您的数据项的属性. 因此,两个示例实质上都使用相同的数据格式和结构.这就是使用d3v4示例更简单的原因.

要使用json数据而不是v4示例中的csv数据,您需要进行两项更改:

  1. 获取正确的列数据:

规范中的列使用var keys = data.columns.slice(1);来获取csv数据中应使用矩形绘制的列. columns是d3.csv添加到数据数组的属性,用于指定列标题.删除的值不是用矩形绘制的,而是标识堆栈,它可以是堆栈标签并用于x轴放置.由于d3.csv添加了columns属性,因此我们需要一种略有不同的方法.

在您的情况下,看起来我们想从数据中获取男性,女性,未知人物,并且每个组的结构如下:

{
     "hospitalName": "hospital1",
     "category": "Injury & Poisoning",        
     "Females": "0",
     "Males": "4",
     "Unknown": "0",
     "count": "4"
}

因此,我们可以对键/属性(将用矩形绘制)进行一些修改:

var columns = d3.keys(data[0]);  // get the properties of the first item in the data array
var keys = columns.slice(2,5); // extract keys with index 2,3,4. These will be the properties that are represented by rectangles in the chart.

  1. 说明多个具有相同名称的组/堆栈

由于大多数示例的比例尺将使用组名称,因此这些将不起作用.取而代之的是,每个组都需要唯一的东西,索引可以正常工作:

x.domain(data.map(function(d,i) { return i; }));

您需要对刻度线进行一些格式化,以免将索引作为标签,让我们说:

d3.axisBottom(x).tickFormat(function(d,i) { return data[i].hospitalName })

使用该类别将价格添加到对勾中应该足够容易了.

  1. 修改总属性

是的,我说了两个步骤,这太短了,不足以保证完整的项目符号,但是列出三个项目会更好.原始规范使用d.total,您的数据使用d.count,这用于确定y标度的域.

一起:

 <!DOCTYPE html>
<style>

.axis .domain {
  display: none;
}

</style>
<svg width="600" height="200"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>

var svg = d3.select("svg"),
    margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom,
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var x = d3.scaleBand()
    .rangeRound([0, width])
    .paddingInner(0.05)
    .align(0.1);

var y = d3.scaleLinear()
    .rangeRound([height, 0]);

var z = d3.scaleOrdinal()
    .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);

  var data = [{
     "hospitalName": "hospital1",
     "category": "Injury & Poisoning",        
     "Females": "0",
     "Males": "4",
     "Unknown": "0",
     "count": "4"
},
{
    "hospitalName": "hospital1",
    "category": "Symptoms, Signs, & Ill-Defined Conditions",
    "Females": "1",
    "Males": "1",
    "Unknown": "0",
    "count": "2"
},
{
    "hospitalName": "hospital2",
    "category": "Mental Disorders",
    "Females": "0",
    "Males": "1",
    "Unknown": "0",
    "count": "1"
}]
	
	var columns = d3.keys(data[0]);

  var keys = columns.slice(2,5);

  data.sort(function(a, b) { return b.total - a.total; });
  x.domain(data.map(function(d,i) { return i; }));
  y.domain([0, d3.max(data, function(d) { return d.count; })]).nice();
  z.domain(keys);

  g.append("g")
    .selectAll("g")
    .data(d3.stack().keys(keys)(data))
    .enter().append("g")
      .attr("fill", function(d) { return z(d.key); })
    .selectAll("rect")
    .data(function(d) { return d; })
    .enter().append("rect")
      .attr("x", function(d,i) { return x(i); })
      .attr("y", function(d) { return y(d[1]); })
      .attr("height", function(d) { return y(d[0]) - y(d[1]); })
      .attr("width", x.bandwidth());

  g.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x).tickFormat(function(d,i) { return data[i].hospitalName}));

  g.append("g")
      .attr("class", "axis")
      .call(d3.axisLeft(y).ticks(null, "s"))
    .append("text")
      .attr("x", 2)
      .attr("y", y(y.ticks().pop()) + 0.5)
      .attr("dy", "0.32em")
      .attr("fill", "#000")
      .attr("font-weight", "bold")
      .attr("text-anchor", "start")
      .text("Population");

  var legend = g.append("g")
      .attr("font-family", "sans-serif")
      .attr("font-size", 10)
      .attr("text-anchor", "end")
    .selectAll("g")
    .data(keys.slice().reverse())
    .enter().append("g")
      .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

  legend.append("rect")
      .attr("x", width - 19)
      .attr("width", 19)
      .attr("height", 19)
      .attr("fill", z);

  legend.append("text")
      .attr("x", width - 24)
      .attr("y", 9.5)
      .attr("dy", "0.32em")
      .text(function(d) { return d; });


</script> 

如果要使用d3.json,则可以使用:

d3.json("json.json", function(error,data) {
  if(error) throw error;

  // Parts that use the data here.

})

I have found a d3v3 stacked bar chart example that I would like to use because it uses json data.

There is also a d3v4 canonical bar chart example that loads and uses a csv.

I want to make a d3v4 stacked bar chart but I want to use json data to create it rather than load from a csv. I am not sure how to upgrade the v3 version or to modify the v4 version to accomplish this.

This is my data structure:

[{
     "hospitalName": "hospital1",
     "category": "Injury & Poisoning",        
     "Females": "0",
     "Males": "4",
     "Unknown": "0",
     "count": "4"
},
{
    "hospitalName": "hospital1",
    "category": "Symptoms, Signs, & Ill-Defined Conditions",
    "Females": "1",
    "Males": "1",
    "Unknown": "0",
    "count": "2"
},
{
    "hospitalName": "hospital2",
    "category": "Mental Disorders",
    "Females": "0",
    "Males": "1",
    "Unknown": "0",
    "count": "1"
}]

How do I use this data within a d3v4 stacked bar chart given the two examples?

解决方案

Given the choice between upgrading a v3 example that suits your data source type and modifying the v4 example to take json rather than csv data, the choice to convert the existing canonical v4 example should win.

d3.csv converts csv files to json. d3.csv creates json that looks just like your json from a source csv that has headers equal to your data items' properties. So both examples essentially use the same data format and structure. This is why using the d3v4 example is more straightforward.

To use your json data rather than the csv data in the v4 example you'll need to make two changes:

  1. Getting the right column data:

The columns from the canonical uses var keys = data.columns.slice(1); to get which columns in the csv data should be plotted with rectangles. columns is an attribute added to the data array, by d3.csv, that specifies the column headers. The removed value isn't plotted with rectangles, but idenifies the stack, it could be a stack label and used for x axis placement. As the columns property is added by d3.csv we need a slightly different approach.

In your case it looks like we want to get males, females, unknown from the data and your structure for each group looks like:

{
     "hospitalName": "hospital1",
     "category": "Injury & Poisoning",        
     "Females": "0",
     "Males": "4",
     "Unknown": "0",
     "count": "4"
}

So we can get the keys/properties (that will be plotted with rectangles) with a slight modification:

var columns = d3.keys(data[0]);  // get the properties of the first item in the data array
var keys = columns.slice(2,5); // extract keys with index 2,3,4. These will be the properties that are represented by rectangles in the chart.

  1. Accounting for multiple groups/stacks with the same name

As the scale for most examples will use the group name, these won't work. Instead we need something unique for each group, an index can work just fine:

x.domain(data.map(function(d,i) { return i; }));

You'll need to format the ticks a bit so you don't get the index as the label, lets say:

d3.axisBottom(x).tickFormat(function(d,i) { return data[i].hospitalName })

It should be easy enough to add the category to the tick with that.

  1. Modify the total attribute

Yes I said two steps, this is too short to warrant a whole bullet, but lists are better with three items. The original canonical uses d.total, your data uses d.count, this is used to determine the y scale's domain.

Altogether:

<!DOCTYPE html>
<style>

.axis .domain {
  display: none;
}

</style>
<svg width="600" height="200"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>

var svg = d3.select("svg"),
    margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom,
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var x = d3.scaleBand()
    .rangeRound([0, width])
    .paddingInner(0.05)
    .align(0.1);

var y = d3.scaleLinear()
    .rangeRound([height, 0]);

var z = d3.scaleOrdinal()
    .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);

  var data = [{
     "hospitalName": "hospital1",
     "category": "Injury & Poisoning",        
     "Females": "0",
     "Males": "4",
     "Unknown": "0",
     "count": "4"
},
{
    "hospitalName": "hospital1",
    "category": "Symptoms, Signs, & Ill-Defined Conditions",
    "Females": "1",
    "Males": "1",
    "Unknown": "0",
    "count": "2"
},
{
    "hospitalName": "hospital2",
    "category": "Mental Disorders",
    "Females": "0",
    "Males": "1",
    "Unknown": "0",
    "count": "1"
}]
	
	var columns = d3.keys(data[0]);

  var keys = columns.slice(2,5);

  data.sort(function(a, b) { return b.total - a.total; });
  x.domain(data.map(function(d,i) { return i; }));
  y.domain([0, d3.max(data, function(d) { return d.count; })]).nice();
  z.domain(keys);

  g.append("g")
    .selectAll("g")
    .data(d3.stack().keys(keys)(data))
    .enter().append("g")
      .attr("fill", function(d) { return z(d.key); })
    .selectAll("rect")
    .data(function(d) { return d; })
    .enter().append("rect")
      .attr("x", function(d,i) { return x(i); })
      .attr("y", function(d) { return y(d[1]); })
      .attr("height", function(d) { return y(d[0]) - y(d[1]); })
      .attr("width", x.bandwidth());

  g.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x).tickFormat(function(d,i) { return data[i].hospitalName}));

  g.append("g")
      .attr("class", "axis")
      .call(d3.axisLeft(y).ticks(null, "s"))
    .append("text")
      .attr("x", 2)
      .attr("y", y(y.ticks().pop()) + 0.5)
      .attr("dy", "0.32em")
      .attr("fill", "#000")
      .attr("font-weight", "bold")
      .attr("text-anchor", "start")
      .text("Population");

  var legend = g.append("g")
      .attr("font-family", "sans-serif")
      .attr("font-size", 10)
      .attr("text-anchor", "end")
    .selectAll("g")
    .data(keys.slice().reverse())
    .enter().append("g")
      .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

  legend.append("rect")
      .attr("x", width - 19)
      .attr("width", 19)
      .attr("height", 19)
      .attr("fill", z);

  legend.append("text")
      .attr("x", width - 24)
      .attr("y", 9.5)
      .attr("dy", "0.32em")
      .text(function(d) { return d; });


</script>

If you want to use d3.json, then you can use:

d3.json("json.json", function(error,data) {
  if(error) throw error;

  // Parts that use the data here.

})

这篇关于在d3v4堆叠条形图中使用JSON的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆