- 数据可视化
【免费下载链接】vega
A visualization grammar.
Vega 内置了一套完整的**统计(statistics)**函数库,用于对概率分布建模、执行回归拟合以及完成分箱、置信区间等统计计算。这些方法既被绑定在顶层vega对象上(在表达式、信号与变换中直接可用),也可以借助独立的 vega-statistics 包以纯 JavaScript 方式单独使用。读完本文,你将掌握随机数生成与控制、正态/对数正态/均匀/整数/混合/KDE 六类分布对象的构造与采样、七种回归模型的拟合与预测,以及带宽估计、直方图分箱、自助置信区间、点图分箱与分位数计算等实用例程,并能结合源码理解其底层实现。
本文以官方文档 docs/docs/api/statistics.md 为主体,辅以 packages/vega-statistics 的真实源码与测试进行印证。
一、模块定位:从vega顶层对象到独立包
在 Vega 的模块化架构中,统计能力集中在packages/vega-statistics子包中,由 packages/vega-statistics/index.js 统一导出。该包的完整导出清单如下(与文档 API 一一对应):
- 随机数:
random、setRandom、randomLCG - 分布方法:
sampleNormal、cumulativeNormal、densityNormal、quantileNormal、sampleLogNormal、cumulativeLogNormal、densityLogNormal、quantileLogNormal、sampleUniform、cumulativeUniform、densityUniform、quantileUniform - 分布对象:
randomNormal、randomLogNormal、randomUniform、randomInteger、randomMixture、randomKDE - 回归:
regressionConstant、regressionLinear、regressionLog、regressionExp、regressionPow、regressionQuad、regressionPoly、regressionLoess、sampleCurve - 统计例程:
bandwidthNRD、bin、bootstrapCI、dotbin、quantiles、quartiles
在完整 Vega 环境中,这些方法绑定到顶层vega对象,这意味着你可以在 Vega 的表达式、signal 或 transform 参数中直接调用vega.sampleNormal()、vega.bin()等。需要独立使用(例如在 Node.js 或浏览器端做纯统计计算)时,则直接安装并导入vega-statistics包即可。
二、随机数生成:默认、替换与可复现种子
所有需要随机数的 Vega 例程(分布采样、bootstrapCI 自助抽样等)都应统一经由下列 API 获取随机数,从而保证可被全局替换与复现。
vega.random()
vega.random()返回 [0, 1) 区间上的均匀伪随机数。默认实现就是 JavaScript 内置的Math.random,这一点在 packages/vega-statistics/src/random.js 中一目了然:
export var random = Math.random; export function setRandom(r) { random = r; }vega.setRandom(randfunc)
vega.setRandom(randfunc)用传入的函数替换全局随机数生成器,之后所有random()调用都会走新函数。典型用途有二:
- 引入替代随机源;
- 注入确定性(seeded)生成器,保证测试与输出稳定可复现。
vega.randomLCG(seed)
vega.randomLCG(seed)返回一个以给定seed初始化的新随机数生成器,其返回的函数无参调用、产生 [0, 1) 区间随机值,实现为经典的线性同余生成器(LCG)。源码见 packages/vega-statistics/src/lcg.js,使用的是 glibc 的经典参数:
export default function(seed) { return function() { seed = (1103515245 * seed + 12345) % 2147483647; return seed / 2147483647; }; }组合使用示例——先构造种子生成器,再注入全局:
// 生成确定性的均匀随机数序列 const rng = vega.randomLCG(42); vega.setRandom(rng); vega.random(); // 稳定输出,每次运行结果一致这一组合在 packages/vega-statistics/test 下的各分布测试中被广泛采用,用于保证测试可复现。
三、分布方法:无状态采样与计算函数
分布方法是一组无状态的纯函数,直接接受分布参数并返回计算结果,适合在表达式或变换参数中即取即用。
3.1 正态(高斯)分布
对应源码 packages/vega-statistics/src/normal.js。默认mean=0、stdev=1,支持四个函数:
| 函数 | 签名 | 说明 |
|---|---|---|
vega.sampleNormal | ([mean, stdev]) | 从正态分布采样一个值 |
vega.cumulativeNormal | (value[, mean, stdev]) | 给定输入值处的累积分布函数(CDF)值 |
vega.densityNormal | (value[, mean, stdev]) | 给定输入值处的概率密度函数(PDF)值 |
vega.quantileNormal | (probability[, mean, stdev]) | 给定概率的分位数(CDF 的逆) |
源码实现的几个工程要点:
- 采样使用Box-Muller 变换(normal.js),并通过缓存第二个高斯样本(
nextSample)避免浪费每次变换产生的一个随机数; - CDF采用 West (2009) 的有理多项式近似(normal.js),对 |z|>37 直接返回 0 以规避数值溢出;
- 分位数基于反误差函数
erfinv实现(normal.js),该近似移植自 Apache Commons Math 的 Mike Giles 实现,对越界概率返回NaN。
3.2 对数正态分布
对应源码 packages/vega-statistics/src/lognormal.js。注意这里的mean/stdev是对数尺度的参数,默认0与1。四个函数均基于正态实现推导:采样直接对正态样本取指数(Math.exp(mean + sampleNormal() * stdev)),CDF 等价于cumulativeNormal(Math.log(value), ...),分位数等价于Math.exp(quantileNormal(...)),PDF 则在value <= 0时返回 0。
| 函数 | 签名 |
|---|---|
vega.sampleLogNormal | ([mean, stdev]) |
vega.cumulativeLogNormal | (value[, mean, stdev]) |
vega.densityLogNormal | (value[, mean, stdev]) |
vega.quantileLogNormal | (probability[, mean, stdev]) |
3.3 连续均匀分布
对应源码 packages/vega-statistics/src/uniform.js,定义在区间[min, max)上,默认min=0、max=1。参数约定:只传一个参数时,它被解释为max(此时min取 0)。
| 函数 | 签名 |
|---|---|
vega.sampleUniform | ([min, max]) |
vega.cumulativeUniform | (value[, min, max]) |
vega.densityUniform | (value[, min, max]) |
vega.quantileUniform | (probability[, min, max]) |
源码中 PDF 在区间外返回 0、区间内返回常数1/(max-min);CDF 在value < min时为 0、value > max时为 1、区间内线性插值;分位数对越界概率返回NaN(uniform.js)。
四、分布对象:参数化随机变量的统一接口
分布对象把一组分布参数封装成"随机变量",统一暴露四个方法(见 statistics.md):
dist.sample():从该分布抽取一个随机值;dist.pdf(value):计算给定输入值处的概率密度函数值;dist.cdf(value):计算给定输入值处的累积分布函数值;dist.icdf(probability):计算给定概率的逆累积分布函数值。
构造后的对象还支持 getter/setter 形式的参数读写。以正态为例(normal.js),mean()与stdev()无参调用返回当前值,带参调用设置新值并返回dist自身以支持链式调用。
4.1 randomNormal / randomLogNormal / randomUniform
vega.randomNormal([mean, stdev]):正态分布对象,可用mean/stdevgetter/setter;vega.randomLogNormal([mean, stdev]):对数正态分布对象,可用mean/stdevgetter/setter(对数尺度);vega.randomUniform([min, max]):连续均匀分布对象,可用min/maxgetter/setter,单参数时解释为max。
4.2 randomInteger:离散均匀分布
vega.randomInteger([min,] max)创建整数域[min, max)上的离散均匀分布对象(源码 packages/vega-statistics/src/integer.js)。只传一个参数时解释为max,min默认 0;同样提供min/maxgetter/setter。实现上sample()为a + Math.floor(d * random()),pdf仅在x为整数且位于[a, b)时返回1/d(integer.js)。
4.3 randomMixture:混合分布
vega.randomMixture(distributions[, weights])创建若干分布对象的(加权)混合(源码 packages/vega-statistics/src/mixture.js):
distributions必须是分布对象数组;- 可选
weights数组提供各分布的数值权重,会被自动归一化为总和为 1;未指定的权重默认为 1(归一化前); - 不支持
icdf:调用会直接抛错'Mixture icdf not supported.'(mixture.js); - 提供
distributions与weightsgetter/setter。
从源码看,sample()先按归一化权重累加概率随机选中一个子分布,再调用其sample();pdf/cdf则是各子分布结果的加权求和(mixture.js)。
4.4 randomKDE:核密度估计
vega.randomKDE(values[, bandwidth])基于数值数组values构造**核密度估计(KDE)**分布对象(源码 packages/vega-statistics/src/kde.js):
- 使用高斯核估计平滑的连续概率分布;
- 可选
bandwidth决定高斯核宽度;若为 0 或未指定,将基于输入数据自动估计默认带宽(调用estimateBandwidth,即下文bandwidthNRD); - 不支持
icdf:调用同样抛错'KDE icdf not supported.'(kde.js); - 提供
data与bandwidthgetter/setter。
实现上sample()先均匀抽取一个支撑点,再叠加一个缩放后的高斯噪声(support[~~(random()*n)] + bandwidth * kernel.sample());pdf/cdf是对所有支撑点核函数的平均(kde.js)。
4.5 综合示例与变换中的使用
// 正态分布对象 const dist = vega.randomNormal(100, 15); dist.mean(); // 100 dist.sample(); // 一次采样 dist.cdf(115); // 小于 115 的概率 dist.icdf(0.5); // 中位数 ≈ 100 dist.stdev(20); // 修改标准差 // 混合分布:70% 正态 + 30% 均匀 const mix = vega.randomMixture([ vega.randomNormal(0, 1), vega.randomUniform(0, 10) ], [0.7, 0.3]); mix.sample();分布对象在 Vega 变换层被直接消费:vega-transforms的 packages/vega-transforms/src/util/Distributions.js 将kde、mixture、normal、lognormal、uniform五种函数名映射到vega-statistics的构造器,据此把 Vega 规范中的distributions参数对象解析为分布对象实例(含from数据字段抽取与混合分布递归解析)。
五、回归:二维拟合、预测与决定系数
文档把回归单独列为一节:用二维回归方法根据一个变量预测另一个变量。除regressionConstant外,所有回归方法签名统一为vega.regressionXxx(data, x, y[, ...]),其中data是输入数据数组,x/y是取值访问器函数。返回对象包含三部分:
coef:拟合系数数组;predict:对输入x值返回回归预测值的函数;rSquared:R² 决定系数,表示模型解释的y总方差比例(计算见 packages/vega-statistics/src/regression/r-squared.js:1 - SSE/SST)。
| 方法 | 函数形式 | coef |
|---|---|---|
regressionLinear | y = a + b*x | [a, b] |
regressionLog | y = a + b*log(x) | [a, b] |
regressionExp | y = a + e^(b*x) | [a, b] |
regressionPow | y = a * x^b | [a, b] |
regressionQuad | y = a + b*x + c*x² | [a, b, c] |
regressionPoly | y = a + b*x + ... + k*x^order | [a, b, c, d, ...](长度 order+1) |
源码佐证:
- 线性回归基于普通最小二乘(OLS):通过在线累加均值与矩,调用 packages/vega-statistics/src/regression/ols.js 求解斜率与截距(linear.js);
- 多项式回归对 order 0/1/2 分别复用
constant/linear/quad的高效实现,更高阶则构建法方程矩阵并用高斯消元求解(poly.js),且对系数做了去中心化还原(uncenter); - 对数/指数/幂回归通过变量代换(对
x或y取对数)转化为线性问题后复用 OLS。
regressionLoess:局部加权回归
vega.regressionLoess(data, x, y, bandwidth)使用loess(局部估计散点平滑)拟合非参数光滑趋势线(源码 packages/vega-statistics/src/regression/loess.js):
bandwidth表示滑动窗口大小,取值[0, 1],是包含的总数据点数量的比例;源码中邻域大小为Math.max(2, ~~(bandwidth * n))(loess.js);- 对每个点,在最近邻滑动窗口内做加权线性回归,权重由tricube 核
(1 - |t|³)³给出; - 执行最多 2 次鲁棒迭代(
maxiters = 2),基于残差中位数重新加权,抑制离群点影响; - 返回平滑后的
[x, y]点数组(重复 x 值取平均)。该实现改编自 science.js。
sampleCurve:自适应曲线采样
vega.sampleCurve(f, extent[, minSteps, maxSteps])从插值函数f在域extent上生成样本点并返回[x, y]数组(源码 packages/vega-statistics/src/sampleCurve.js)。其要点:
- 自适应细分:在曲率更高处动态加密采样点,当当前样本与拟细分点的夹角差低于四分之一度(实现为
0.5 * π / 180弧度,见 sampleCurve.js)时停止细分; minSteps(默认 25):初始均匀分布的最小样本数;maxSteps(默认 200):自适应采样停止的最大分辨率,相对于大小为maxSteps的均匀网格定义;- 若
minSteps与maxSteps相同,则不做自适应,只返回初始均匀样本。
const pts = vega.sampleCurve(x => Math.sin(x), [0, Math.PI * 2]); // 返回 [x, y] 数组,弯折处更密集六、统计例程:分箱、置信区间与分位数
bandwidthNRD:正态参考带宽
vega.bandwidthNRD(array[, accessor])为高斯核密度估计估计带宽,假设正态参考分布(源码 packages/vega-statistics/src/bandwidth.js)。公式源自 Scott (1992):
1.06 × min(标准差, 四分位距 / 1.34) × n^(-1/5)
其中n为样本量;当四分位距或标准差为零时有特殊处理。可选的accessor先从对象数组提取数值,等价于先执行array.map(accessor)。
bin:直方图分箱方案
vega.bin(options)确定定量分箱方案(例如构建直方图)。它会在给定数值基(base)上搜索可能的步长空间,并施加最大箱数等约束,最终返回描述分箱方案的{start, stop, step}对象。支持的选项如下(完整继承自官方文档):
| 选项 | 说明 | 默认值 |
|---|---|---|
extent | (必填)[min, max]两元素数组,表示分箱值域 | — |
base | 自动分箱使用的数值基 | 10 |
maxbins | 允许的最大箱数(常因按"整齐"圆整值切分域而更少) | 20 |
span | 生成箱边界的值跨度,默认extent[1]-extent[0];允许在自定义跨度(如放大区域)上自动确定步长,同时保留整体extent | 由 extent 决定 |
step | 精确步长;提供后maxbins、span、steps均被忽略 | — |
steps | 允许的步长数组;提供后maxbins被忽略 | — |
minstep | 最小允许步长(对整数值尤其有用) | 0 |
divide | 允许的细分比例因子数组 | [5, 2] |
nice | 是否将 start/stop 相对步长圆整为整齐值 | true |
官方文档给出的三个经典示例(在 statistics.md 中亦有收录):
vega.bin({extent:[0, 1], maxbins:10}); // {start:0, stop:1, step:0.1} vega.bin({extent:[0, 1], maxbins:5}); // {start:0, stop:10, step:2} vega.bin({extent:[5, 10], maxbins:5}); // {start:5, stop:10, step:1}源码实现(packages/vega-statistics/src/bin.js)的决策顺序是:显式step→ 限定steps数组 → 基于 span 与 base 自动推导(计算对数级别、按maxbins约束放大、再按divide细分收缩),最后按nice圆整边界并处理浮点精度。其行为由 packages/vega-statistics/test/bin-test.js 系统验证。
bootstrapCI:自助法置信区间
vega.bootstrapCI(array, samples, alpha[, accessor])基于自助法(bootstrap)重采样计算置信区间(源码 packages/vega-statistics/src/bootstrapCI.js):
samples为重采样迭代次数,alpha为目标显著水平;例如alpha=0.05对应 95% 置信区间;- 每次迭代从原样本有放回地均匀抽取
n个值并求均值,samples个均值排序后取alpha/2与1-alpha/2分位作为区间两端; - 忽略
null、undefined与NaN值(由 packages/vega-statistics/src/numbers.js 的生成器负责过滤与数值化); - 空数组返回
[undefined, undefined]。
dotbin:点图分箱
vega.dotbin(sortedArray, step[, smooth, accessor])计算**点图(dot plot)**分箱位置,返回与输入sortedArray索引一一对应的箱位置数组(源码 packages/vega-statistics/src/dotbin.js):
- 实现 Wilkinson (1999) 的 "dot density" 算法;
step决定箱宽,距锚点step范围内的点共享同一箱位置; - 可选
smooth布尔值:为真时对箱位置做平滑以降低方差(将相邻堆叠内的点按step/4阈值交换,见 dotbin.js); - 注意:调用前应移除所有
null、undefined与NaN值;输入需为已排序数组。
quantiles 与 quartiles:分位数与四分位边界
vega.quantiles(array, p[, accessor]):给定概率阈值数组p(范围 [0, 1]),返回长度与p相同的 p-分位数数组(源码 packages/vega-statistics/src/quantiles.js)。实现将数值转为Float64Array排序后调用 d3-array 的quantileSorted,并刻意不依赖类型数组排序的返回值,以避免 Safari 的未定义排序结果问题;vega.quartiles(array[, accessor]):返回 3 元素数组[第一四分位, 中位数, 第三四分位](源码 packages/vega-statistics/src/quartiles.js),即quantiles(array, [0.25, 0.50, 0.75], accessor)的快捷封装。
两者同样忽略null、undefined与NaN值,并支持可选accessor提取字段。
七、在 Vega 中的实际应用与进一步阅读
变换层消费
vega-statistics的能力被多个数据变换直接复用(见 packages/vega-transforms/src 中对vega-statistics的引用):
- Bin.js:调用
vega.bin生成分箱方案; - Density.js:调用
randomKDE生成核密度估计分布并采样; - DotBin.js:调用
dotbin计算点图位置; - KDE.js:基于
randomKDE输出平滑密度; - Quantile.js:调用
quantiles计算分位数; - Sample.js:使用分布对象做随机采样。
对应这些变换的 Vega 规范文档见 docs/docs/transforms 目录(如 density.md、bin.md、quantile.md 等),其中distribution参数即通过上文提到的 Distributions.js 解析器映射到本文的分布对象构造器。
表达式层消费
vega-functions的表达式代码生成器 packages/vega-functions/src/codegen.js 将cumulativeNormal、densityNormal、quantileNormal、sampleNormal、sampleLogNormal、sampleUniform、densityUniform、quantileUniform、cumulativeLogNormal、densityLogNormal、quantileLogNormal、random等统计函数注入表达式运行时,因此在 Vega 的 signal 表达式或 transform 参数中可以直接书写cumulativeNormal(datum.x, 0, 1)这类调用。
独立使用与测试
独立使用时直接导入vega-statistics包即可:
import {sampleNormal, bin, regressionLinear} from 'vega-statistics';每个 API 都有对应的单元测试可作行为参考,例如 packages/vega-statistics/test/normal-test.js、bin-test.js、regression-test.js、kde-test.js、bootstrapCI-test.js 等,覆盖默认参数、边界条件与数值精度。
小结
Vega 的 Statistics API 以 packages/vega-statistics 为内核,为概率分布建模、随机数控制、回归拟合与统计计算提供了统一而完备的编程接口:random/setRandom/randomLCG提供可复现的随机数基础;sample*/cumulative*/density*/quantile*系列提供无状态的分布计算;六类分布对象以sample/pdf/cdf/icdf统一接口封装随机变量(混合与 KDE 不支持icdf);七种回归方法与自适应曲线采样覆盖参数与非参数拟合;bin、bootstrapCI、dotbin、bandwidthNRD、quantiles、quartiles则支撑直方图、置信区间、点图与箱线图等常见可视化统计需求。这些能力既通过顶层vega对象在规范中直接可用,也可脱离 Vega 独立集成到任意 JavaScript 项目中。
- 数据可视化
【免费下载链接】vega
A visualization grammar.
相关推荐
Vega Regression Transform 完全指南:参数化回归模型与趋势线拟合
Vega Regression Transform 完全指南:参数化回归模型与趋势线拟合 Vega 的 regression transform 用于对二维数据
数据可视化Vega Density Transform 完全指南:从概率分布到核密度估计的可视化采样
Vega Density Transform 完全指南:从概率分布到核密度估计的可视化采样 导读 本文深入讲解 Vega 可视化语法库中的 density 变换
数据可视化如何用CuPy实现GPU加速的概率分布计算:统计建模与数据分析终极指南
如何用CuPy实现GPU加速的概率分布计算:统计建模与数据分析终极指南 CuPy是一个基于NumPy接口的Python库,专为GPU加速计算而设计。通过CuPy
科学计算高性能计算
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考