【问题标题】:D3 line graph show positive and negative numbersD3折线图显示正负数
【发布时间】:2019-03-02 02:08:50
【问题描述】:

我正在使用 D3 创建折线图。此图表可在此处jsfiddle 获得。

我正在尝试手动绘制线条来表示某些数据点值。我已经尝试将 cmets 添加到代码中的大部分行中,因此希望您能继续跟进。

我的问题是我似乎无法以一种好的方式绘制负数,如果我这样做了,那么图形数据线就会错位。所以我的问题是:我如何缩放我的图表以便我可以同时显示负数和正数?在这种情况下,图表应该根据我设置的最大值/最小值从2 to -2 开始。

目前。我正在像这样缩放我的图表

  //
  // Setup y scale
  //
  var y = d3.scale.linear()
    .domain([0, max])
    .range([height, 0]);

  //
  // Setup x scale
  //
  var x = d3.time.scale()
    .domain(d3.extent(data, dateFn))
    .range([0, width]);

在我看来,.domain([-2,max]) 就足够了,但这似乎会让事情变得更糟。

另外,我的台词似乎与数据线所说的不符。在 jsfiddle 中,绿线设置为 1。但值为1 的数据行不在绿线上。

所以,我猜这几乎是一个规模问题。

图形在工作时应该是什么样子的视觉(毕加索-esc)表示。

【问题讨论】:

  • 如果你降低高度,你会看到线条被绘制出来。为什么不使用 yScale 来绘制限制线?为什么不将限制线放在对象数组中并使用简单的 forEach 来绘制所有线。您需要查看 10 位小数的 y 刻度吗?
  • 您希望 y 域为 [-2, 2] 还是由数据驱动?
  • @ksav data-driven, 2,-2 恰好是我的最高和最低值。应该澄清,mb。
  • 您是否有公式可以计算出标称值、upperTolerance、lowerTolerance、innerUpperTolerance、innerLowerTolerance 值?
  • @ksav 它们将通过 Kettle-job 进行处理并通过端点发送给我,因此,它们只会作为我的输入出现。即您可以将公差值视为静态值。

标签: d3.js


【解决方案1】:

由于您希望 y 域为 [-2, 2] 而不是由数据驱动,因此您可以从 drawGraph 函数中删除许多设置和辅助函数。

绘制图表后,您可以简单地遍历yLines 数组,并根据您的yScale 在指定val 处为每个指定color 绘制一条线。

更新:已编辑:因为您将从端点获得 nominal, upperTolerance, lowerTolerance, innerUpperTolerance, innerLowerTolerance 的值(并且不需要从客户端的数据计算),只需将这些值输入数据驱动的 yScale绘制彩色线条。

下面我刚刚使用了值1, 1.8, -1.8,但您将收到与您的数据更有意义地关联的值。

// Setup
const yLines = [{
    val: 1,
    color: 'green'
  },
  {
    val: 1.8,
    color: 'yellow'
  },
  {
    val: -1.8,
    color: 'red'
  }
]

const margin = {
  top: 10,
  right: 80,
  bottom: 60,
  left: 20
};

const strokeWidth = 3;
const pointRadius = 4;
const svgWidth = 600;
const svgHeight = 600;
const width = svgWidth - margin.left - margin.right;
const height = svgHeight - margin.top - margin.bottom;
const stroke = '#2990ea'; // blue
const areaFill = 'rgba(41,144,234,0.1)'; // lighter blue
const format = d3.time.format("%b %e %Y");

const valueFn = function(d) {
  return d.value
};
const dateFn = function(d) {
  return format.parse(d.name)
};

// select the div and append svg to it
const graph = d3.select('#chart').append('svg')
  .attr('width', width + margin.left + margin.right)
  .attr('height', height + margin.top + margin.bottom)
  .style('overflow', 'visible');

const transformGroup = graph.append('g')
  .attr('tranform', `translate(${margin.left}, ${margin.right})`)


// Make a group for yLines
const extraLines = transformGroup.append('g')
  .attr('class', 'extra-lines')



// Generate some dummy data
const getData = function() {
  let JSONData = [];
  for (var i = 0; i < 30; i++) {
    JSONData.push({
      "name": moment().add(i, 'days').format('MMM D YYYY'),
      "value": Math.floor(Math.random() * (Math.floor(Math.random() * 20))) - 10
    })
  }
  return JSONData.slice()
}

const drawGraph = function(data) {

  console.log(data)

  // Setup y scale
  const y = d3.scale.linear()
    .domain(d3.extent(data.map((d) => d.value)))
    .range([height, 0]);

  // Setup y axis
  const yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .ticks(10)
    .tickSize(0, 0, 0)

  // append group & call yAxis
  transformGroup.append("g")
    .attr("class", "y axis")
    .attr("transform", "translate(" + margin.left + ",0)")
    .call(yAxis);

  // Draw extra coloured lines from yLines array
  extraLines.selectAll('.extra-line')
    .data(yLines)
    .enter()
    .append('line')
    .attr('class', 'extra-line')
    .attr('x1', margin.left)
    .attr('x2', svgWidth - margin.right)
    .attr('stroke', d => d.color)
    .attr('y1', d => y(+d.val))
    .attr('y2', d => y(+d.val))
    .attr('stroke-width', strokeWidth)
    .attr('opacity', 0.5)


  // Setup x scale
  const x = d3.time.scale()
    .domain(d3.extent(data, dateFn))
    .range([0, width])

  // function for filling area under chart
  const area = d3.svg.area()
    .x(d => x(format.parse(d.name)))
    .y0(height)
    .y1(d => y(d.value))

  // function for drawing line
  const line = d3.svg.line()
    .x(d => x(format.parse(d.name)))
    .y(d => y(d.value))

  const lineStart = d3.svg.line()
    .x(d => x(format.parse(d.name)))
    .y(d => y(0))

  // make the line
  transformGroup.append('path')
    .attr('stroke', stroke)
    .attr('stroke-width', strokeWidth)
    .attr('fill', 'none')
    .attr('transform', `translate(${margin.left}, ${margin.top})`)
    .attr('d', lineStart(data))
    .attr('d', line(data))

  // fill area under the graph
  transformGroup.append("path")
    .datum(data)
    .attr("class", "area")
    .attr('fill', areaFill)
    .attr('transform', `translate(${margin.left}, ${margin.top})`)
    .attr('d', lineStart(data))
    .attr("d", area)
}

drawGraph(getData())
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment.min.js"></script>

<div id="chart" style="margin: 0 auto;"></div>

【讨论】:

  • 这些行仍然未与数据对齐。我尝试将其中一个 yLines 设置为 -1,但 -1 上的数据不在该行上。有什么想法吗?
  • 将图片附加到问题以说明@ksav。
  • nvm,找到了!但不确定如何解决。在我的示例中,我需要将 margin.top 设置为 30。设置 margin = { top:0.. } 可以解决问题,因此某处的计算之一中缺少 margin.top。
  • 你是对的。 margin.top 没有被添加到 yAxis 的变换中。固定。
  • yLines 没有正确对齐,请参见上面的最后一个截图。我修复了它,并批准了这个作为答案。非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2014-08-30
  • 2022-01-22
  • 2021-11-22
  • 2013-01-09
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多