React/D3.js - 密度图 - 错误:<path> 属性 d:预期数字,“M10,NaNL12,NaNC14,Na…”

问题描述 投票:0回答:1

我被要求使用 D3.js 在 React 中实现密度图,但我的图在屏幕上没有显示任何内容。我对图表和数据可视化不太熟悉,所以我很挣扎。大多数情况下,我一直在遵循此处显示的示例:https://www.react-graph-gallery.com/densis-plot

在控制台中,我可以看到这个错误

Error: <path> attribute d: Expected number, "M10,NaNL12,NaNC14,Na…".

我正在使用的数据集看起来像这样

[1768, 1809, 3091, 2314, 2845, 3068, 2402, 2514, 2899, 2793, 3297, 2550, 3525, 2863, 3193, 1726, 2356, 2275, 2591, 2496, 1644, 2912, 2801, 2394, 3147, 3373, 2446, 2473, 2350, 1854, 3022, 3274, 3059, 1483, 1844, 2234, 2295, 2053, 2111, 2410, 2256, 2224, 2756, 2733, 2244, 1980, 2558, 3166, 2713, 2177, 2231, 1942]

下面是我的图表组件的正在进行的代码:

import { useEffect, useMemo, useState } from "react";
import * as d3 from "d3";
import * as statsPackage from "simple-statistics";
import importedData from "./data"

import  AxisBottom  from "./axisBottom";
import  AxisLeft  from "./AxisTop";

const MARGIN = { top: 30, right: 30, bottom: 50, left: 50 };

type DensityChartProps = {
  width?: number;
  height?: number;
  filteredDataset: number[];
};

export const DensityChart = ({ width = 700, height = 400, filteredDataset}: DensityChartProps) => {
  const boundsWidth = width - MARGIN.right - MARGIN.left;
  const boundsHeight = height - MARGIN.top - MARGIN.bottom;
  const usedData = filteredDataset?.map(num => Math.round(num) ) ?? importedData;

  const [states, setStates] = useState({
    stDev: statsPackage.standardDeviation(usedData),
    mean: statsPackage.mean(usedData),
    stDevs: 1
  })

  useEffect(() => {
    console.log("states", states);
    console.log("all numbers? ", filteredData.every(num => typeof num === "number"))
  }, [states])
  

  const filteredData = usedData.filter((value) =>
  {
    try {
      return Math.abs(value - states.mean) <= states.stDevs * states.stDev;
    } catch (error) {
      console.error("Error filtering data:", error);
      return false;
    }
  }
) ;

  const xScale = useMemo(() => {
    try {
      return d3.scaleLinear().domain([0, 1000]).range([10, boundsWidth - 10]);
    } catch (error) {
      console.error("Error creating xScale:", error);
      return d3.scaleLinear().domain([0, 1]).range([10, boundsWidth - 10]);
    }
  }, [filteredData, width, states.stDevs]);

  // Compute kernel density estimation
  const density = useMemo(() => {
    const kde = kernelDensityEstimator(kernelEpanechnikov(7), xScale.ticks(40));
    return kde(filteredData);
  }, [xScale]);

  const yScale = useMemo(() => {
    try {
      const max = Math.max(...density.map((d) => d[1]));
      console.log("d3.scaleLinear().range([boundsHeight, 0]).domain([0, max])", d3.scaleLinear().range([boundsHeight, 0]).domain([0, max]))
      return d3.scaleLinear().range([boundsHeight, 0]).domain([0, max]);
    } catch (error) {
      console.error("Error creating yScale:", error);
      return d3.scaleLinear().range([boundsHeight, 0]).domain([0, 1]);
    }
  }, [filteredData, height, states.stDevs]);

  const path = useMemo(() => {
    try {
      const lineGenerator = d3.line().x((d) => {
        console.log("xScale(d[0]", xScale(d[0]));
       return xScale(d[0])
      })
      
      .y((d) => {
      console.log("yScale(d[1]", yScale(d[1]));
        return yScale(d[1])
      })
      
      .curve(d3.curveBasis);
      console.log("lineGenerator(density)", lineGenerator(density));
      return lineGenerator(density);
    } catch (error) {
      console.error("Error creating path:", error);
      return "";
    }
  }, [density]);

  const handleStdDeviationChange = (e) => {
    try {
      setStates({ ...states, stDevs: Number(e.target.value) });
    } catch (error) {
      console.error("Error setting standard deviation:", error);
    }
  };

  return (
    <>
    <svg width={width} height={height}>
      <g
        width={boundsWidth}
        height={boundsHeight}
        transform={`translate(${[MARGIN.left, MARGIN.top].join(",")})`}
      >
        <path
          d={path}
          fill="blue"
          opacity={0.4}
          stroke="black"
          strokeWidth={1}
          strokeLinejoin="round"
        />

        {/* X axis, use an additional translation to appear at the bottom */}
        <g transform={`translate(0, ${boundsHeight})`}>
          <AxisLeft yScale={yScale} pixelsPerTick={40} />
        </g>
        <g transform={`translate(0, ${boundsHeight})`}>
          <AxisBottom xScale={xScale} pixelsPerTick={40} />
        </g>

      </g>
    </svg>
    <p>Standard deviation: {states.stDev}</p>
    <br />
    <p>Mean: {states.mean}</p>
    <br />
    <label>Display Data Within N Standard Deviations: </label>
      <input
        type="number"
        value={states.stDevs}
        onChange={handleStdDeviationChange}
        step="0.1"
      />
    </>
  );
};
export default DensityChart;

// TODO: improve types
// Function to compute density
function kernelDensityEstimator(kernel: (v: number) => number, X: number[]) {
  return function (V: number[]) {
    return X.map((x) => [x, d3.mean(V, (v) => kernel(x - v))]);
  };
}

function kernelEpanechnikov(k: number) {
  return function (v: number) {
    return Math.abs((v /= k)) <= 1 ? (0.75 * (1 - v * v)) / k : 0;
  };
}

但是,如果我使用如下所示的虚拟数据集,它似乎工作正常:

75, 104, 369, 300, 92, 64, 265, 35, 287, 69, 52, 23, 287, 87, 114, 114, 98, 137, 87, 90, 63, 69, 80, 113, 58, 115, 30, 35, 92, 460, 74, 72, 63, 115, 60, 75, 31, 277, 52, 218, 132, 316, 127, 87, 449, 46, 345, 48, 184, 149, 345, 92, 749, 93, 9502, 138, 48, 87, 103, 32, 93, 57, 109, 127, 149, 78, 162, 173, 87, 184, 288, 576, 460, 150, 127, 92, 84, 115, 218, 404, 52, 85, 66, 52, 201, 287, 69, 114, 379, 115, 161, 91, 231, 230, 822, 115, 80, 58, 207, 171, …]

我想我一定犯了一个非常愚蠢的错误,但我会很感激任何帮助。

javascript reactjs d3.js visualization density-plot
1个回答
0
投票

您的

xscale
domain([0, 1000])

导致您的所有 KDE 存储桶 (

xScale.ticks(40)
) 都来自
[0 ... 1000]

您的所有样本数据都在

[2000 ... 3000]
范围内。

不太合适

结果你的 KDE 值都是

0

你的图表是一条水平线。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.