分组折线图,如何以重复图案绘制线

时间:2019-11-11 08:22:48

标签: javascript d3.js

我有一些要可视化为“分组折线图”的数据,其中重复绘制了我的三个增长时间段(离散地,仅连接了3个数据点,没有一条连续线),与分组条形图的方式相同功能。我也兜了好几圈。我的数据结构是这样的:

var data = [
   {'fmc':'fmc1', 'values':[{'growth':12, 'period':'t1'},{'growth':7,'period':'t2'},{'growth':4, 'period':'t3'}]},
   {'fmc':'fmc2', 'values':[{'growth':15, 'period':'t1'},{'growth':8,'period':'t2'},{'growth':4, 'period':'t3'}]},
...
]

下面的完整代码段:

var margins = {top: 20, right: 20, bottom: 30, left: 40},
    width = 960,
    height = 500;

var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;

var x0 = d3.scaleBand()
    .rangeRound([0, width]).padding(.1);

var x1 = d3.scaleBand().padding(.05);

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

var xAxis = d3.axisBottom(x0)
    .tickSize(0);

var yAxis = d3.axisRight(y);

var color = d3.scaleOrdinal()
    .range(["#003366","#366092","#4f81b9","#b8cce4","#f6d18b"]);

var svg = d3.select('body').append("svg")
    .attr("width", totalWidth)
    .attr("height", totalHeight);


var graphGroup = svg.append("g")
    .attr("transform", "translate(" + margins.left + "," + margins.top + ")");

//var jsonData = d3.json('11-insti-data.json');

//jsonData.then(function(data) {

var data = [{'fmc': 'fmc1',
  'values': [{'growth': 19.58, 'period': 't1'},
   {'growth': 4.12, 'period': 't2'},
   {'growth': 6.09, 'period': 't3'}]},
 {'fmc': 'fmc2',
  'values': [{'growth': 36.35, 'period': 't1'},
   {'growth': 20.9, 'period': 't2'},
   {'growth': 9.21, 'period': 't3'}]},
 {'fmc': 'fmc3',
  'values': [{'growth': 30.69, 'period': 't1'},
   {'growth': 14.72, 'period': 't2'},
   {'growth': 5.66, 'period': 't3'}]},
 {'fmc': 'fmc4',
  'values': [{'growth': 100.66, 'period': 't1'},
   {'growth': 58.55, 'period': 't2'},
   {'growth': 32.71, 'period': 't3'}]},
 {'fmc': 'fmc5',
  'values': [{'growth': 27.8, 'period': 't1'},
   {'growth': 6.97, 'period': 't2'},
   {'growth': 12.55, 'period': 't3'}]},
 {'fmc': 'fmc6',
  'values': [{'growth': 32.8, 'period': 't1'},
   {'growth': -0.46, 'period': 't2'},
   {'growth': 11.54, 'period': 't3'}]},
 {'fmc': 'fmc7',
  'values': [{'growth': -3.83, 'period': 't1'},
   {'growth': 11.32, 'period': 't2'},
   {'growth': 7.9, 'period': 't3'}]},
 {'fmc': 'fmc8',
  'values': [{'growth': 2.93, 'period': 't1'},
   {'growth': -13.87, 'period': 't2'},
   {'growth': 2.6, 'period': 't3'}]},
 {'fmc': 'fmc9',
  'values': [{'growth': 85.53, 'period': 't1'},
   {'growth': 42.83, 'period': 't2'},
   {'growth': 71.27, 'period': 't3'}]},
 {'fmc': 'fmc10',
  'values': [{'growth': 44.02, 'period': 't1'},
   {'growth': 36.0, 'period': 't2'},
   {'growth': 9.76, 'period': 't3'}]}];

  var categoriesNames = data.map(function(d) { return d.fmc; });
  var rateNames = data[0].values.map(function(d) { return d.period; });

  x0.domain(categoriesNames);
  x1.domain(rateNames).rangeRound([0, x0.bandwidth()]);
  y.domain([0, 100]);

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

  graphGroup.append("g")
      .attr("class", "y axis")
      .call(yAxis)
  .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .style('font-weight','bold')
      .text("Value");

  var line = d3.line()
                .x(function(d) { return x1(d.period); })
                .y(function(d) { return y(d.growth); })
                .curve(d3.curveCardinal);

  var slice = graphGroup.selectAll(".slice")
      .data(data)
      .enter().append("g")
      .attr("class", "g")
      .attr("transform",function(d) { return "translate(" + x0(d.fmc) + ",0)"; });

  slice.selectAll('path')
        .data(function(d) { return d.values; })
      .enter().append('path')
      .attr("d", line(data.growth))
      .attr("stroke", "#366092")
      .attr("stroke-width", 3)
      .attr("fill", "none");

  slice.selectAll('circle')
      .data(function(d) { return d.values; })
    .enter().append('circle')
    .attr('cx', function(d) {return x1(d.period)})
    .style("fill", function(d) { return color(d.period) })
    .attr("cy", function(d) { return y(d.growth); })
    .attr('r', 3);
<script src="https://d3js.org/d3.v5.min.js"></script>

我在D3v5的第2行出现错误:

  

未捕获的TypeError:无法读取未定义的属性长度

当我将数据硬编码到其中时,这有点困惑。

我猜我的线路访问器功能未达到标准水平:

  slice.selectAll('path')
        .data(function(d) { return d.values; })
      .enter().append('path')
      .attr("d", line(data.growth))
      .attr("stroke", "#366092")
      .attr("stroke-width", 3)
      .attr("fill", "none");

我也尝试了不同的组合,例如:attr("d", line(data.values.growth)),但仍然没有超出错误范围。

问题

到底是什么问题?最佳解决方案是什么?

1 个答案:

答案 0 :(得分:2)

两个小变化:

  1. 将数据括在方括号中(即,使其成为包含另一个数组的数组),否则传递的数据将是内部对象

    slice.selectAll('path')
      .data(function(d) {
        return [d.values];//instead of return d.values
      });
    
  2. 您的路径的d属性(line(data.growth))毫无意义,您只需要将已经绑定的数据传递给行生成器即可:

    .attr("d", line)
    

这是您的代码,其中有两项更改:

var margins = {
    top: 20,
    right: 20,
    bottom: 30,
    left: 40
  },
  width = 960,
  height = 500;

var totalWidth = width + margins.left + margins.right;
var totalHeight = height + margins.top + margins.bottom;

var x0 = d3.scaleBand()
  .rangeRound([0, width]).padding(.1);

var x1 = d3.scaleBand().padding(.05);

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

var xAxis = d3.axisBottom(x0)
  .tickSize(0);

var yAxis = d3.axisRight(y);

var color = d3.scaleOrdinal()
  .range(["#003366", "#366092", "#4f81b9", "#b8cce4", "#f6d18b"]);

var svg = d3.select('body').append("svg")
  .attr("width", totalWidth)
  .attr("height", totalHeight);


var graphGroup = svg.append("g")
  .attr("transform", "translate(" + margins.left + "," + margins.top + ")");

//var jsonData = d3.json('11-insti-data.json');

//jsonData.then(function(data) {

var data = [{
    'fmc': 'fmc1',
    'values': [{
        'growth': 19.58,
        'period': 't1'
      },
      {
        'growth': 4.12,
        'period': 't2'
      },
      {
        'growth': 6.09,
        'period': 't3'
      }
    ]
  },
  {
    'fmc': 'fmc2',
    'values': [{
        'growth': 36.35,
        'period': 't1'
      },
      {
        'growth': 20.9,
        'period': 't2'
      },
      {
        'growth': 9.21,
        'period': 't3'
      }
    ]
  },
  {
    'fmc': 'fmc3',
    'values': [{
        'growth': 30.69,
        'period': 't1'
      },
      {
        'growth': 14.72,
        'period': 't2'
      },
      {
        'growth': 5.66,
        'period': 't3'
      }
    ]
  },
  {
    'fmc': 'fmc4',
    'values': [{
        'growth': 100.66,
        'period': 't1'
      },
      {
        'growth': 58.55,
        'period': 't2'
      },
      {
        'growth': 32.71,
        'period': 't3'
      }
    ]
  },
  {
    'fmc': 'fmc5',
    'values': [{
        'growth': 27.8,
        'period': 't1'
      },
      {
        'growth': 6.97,
        'period': 't2'
      },
      {
        'growth': 12.55,
        'period': 't3'
      }
    ]
  },
  {
    'fmc': 'fmc6',
    'values': [{
        'growth': 32.8,
        'period': 't1'
      },
      {
        'growth': -0.46,
        'period': 't2'
      },
      {
        'growth': 11.54,
        'period': 't3'
      }
    ]
  },
  {
    'fmc': 'fmc7',
    'values': [{
        'growth': -3.83,
        'period': 't1'
      },
      {
        'growth': 11.32,
        'period': 't2'
      },
      {
        'growth': 7.9,
        'period': 't3'
      }
    ]
  },
  {
    'fmc': 'fmc8',
    'values': [{
        'growth': 2.93,
        'period': 't1'
      },
      {
        'growth': -13.87,
        'period': 't2'
      },
      {
        'growth': 2.6,
        'period': 't3'
      }
    ]
  },
  {
    'fmc': 'fmc9',
    'values': [{
        'growth': 85.53,
        'period': 't1'
      },
      {
        'growth': 42.83,
        'period': 't2'
      },
      {
        'growth': 71.27,
        'period': 't3'
      }
    ]
  },
  {
    'fmc': 'fmc10',
    'values': [{
        'growth': 44.02,
        'period': 't1'
      },
      {
        'growth': 36.0,
        'period': 't2'
      },
      {
        'growth': 9.76,
        'period': 't3'
      }
    ]
  }
];

var categoriesNames = data.map(function(d) {
  return d.fmc;
});
var rateNames = data[0].values.map(function(d) {
  return d.period;
});

x0.domain(categoriesNames);
x1.domain(rateNames).rangeRound([0, x0.bandwidth()]);
y.domain([0, 100]);

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

graphGroup.append("g")
  .attr("class", "y axis")
  .call(yAxis)
  .append("text")
  .attr("transform", "rotate(-90)")
  .attr("y", 6)
  .attr("dy", ".71em")
  .style("text-anchor", "end")
  .style('font-weight', 'bold')
  .text("Value");

var line = d3.line()
  .x(function(d) {
    return x1(d.period);
  })
  .y(function(d) {
    return y(d.growth);
  })
  .curve(d3.curveCardinal);

var slice = graphGroup.selectAll(".slice")
  .data(data)
  .enter().append("g")
  .attr("class", "g")
  .attr("transform", function(d) {
    return "translate(" + x0(d.fmc) + ",0)";
  });

slice.selectAll('path')
  .data(function(d) {
    return [d.values];
  })
  .enter()
  .append('path')
  .attr("d", line)
  .attr("stroke", "#366092")
  .attr("stroke-width", 3)
  .attr("fill", "none");

slice.selectAll('circle')
  .data(function(d) {
    return d.values;
  })
  .enter().append('circle')
  .attr('cx', function(d) {
    return x1(d.period)
  })
  .style("fill", function(d) {
    return color(d.period)
  })
  .attr("cy", function(d) {
    return y(d.growth);
  })
  .attr('r', 3);
<script src="https://d3js.org/d3.v5.min.js"></script>