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

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

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

  1. const calculateEllipse = (points, xAxis, yAxis) => {
  2. const n = points.length;
  3. const xMean = points.reduce((sum, p) => sum + p.x, 0) / n;
  4. const yMean = points.reduce((sum, p) => sum + p.y, 0) / n;
  5. let a = 0;
  6. let b = 0;
  7. let c = 0;
  8. points.forEach((p) => {
  9. const xPixel = xAxis.getPixelForValue(p.x) - xAxis.getPixelForValue(xMean);
  10. const yPixel = yAxis.getPixelForValue(p.y) - yAxis.getPixelForValue(yMean);
  11. a += xPixel * xPixel;
  12. b += xPixel * yPixel;
  13. c += yPixel * yPixel;
  14. });
  15. a /= n;
  16. b /= n;
  17. c /= n;
  18. const d = Math.sqrt((a - c) * (a - c) + 4 * b * b);
  19. const e1 = (a + c + d) / 2;
  20. const e2 = (a + c - d) / 2;
  21. const angle = (a > c ? Math.atan2(b, a - e1) : Math.atan2(c - e1, b)) / 2;
  22. const scaleFactor = 2.4477; // Scaling factor for a 95% confidence ellipse
  23. return {
  24. x: xAxis.getPixelForValue(xMean),
  25. y: yAxis.getPixelForValue(yMean),
  26. a: Math.sqrt(e1) * scaleFactor,
  27. b: Math.sqrt(e2) * scaleFactor,
  28. angle,
  29. };
  30. };
  31. const ellipsePlugin = {
  32. id: "ellipse",
  33. afterDatasetsDraw: function (chart, args, options) {
  34. const ctx = chart.ctx;
  35. const show = chart.options.plugins.ellipse.show;
  36. const chartArea = chart.chartArea; // get the chart area
  37. const xAxis = chart.scales.x;
  38. const yAxis = chart.scales.y;
  39. if (show) {
  40. chart.data.datasets.forEach((dataset, index) => {
  41. if (chart.isDatasetVisible(index)) {
  42. const ellipseData = calculateEllipse(dataset.data, xAxis, yAxis);
  43. ctx.save();
  44. // check if any part of the ellipse is outside the chart area
  45. if (
  46. ellipseData.x - ellipseData.a <= chartArea.right &&
  47. ellipseData.x + ellipseData.a >= chartArea.left &&
  48. ellipseData.y - ellipseData.b <= chartArea.bottom &&
  49. ellipseData.y + ellipseData.b >= chartArea.top
  50. ) {
  51. // draw only if the ellipse is completely inside the chart area
  52. ctx.beginPath();
  53. ctx.translate(ellipseData.x, ellipseData.y);
  54. ctx.rotate(ellipseData.angle);
  55. ctx.scale(ellipseData.a, ellipseData.b);
  56. ctx.arc(0, 0, 1, 0, 2 * Math.PI);
  57. ctx.restore();
  58. ctx.strokeStyle = dataset.borderColor;
  59. ctx.lineWidth = 2;
  60. ctx.stroke();
  61. }
  62. }
  63. });
  64. }
  65. },
  66. };
m528fe3b

m528fe3b1#

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

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

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

相关问题