Highcharts实战讲解|散点图+多项式回归曲线复杂图表示列Demo

功能说明:散点观测样本,同时绘制一阶(线性)、二阶、三阶多项式回归拟合曲线 ;底层依靠 mathjs 矩阵库实现最小二乘多项式回归计算。 原代码依赖mathjs做矩阵转置、求逆、矩阵乘法;下面提供完整可直接运行HTML,附带详细解析。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>散点图‑多项式回归拟合</title>
    <!-- mathjs矩阵运算库 -->
    <script src="https://cdn.jsdelivr.net/npm/mathjs@11.8.0/lib/browser/math.min.js"></script>
    <!-- Highcharts官方CDN -->
    <script src="https://code.highcharts.com/highcharts.js"></script>
    <style>
        #container {
            width: 100%;
            height: 620px;
        }
    </style>
</head>
<body>
<div id="container"></div>
<script>
function generateChart(dataset) {
    Highcharts.chart('container', {
        title: {
            text: '散点图与多项式回归拟合曲线'
        },
        subtitle: {
            text: '最小二乘法:一阶、二阶、三阶多项式对比'
        },
        xAxis: {
            title: {
                text: 'X变量'
            },
            minPadding: 0.1,
            maxPadding: 0.1
        },
        yAxis: {
            title: {
                text: 'Y变量'
            }
        },
        tooltip: {
            shared: false
        },
        series: [{
            type: 'scatter',
            name: '观测样本点',
            data: dataset,
            marker: {
                radius: 4
            }
        }, {
            type: 'spline',
            name: '一阶回归(线性)',
            data: generateLineData(linearRegression(dataset, 1), -3, 5, 10),
            marker: {
                enabled: false
            },
            states: {
                hover: {
                    lineWidth: 0
                }
            },
            enableMouseTracking: false
        }, {
            type: 'spline',
            name: '二阶回归(二次多项式)',
            data: generateLineData(linearRegression(dataset, 2), -3, 5, 10),
            marker: {
                enabled: false
            },
            states: {
                hover: {
                    lineWidth: 0
                }
            },
            enableMouseTracking: false
        }, {
            type: 'spline',
            name: '三阶回归(三次多项式)',
            data: generateLineData(linearRegression(dataset, 3), -3, 5, 10),
            marker: {
                enabled: false
            },
            states: {
                hover: {
                    lineWidth: 0
                }
            },
            enableMouseTracking: false
        }],
        credits: {
            enabled: false
        }
    });
}

const dataset = [
    [4.648, 2.013],
    [4.583, 1.354],
    [-2.548, 1.066],
    [-2.321, -0.733],
    [3.684, 1.013],
    [2.888, -0.539],
    [2.358, 1.496],
    [-0.535, 1.718],
    [1.848, -0.462],
    [1.854, 2.748],
    [1.65, 3.253],
    [-1.733, 2.058],
    [0.445, 2.586],
    [0.148, 1.168],
    [2.784, 1.399],
    [4.959, 4.581],
    [4.595, 3.141],
    [1.353, 2.451],
    [0.559, 2.402],
    [-0.854, 0.831],
    [-2.713, 0.781],
    [-2.78, -1.127],
    [0.719, 0.905],
    [-0.452, 3.767],
    [0.04, 2.959],
    [4.134, 1.68],
    [1.206, 1.339],
    [1.484, 1.781],
    [-1.111, 1.82],
    [-2.809, -0.987],
    [-0.399, 2.752],
    [-1.906, 0.949],
    [1.082, 1.394],
    [4.989, 4.606],
    [2.396, 0.42],
    [-1.545, 1.738],
    [4.149, 2.807],
    [3.374, 1.321],
    [2.875, 0.939],
    [4.253, 3.535],
    [3.103, -0.248],
    [3.318, 2.644],
    [-0.17, 1.078],
    [4.848, 3.636],
    [4.695, 2.203],
    [-1.711, 1.126],
    [3.032, -0.522],
    [2.721, 0.315],
    [0.691, 2.694],
    [1.243, 2.708],
    [0.92, 2.536],
    [4.399, 2.117],
    [1.007, 2.395],
    [3.652, 1.265],
    [-0.169, 2.138],
    [4.063, 1.791],
    [4.198, 1.705],
    [0.688, 3.712],
    [1.542, 1.832],
    [4.363, 1.436],
    [2.79, 0.954],
    [0.893, 1.342],
    [-1.226, 3.519],
    [-0.403, 2.466],
    [2.597, -0.78],
    [-1.671, 0.765],
    [4.264, 2.736],
    [-0.855, 3.988],
    [4.291, 2.888],
    [-0.523, 2.865],
    [4.659, 3.201],
    [2.65, 2.046],
    [1.034, 0.55],
    [1.142, 1.522],
    [2.211, 1.456],
    [1.704, 2.286],
    [-0.505, 3.947],
    [-1.337, 1.281],
    [1.095, 1.113],
    [4.473, 1.199],
    [1.986, 2.308],
    [-2.397, 1.838],
    [3.563, 1.649],
    [2.808, 1.676],
    [4.261, 0.631],
    [-1.469, 2.266],
    [2.958, 0.901],
    [-2.53, 0.325],
    [2.223, 1.89],
    [-0.815, 2.656],
    [-1.187, 2.236],
    [4.004, 1.712],
    [-2.15, -0.832],
    [1.179, 2.359],
    [3.832, 2.834],
    [-1.041, 3.408],
    [-1.316, 1.606],
    [4.045, 1.696],
    [0.383, 3.496],
    [2.736, 0.766]
];

/**
 * @param dataset 二维样本数组 [[x,y]]
 * @param nDegrees 多项式阶数 1=线性,2=二次,3=三次
 * @returns 回归系数数组 [a0,a1,a2...] y = a0 + a1*x +a2*x² ...
 */
function linearRegression(dataset, nDegrees) {
    const x = dataset.map(val => val[0]);
    const y = dataset.map(val => val[1]);
    const X = [];
    // 构造范德蒙德矩阵
    for (let i = 0; i <= nDegrees; i++) {
        X.push(x.map(num => num ** i));
    }
    // 最小二乘矩阵求解:b = (X·Xᵀ)⁻¹ · X · y
    const Xt = math.transpose(X);
    const XtX = math.multiply(X, Xt);
    const invXtX = math.inv(XtX);
    const Xty = math.multiply(y, Xt);
    const b = math.multiply(Xty, invXtX);
    return b;
}

/**
 * 根据回归系数生成拟合曲线采样点
 * @param constants 回归系数
 * @param start x起始
 * @param end x结束
 * @param resolution 采样密度
 * @returns [[x,y]]
 */
function generateLineData(constants, start, end, resolution) {
    const outData = [];
    for (let x = start; x <= end; x += 1 / resolution) {
        let y = 0;
        for (let j = 0; j < constants.length; j++) {
            y += constants[j] * (x ** j);
        }
        outData.push([x, y]);
    }
    return outData;
}

generateChart(dataset);
</script>
</body>
</html>

代码模块讲解

1、图表层 generateChart()

  • scatter:原始观测散点样本;
  • 3组 spline:分别渲染一/二/三阶多项式拟合曲线;
  • enableMouseTracking:false:关闭曲线鼠标交互,避免干扰散点tooltip;
  • states.hover.lineWidth:0:鼠标悬浮不改变曲线粗细。

2、linearRegression() 多项式回归函数

采用矩阵最小二乘法求解多项式系数:

  1. 拆分样本x、y;
  2. 构建范德蒙德矩阵 X;
  3. 公式: B=(XXT)−1XyB=(XX^T)^{-1}Xy B=(XXT)−1Xy,使用mathjs完成矩阵转置、相乘、求逆;
  4. 返回多项式系数数组 a0,a1,a2...an a_0,a_1,a_2...a_n a0,a1,a2...an,对应 y=a0+a1x+a2x2+... y=a_0+a_1x+a_2x^2+... y=a0+a1x+a2x2+...。

⚠️依赖说明:该算法强依赖 mathjs 做矩阵求逆;不能移除,否则矩阵运算报错。

3、generateLineData() 拟合曲线采样函数

根据得到多项式系数,在区间[start ~ end]按分辨率采样,生成Highcharts可以渲染的[[x,y]]曲线数组。

4、数据集 dataset

二维数组格式 [[x,y], [x,y]],每一组代表一个观测样本。

业务与可视化要点

  1. 过拟合现象:阶数越高,曲线对样本贴合越强,但容易过拟合;三阶曲线波动会大于线性。
  2. 适用场景:相关性分析、实验数据拟合、统计分析大屏。
  3. 扩展方向:
    • 增加R²拟合优度计算展示;
    • 异常点标记(离群点高亮marker);
    • 增加下拉切换多项式阶数;
    • 导出拟合系数。

常见坑

  1. 缺少mathjs库:直接报window.math is undefined
  2. x范围设置不合理:generateLineData(start, end)区间必须覆盖样本x范围;
  3. 矩阵奇异:当样本高度共线,矩阵求逆报错,需要对数据预处理。
相关推荐
Highcharts1 小时前
Highcharts实战及讲解|多图表联动与同步图表示例 Demo
数据可视化
梨想橙汁1 小时前
Git 常用命令大全:提交、查看日志、版本回退、文件撤销
前端·javascript
mONESY1 小时前
对话一长上下文就爆?LangChain.js Memory 三种淘汰策略(截断 / 总结 / 检索)实战
javascript
雪芽蓝域zzs2 小时前
第三十二节:部门组织管理(el‑tree 组织树
前端·javascript·vue.js
leoZ2312 小时前
第 7 篇:进阶——校验、联动、列表页
开发语言·前端·javascript·vue.js·人工智能·目标检测·ecmascript
VeryCool2 小时前
别吹了,依赖图像识别的GPT‑6 Astra永远快不起来
前端·javascript·aigc
AC赳赳老秦3 小时前
农产品公开数据应用:OpenClaw 抓取农产品价格、产销公开数据,实现农产品行情动态监测
java·c语言·javascript·python·php·deepseek·openclaw
躺柒3 小时前
读数据可视化25高维多元数据(上)
信息可视化·可视化·数据可视化·高维·多变·多元数据
全栈技术负责人3 小时前
大模型流式输出核心技术:智能 Markdown 渲染引擎方案
前端·javascript·vue.js