ChartJS 在散点图上计算和绘制椭圆,工作正常但正确吗?

gk7wooem  于 2023-03-29  发布在  Chart.js
关注(0)|答案(1)|浏览(195)

我正在使用react-chartjs-2中的散点图开发一个react应用程序。我创建了一些帮助函数来计算并在图表上绘制95%置信区间椭圆。到目前为止,一切顺利。
它大部分工作得很好(至少从视觉的Angular 来看),但有时,我得到一些奇怪的结果-以下面的蓝色项目为例。旋转似乎关闭,因为你可以把所有的蓝色项目放进一个稍微小一点的,但旋转的椭圆中。
我已经把下面的代码,并很高兴提供更多的信息,如果任何人都可以帮助:
1.解释为什么这个结果有意义或
1.帮助我找出如何调整我的方法,以获得更好的结果。
谢谢!

const calculateEllipse = (points, xAxis, yAxis) => {
  const n = points.length;
  const xMean = points.reduce((sum, p) => sum + p.x, 0) / n;
  const yMean = points.reduce((sum, p) => sum + p.y, 0) / n;

  let a = 0;
  let b = 0;
  let c = 0;
  points.forEach((p) => {
    const xPixel = xAxis.getPixelForValue(p.x) - xAxis.getPixelForValue(xMean);
    const yPixel = yAxis.getPixelForValue(p.y) - yAxis.getPixelForValue(yMean);
    a += xPixel * xPixel;
    b += xPixel * yPixel;
    c += yPixel * yPixel;
  });

  a /= n;
  b /= n;
  c /= n;

  const d = Math.sqrt((a - c) * (a - c) + 4 * b * b);
  const e1 = (a + c + d) / 2;
  const e2 = (a + c - d) / 2;

  const angle = (a > c ? Math.atan2(b, a - e1) : Math.atan2(c - e1, b)) / 2;

  const scaleFactor = 2.4477; // Scaling factor for a 95% confidence ellipse

  return {
    x: xAxis.getPixelForValue(xMean),
    y: yAxis.getPixelForValue(yMean),
    a: Math.sqrt(e1) * scaleFactor,
    b: Math.sqrt(e2) * scaleFactor,
    angle,
  };
};

const ellipsePlugin = {
  id: "ellipse",
  afterDatasetsDraw: function (chart, args, options) {
    const ctx = chart.ctx;
    const show = chart.options.plugins.ellipse.show;
    const chartArea = chart.chartArea; // get the chart area

    const xAxis = chart.scales.x;
    const yAxis = chart.scales.y;
    if (show) {
      chart.data.datasets.forEach((dataset, index) => {
        if (chart.isDatasetVisible(index)) {
          const ellipseData = calculateEllipse(dataset.data, xAxis, yAxis);
          ctx.save();

          // check if any part of the ellipse is outside the chart area
          if (
            ellipseData.x - ellipseData.a <= chartArea.right &&
            ellipseData.x + ellipseData.a >= chartArea.left &&
            ellipseData.y - ellipseData.b <= chartArea.bottom &&
            ellipseData.y + ellipseData.b >= chartArea.top
          ) {
            // draw only if the ellipse is completely inside the chart area
            ctx.beginPath();
            ctx.translate(ellipseData.x, ellipseData.y);
            ctx.rotate(ellipseData.angle);
            ctx.scale(ellipseData.a, ellipseData.b);
            ctx.arc(0, 0, 1, 0, 2 * Math.PI);
            ctx.restore();

            ctx.strokeStyle = dataset.borderColor;
            ctx.lineWidth = 2;
            ctx.stroke();
          }
        }
      });
    }
  },
};
m528fe3b

m528fe3b1#

我觉得你的Angular 公式不对,肯定有原因,我没有按照计算,但我验证了standard formula(式(23)):

const angle = Math.atan2(2*b, (a-c))/2;

工作正常-请参见下面的模拟。atan2的使用使得公式对a〉c和a〈c都有效。
x一个一个一个一个x一个一个二个x

相关问题