,d3.js"/>

首页 > 解决方案 > D3 Error: attribute d: Expected number, "MNaN,NaNLNaN,NaNL…"

问题描述

I have a feeling that the issue is with the order_date format.

All the logs through the area function work but it doesn't render and when I check the console, I get Error: <path> attribute d: Expected number, "MNaN,NaNLNaN,NaNL…"

If I comment out the .attr('d', area) no errors.

This is also on jsFiddle (https://jsfiddle.net/walkerl/8uxrxm0k/3/)

const rawData = [{
    "agency_id": 541,
    "agency_name": "Store1",
    "order_date": "2018-01-01T00:00:00.000Z",
    "store_name": "Store1",
    "store_id": 1469,
    "orders": 3,
    "avg_order_size": 2,
    "revenue": 500
  },
  {
    "agency_id": 541,
    "agency_name": "Store2",
    "order_date": "2018-01-01T00:00:00.000Z",
    "store_name": "Store2",
    "store_id": 1468,
    "orders": 2,
    "avg_order_size": 1,
    "revenue": 81
  },
  {
    "agency_id": 541,
    "agency_name": "Store3",
    "order_date": "2018-01-01T00:00:00.000Z",
    "store_name": "Store3",
    "store_id": 1404,
    "orders": 14,
    "avg_order_size": 1,
    "revenue": 1348.26
  },
  {
    "agency_id": 541,
    "agency_name": "Store4",
    "order_date": "2018-01-01T00:00:00.000Z",
    "store_name": "Store4",
    "store_id": 1517,
    "orders": 41,
    "avg_order_size": 1,
    "revenue": 8115.6
  },
  {
    "agency_id": 541,
    "agency_name": "Store1",
    "order_date": "2018-01-02T00:00:00.000Z",
    "store_name": "Store1",
    "store_id": 1469,
    "orders": 7,
    "avg_order_size": 1,
    "revenue": 1522
  },
  {
    "agency_id": 541,
    "agency_name": "Store2",
    "order_date": "2018-01-02T00:00:00.000Z",
    "store_name": "Store2",
    "store_id": 1468,
    "orders": 3,
    "avg_order_size": 2,
    "revenue": 297.5
  },
  {
    "agency_id": 541,
    "agency_name": "Store3",
    "order_date": "2018-01-02T00:00:00.000Z",
    "store_name": "Store3",
    "store_id": 1404,
    "orders": 14,
    "avg_order_size": 2,
    "revenue": 1515.76
  },
  {
    "agency_id": 541,
    "agency_name": "Store4",
    "order_date": "2018-01-02T00:00:00.000Z",
    "store_name": "Store4",
    "store_id": 1517,
    "orders": 40,
    "avg_order_size": 1,
    "revenue": 7288.8
  }
];

/**
const cleanData = [
  {
    "order_date": "2017-12-31",
    "Store1": 500,
    "Store2": 81,
    "Store3": 1348.26,
    "Store4": 8115.6
  },
  {
    "order_date": "2018-01-01",
    "Store1": 1522,
    "Store2": 297.5,
    "Store3": 1515.76,
    "Store4": 7288.8
  }
]
*/


const parseTime = d3.timeParse('%Y-%m-%d');
const formatTime = d3.timeFormat('%Y-%m-%dT%H:%M:%S.%LZ');
const p = d3.precisionFixed('.01');
const formatNumber = d3.format(`.${p}f`);
const stores = rawData
  .map(d => ({
    name: d.store_name,
    id: d.store_id
  }))
  .reduce((acc, cur) => acc.find(s => s.id === cur.id) ? acc : [...acc, cur], []);
const storeNames = stores.map(s => s.name)

// This is to group the data as objects with the date and revenue for each store.  Also, the date has to be a js object
const stackData = rawData
  .map(d => ({ ...d,
    order_date: formatTime(new Date(d.order_date)),
    revenue: formatNumber(d.revenue)
  }))
  .reduce((acc, cur) => {
    let existing = acc.find(e => e.order_date === cur.order_date)
    if (existing) {
      existing[cur.store_name] = cur.revenue;
      return acc
    }
    return [...acc, {
      order_date: cur.order_date,
      [cur.store_name]: cur.revenue
    }]
  }, []);

const margin = {
  top: 50,
  right: 30,
  bottom: 70,
  left: 50
};
const width = 960 - margin.right - margin.left;
const height = 600 - margin.top - margin.bottom;

const color = d3.scaleOrdinal(d3.schemePastel1);

let g = d3.select('#stacked-area')
  .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})`)

let x = d3.scaleTime()
  .range([0, width])

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

let xAxisCall = d3.axisBottom();
let yAxisCall = d3.axisLeft();

let xAxisGroup = g.append('g')
  .attr('class', 'x axis')
  .attr('transform', `translate(0, ${height})`)

let yAxisGroup = g.append('g')
  .attr('class', 'y axis')


let stack = d3.stack()
  .keys(stores.map(s => s.name))

stack.order(d3.stackOrderNone);
stack.offset(d3.stackOffsetNone);


var area = d3.area()
  .x(function(d) {
    console.log('Data in X: ', d)
    return x(d.data.order_date);
  })
  .y0(function(d) {
    console.log('Data in Y0: ', d)
    return y(d[0]);
  })
  .y1(function(d) {
    console.log('Data in Y1: ', d)
    return y(d[1]);
  });

// console.log('Area: ', area)
// console.log('StackData: ', stackData)
// console.log('Stack: ', stack(stackData))

// Will need to get max y-value */
const maxY = stackData.map(d => {
  const vals = d3.keys(d).map(k => k !== 'order_date' ? d[k] : 0);
  return d3.sum(vals);
});

// For any missing data, add a 0
const cleanData = stackData.map(d => {
  let existingStores = d3.keys(d);
  storeNames.forEach(n => {
    if (!existingStores.includes(n)) d[n] = 0
  })
  return d
});

// Update Scales
x.domain(d3.extent(rawData.map(d => new Date(d.order_date))));
// x.domain(d3.extent(stackData, function(d) {
//  return d.order_date;
// }));
y.domain([0, maxY]);

// Update axes
xAxisCall.scale(x);
xAxisGroup.call(xAxisCall)
yAxisCall.scale(y);
yAxisGroup.call(yAxisCall);

console.log('Data going to stack: ', cleanData);
const graph = g.selectAll('.store')
  .data(stack(cleanData));

console.log('CleanData: ', JSON.stringify(cleanData))
graph.enter().append('g')
  .attr('class', d => 'store')
  .append('path')
  .attr('class', 'area')
  .attr('d', area)
// .style('fill', d => color(d.key))
// .style('fill-opacity', 0.5)
/* } */
<script src="https://d3js.org/d3.v5.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">


<div class="container">
  <div class="row">
    <div id="left-charts" class="col-sm-12 col-md-8">
      <div id="stacked-area"></div>
    </div>
  </div>
</div>

标签: d3.js

解决方案


这是部分答案,因为我仍然无法显示它,但能够摆脱 NaN。我发现的问题是您从未在许多地方调用 order_date 上的 parseTime。这也是您的小提琴中没有出现 x 轴刻度的原因。也不会出现 y 轴刻度。这让我觉得 y 域也不对。我在 maxY 上做了一个 console.log,但它显示了两个值而不是一个值。 data in maxY: 10044.86,10624.060000000001

我觉得情节没有出现,因为数据没有以适当的格式呈现。这是一个小提琴,我已经能够摆脱 NaN 但获得价值。

这是编辑的小提琴。

https://jsfiddle.net/coolakul/cxsx8e41/5/

var area = d3.area()
  .x(function(d) {
    console.log('Data in X: ', parseTime(d.data.order_date))
    return parseTime(d.data.order_date);
  })
  .y0(function(d) {
    console.log('Data in Y0: ', d[0])
    return d[0];
  })
  .y1(function(d) {
    console.log('Data in Y1: ', d[1])
    return d[1];
  });

很想讨论更多,但需要有关您是否尝试基于示例实现图表以及您正在寻找的最终输出的信息。


推荐阅读