(function() { const dom = document.getElementById('chart-scatter-band'); const myChart = echarts.init(dom); const scatterData = [[31, 1], [33, 0], [14, 0], [35, 0], [41, 3], [28, 2], [20, 0], [38, 1], [45, 4], [25, 1]]; const xs = scatterData.map(d => d[0]), ys = scatterData.map(d => d[1]); const n = xs.length, xm = xs.reduce((a, b) => a + b, 0) / n, ym = ys.reduce((a, b) => a + b, 0) / n; let num = 0, den = 0; for (let i = 0; i < n; i++) { num += (xs[i] - xm) * (ys[i] - ym); den += (xs[i] - xm) ** 2; } const slope = den ? num / den : 0, intercept = ym - slope * xm; const residuals = scatterData.map(d => d[1] - (slope * d[0] + intercept)); const resStd = Math.sqrt(residuals.reduce((s, r) => s + r * r, 0) / (n - 2)); const xMin = Math.min(...xs), xMax = Math.max(...xs); const step = (xMax - xMin) / 50; const bandHi = [], bandLo = [], regLine = []; for (let x = xMin; x <= xMax; x += step) { const y = slope * x + intercept; regLine.push([x, y]); bandHi.push([x, y + 1.96 * resStd]); bandLo.push([x, y - 1.96 * resStd]); } myChart.setOption({ grid: { left: '12%', right: '8%', top: '10%', bottom: '12%' }, tooltip: { trigger: 'item', formatter: p => p.seriesName + '
X: ' + p.value[0] + '
Y: ' + p.value[1] }, xAxis: { type: 'value', name: 'X 轴' }, yAxis: { type: 'value', name: 'Y 轴' }, series: [ { type: 'line', name: '95%置信上界', data: bandHi, lineStyle: { type: 'dashed', color: 'transparent' }, areaStyle: { color: 'rgba(59,130,246,0.15)' }, symbol: 'none' }, { type: 'line', name: '95%置信下界', data: bandLo, lineStyle: { type: 'dashed', color: 'transparent' }, areaStyle: { color: '#fff' }, symbol: 'none' }, { type: 'line', name: '回归线', data: regLine, lineStyle: { color: '#ef4444', width: 2 }, symbol: 'none' }, { type: 'scatter', name: '数据点', data: scatterData, symbolSize: 14, itemStyle: { color: '#3b82f6' } } ], legend: { data: ['数据点', '回归线', '95%置信带'], bottom: 0 } }); window.addEventListener('resize', () => myChart.resize()); })();