简体   繁体   English

如何创建一个有很多条的charjs条形图?

[英]How to create a charjs bar graph with many bars?

I want to get the effect of something like this:我想得到这样的效果:

在此处输入图片说明

And here is my best attempt as a combo graph:这是我作为组合图的最佳尝试: 在此处输入图片说明

The problem is I need to vastly ramp up the number of bars in this chart.问题是我需要大幅增加此图表中的条形数量。 However, when I try to do this the bars disappear.但是,当我尝试这样做时,条形消失了。

Here is my code as a typescript reactjs setup:这是我的代码作为打字稿 reactjs 设置:

import './App.css';
import React from 'react';
import { Bar, Line } from 'react-chartjs-2';

const createRandomFollowersData = () => {
  const maxDate = new Date();
  const minDate = new Date(maxDate.valueOf() - 5 * 365 * 24 * 60 * 60 * 1000);
  const dataPoints = Array.from({ length: 500 }).map(() => ({
    timestamp: new Date(
      Math.floor(Math.random() * (maxDate.valueOf() - minDate.valueOf())) +
        minDate.valueOf()
    ).toISOString(),
    followers: Math.floor(Math.random() * 1000000) + 0,
  }));
  return dataPoints.sort(
    (a, b) => new Date(a.timestamp).valueOf() - new Date(b.timestamp).valueOf()
  );
};

const createRandomAssetData = () => {
  const maxDate = new Date();
  const minDate = new Date(maxDate.valueOf() - 5 * 365 * 24 * 60 * 60 * 1000);
  const dataPoints = Array.from({ length: 500 }).map(() => ({
    timestamp: new Date(
      Math.floor(Math.random() * (maxDate.valueOf() - minDate.valueOf())) +
        minDate.valueOf()
    ).toISOString(),
    price: Math.floor(Math.random() * 45) + 1,
  }));
  return dataPoints.sort(
    (a, b) => new Date(a.timestamp).valueOf() - new Date(b.timestamp).valueOf()
  );
};

const followersData = createRandomFollowersData();

const yAxisFollowers = {
  type: 'linear',
  id: 'followers',
};

const yAxisDelta = {
  type: 'linear',
  position: 'right',
  id: 'change',
};

const yAxisRank = {
  type: 'linear',
  id: 'rank',
  ticks: {
    reverse: true,
  },
};

const yAxisAssets = {
  type: 'linear',
  position: 'right',
  id: 'assets',
};

const selectChartAxes = (
  containsFollowers: boolean,
  containsRank: boolean,
  showDelta: boolean,
  showAssets: boolean
) => {
  const yAxes = [];
  if (containsFollowers) yAxes.push(yAxisFollowers);
  if (containsRank) yAxes.push(yAxisRank);
  if (showDelta) yAxes.push(yAxisDelta);
  if (showAssets) yAxes.push(yAxisAssets);
  return yAxes;
};

const decimateChart = (
  data: {
    t: Date;
    y: number;
  }[],
  numBuckets: number,
  startDate?: Date,
  endDate?: Date
) => {
  if (!startDate) {
    startDate = data[0].t;
  }
  if (!endDate) {
    endDate = data[data.length - 1].t;
  }

  // create evenly spaced dates
  const dt = endDate.valueOf() - startDate.valueOf();
  const startValue = startDate.valueOf();
  const spacedDates = Array.from({ length: numBuckets + 1 }).map((_, idx) => {
    return new Date(startValue + (idx * dt) / numBuckets);
  });

  // make buckets
  const buckets = Array.from({ length: numBuckets + 2 }).map(() => []) as {
    t: Date;
    y: number;
  }[][];
  const filteredData = data.filter(
    (e) => e.t >= spacedDates[0] && e.t <= spacedDates[spacedDates.length - 1]
  );

  // place data into buckets
  let jdx = 0;
  spacedDates.forEach((e, idx) => {
    for (; jdx < filteredData.length; ) {
      const e = filteredData[jdx];
      const date = new Date(e.t);
      if (date >= spacedDates[idx] && date <= spacedDates[idx + 1]) {
        buckets[idx].push({
          t: date,
          y: e.y,
        });
        ++jdx;
      } else break;
    }
  });

  // one plot per bucket
  return buckets.map((bucket, idx) => {
    const date = spacedDates[idx];
    if (bucket.length === 0) {
      return {
        t: date,
        y: NaN,
      };
    }
    return bucket[bucket.length - 1];
  });
};

const chartMappedFollowersData = followersData.map((followerData) => ({
  t: new Date(followerData.timestamp),
  y: followerData.followers,
}));

// const decimatedData = decimateChart(chartMappedFollowersData, 75);
const decimatedData = decimateChart(chartMappedFollowersData, 75).map(
  (e, idx) => {
    if (idx > 1 && idx < 10) {
      return {
        t: e.t,
        y: NaN,
      };
    }
    if (idx > 30 && idx < 45) {
      return {
        t: e.t,
        y: NaN,
      };
    }
    return e;
  }
);

const decimatedDataToBars = (
  data: {
    t: Date;
    y: number;
  }[]
) => {
  if (data.length < 2) {
    return {
      t: data[0].t,
      y: data[0].y,
    };
  }
  const bars = [];
  const indexedData = data.map((e, idx) => ({
    ...e,
    idx,
  }));

  const filteredIndexedData = indexedData.filter((e) => !isNaN(e.y));
  for (let idx = 0; idx < filteredIndexedData.length - 1; ++idx) {
    const dt = data[idx + 1].t.valueOf() - data[idx].t.valueOf();
    for (
      let idy = 0;
      idy < filteredIndexedData[idx + 1].idx - filteredIndexedData[idx].idx;
      ++idy
    ) {
      const t = new Date(filteredIndexedData[idx].t.valueOf() + idy * dt);
      const deltaY =
        (filteredIndexedData[idx + 1].y - filteredIndexedData[idx].y) /
        (filteredIndexedData[idx + 1].idx - filteredIndexedData[idx].idx);
      bars.push({
        t,
        y: deltaY,
      });
    }
  }
  return bars;
};

const chartOptionsLinear = {
  scales: {
    yAxes: selectChartAxes(true, false, true, true),
    xAxes: [
      {
        type: 'time',
        time: {
          unit: 'day',
          displayFormats: { day: 'MMM DD, Y' },
          min: chartMappedFollowersData[0].t,
          max: chartMappedFollowersData[chartMappedFollowersData.length - 1].t,
        },
        ticks: {
          source: 'labels',
        },
      },
    ],
    maintainAspectRatio: false,
  },
};

const chartData = {
  labels: decimatedData.map((e) => e.t).filter((_, idx) => idx % 3 === 0),
  datasets: [
    {
      yAxisID: 'followers',
      cubicInterpolationMode: 'monotone',
      backgroundColor: 'rgb(54, 162, 235)',
      borderColor: 'rgb(88, 88, 88)',
      fill: false,
      type: 'line',
      label: 'followers',
      spanGaps: true,
      data: decimatedData,
    },
    {
      yAxisID: 'change',
      type: 'bar',
      backgroundColor: 'rgb(235, 54, 162)',
      label: 'delta',
      data: decimatedDataToBars(decimatedData),
      barThickness: 1,
    },
  ],
};

function App(): JSX.Element {
  return (
    <div style={{ margin: '1em' }}>
      <Bar data={chartData} options={chartOptionsLinear} />
    </div>
  );
}

export default App;

If you swap out data: decimatedDataToBars(decimatedData), to data: decimatedDataToBars(chartMappedFollowersData), you can see the effect;如果把data: decimatedDataToBars(decimatedData),换出data: decimatedDataToBars(decimatedData), data: decimatedDataToBars(chartMappedFollowersData),就可以看到效果了; The bars disappear.条形消失。 Does anyone have any insight into this problem and how I can fix it?有没有人对这个问题有任何见解以及我如何解决它?

So the issue was a bug in 2.8.0 that caused the bars to not show.所以问题是 2.8.0 中的一个错误导致条形不显示。 Upgrading to 2.9.4 fixed the issue for me (but broke some other functionality of why I was using 2.8.0 in the first place.)升级到 2.9.4 为我解决了这个问题(但首先破坏了我为什么使用 2.8.0 的其他一些功能。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM