You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular应用中实现带5%/95%切点的累积概率分布图表需求

在Angular中实现带5%/95%切点标记的三角分布图表

嘿,我来帮你一步步搞定这个需求!咱们先把核心逻辑捋清楚,再落地到Angular代码里。

一、核心数学逻辑:计算5%和95%累积概率对应的切点

你要实现的是三角分布的可视化,首先得明确怎么根据a(最小值)、b(最大值)、c(最可能值)算出p=0.05和p=0.95对应的x切点。

先定义三角分布左半段的概率占比:

const leftRatio = (c - a) / (b - a);

当p ≤ leftRatio时(切点落在a到c区间)

通过累积概率公式推导x的计算方式:

p = 2*(x - a)² / [(b - a)*(c - a)]

转化为:

x = a + Math.sqrt(p * (b - a) * (c - a) / 2)

当p > leftRatio时(切点落在c到b区间)

对应的累积概率公式推导:

p = 1 - 2*(b - x)² / [(b - a)*(b - c)]

转化为:

x = b - Math.sqrt((1 - p) * (b - a) * (b - c) / 2)

接下来咱们把这个逻辑封装到Angular组件里,再搭配图表库实现可视化。

二、Angular组件实现(基于ng2-charts + Chart.js)

我默认你已经安装了ng2-charts和chart.js,如果没有的话先执行:

npm install ng2-charts chart.js

1. 组件TS代码(triangular-distribution-chart.component.ts)

import { Component, Input, OnInit } from '@angular/core';
import { ChartConfiguration, ChartOptions } from 'chart.js';

@Component({
  selector: 'app-triangular-distribution-chart',
  templateUrl: './triangular-distribution-chart.component.html',
  styleUrls: ['./triangular-distribution-chart.component.css']
})
export class TriangularDistributionChartComponent implements OnInit {
  // 接收父组件传入的三个参数
  @Input() a!: number;
  @Input() b!: number;
  @Input() c!: number;

  // 计算后得到的5%、95%切点x值
  p5X!: number;
  p95X!: number;

  // 图表基础配置
  public lineChartData: ChartConfiguration<'line'>['data'] = {
    datasets: [
      {
        data: [],
        label: '三角分布PDF',
        borderColor: '#2196F3',
        backgroundColor: 'rgba(33, 150, 243, 0.1)',
        fill: true,
        tension: 0.4
      },
      {
        data: [],
        label: '5% / 95% 切点',
        borderColor: '#F44336',
        pointBackgroundColor: '#F44336',
        pointRadius: 6,
        pointHoverRadius: 8,
        type: 'scatter'
      }
    ]
  };

  public lineChartOptions: ChartOptions<'line'> = {
    responsive: true,
    scales: {
      x: {
        min: this.a,
        max: this.b,
        title: {
          display: true,
          text: 'X值'
        }
      },
      y: {
        title: {
          display: true,
          text: '概率密度'
        }
      }
    },
    plugins: {
      legend: {
        position: 'top'
      }
    }
  };

  ngOnInit(): void {
    this.calculateCutPoints();
    this.generateChartData();
  }

  // 计算两个切点的x值
  private calculateCutPoints(): void {
    const leftRatio = (this.c - this.a) / (this.b - this.a);
    const p5 = 0.05;
    const p95 = 0.95;

    // 计算5%切点
    if (p5 <= leftRatio) {
      this.p5X = this.a + Math.sqrt(p5 * (this.b - this.a) * (this.c - this.a) / 2);
    } else {
      this.p5X = this.b - Math.sqrt((1 - p5) * (this.b - this.a) * (this.b - this.c) / 2);
    }

    // 计算95%切点
    if (p95 <= leftRatio) {
      this.p95X = this.a + Math.sqrt(p95 * (this.b - this.a) * (this.c - this.a) / 2);
    } else {
      this.p95X = this.b - Math.sqrt((1 - p95) * (this.b - this.a) * (this.b - this.c) / 2);
    }

    // 保留两位小数避免精度问题
    this.p5X = Number(this.p5X.toFixed(2));
    this.p95X = Number(this.p95X.toFixed(2));
  }

  // 生成图表的PDF曲线数据点
  private generateChartData(): void {
    const pdfData: {x: number, y: number}[] = [];
    const step = (this.b - this.a) / 100; // 分成100个点保证曲线平滑

    // 左半段(a→c)的PDF数据
    for (let x = this.a; x <= this.c; x += step) {
      const y = 2 * (x - this.a) / [(this.b - this.a) * (this.c - this.a)];
      pdfData.push({x, y});
    }

    // 右半段(c→b)的PDF数据
    for (let x = this.c; x <= this.b; x += step) {
      const y = 2 * (this.b - x) / [(this.b - this.a) * (this.b - this.c)];
      pdfData.push({x, y});
    }

    // 给切点数据集赋值
    const cutPointData = [
      {x: this.p5X, y: this.getPDFValue(this.p5X)},
      {x: this.p95X, y: this.getPDFValue(this.p95X)}
    ];

    this.lineChartData.datasets[0].data = pdfData;
    this.lineChartData.datasets[1].data = cutPointData;
  }

  // 根据x值计算对应的PDF值,用于定位切点的y坐标
  private getPDFValue(x: number): number {
    if (x < this.a || x > this.b) return 0;
    if (x <= this.c) {
      return 2 * (x - this.a) / [(this.b - this.a) * (this.c - this.a)];
    } else {
      return 2 * (this.b - x) / [(this.b - this.a) * (this.b - this.c)];
    }
  }
}

2. 组件模板(triangular-distribution-chart.component.html)

<div class="chart-container">
  <!-- 顶部永久显示的5%/95%切点标记 -->
  <div class="cutpoint-labels">
    <span class="label-p5">5% 切点: {{p5X}}</span>
    <span class="label-p95">95% 切点: {{p95X}}</span>
  </div>

  <!-- 三角分布图表 -->
  <canvas baseChart
          [data]="lineChartData"
          [options]="lineChartOptions"
          [type]="'line'">
  </canvas>
</div>

3. 组件样式(triangular-distribution-chart.component.css)

.chart-container {
  position: relative;
  width: 100%;
  height: 400px;
  margin: 20px 0;
}

.cutpoint-labels {
  position: absolute;
  top: -30px;
  width: 100%;
  display: flex;
  justify-content: space-between;
  padding: 0 20px;
  box-sizing: border-box;
}

.label-p5, .label-p95 {
  font-weight: bold;
  color: #F44336;
}

canvas {
  margin-top: 20px;
}

三、使用组件

在父组件的模板里直接调用,传入你的参数即可:

<app-triangular-distribution-chart [a]="10" [b]="50" [c]="30"></app-triangular-distribution-chart>

四、额外优化建议

  • 可以在组件里添加参数校验(比如ngOnChanges里检查a < c < b),避免非法参数导致计算错误
  • 如果需要更灵活的样式,比如给切点添加 tooltip 提示,可以修改Chart.js的plugins.tooltip配置
  • 若不想用ng2-charts,也可以用原生Canvas或D3.js实现,核心的数学计算逻辑完全通用

内容的提问来源于stack exchange,提问作者igogra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:42:32