偏度定义为E[(X−μ)³]/σ³,峰度常用超额峰度E[(X−μ)⁴]/σ⁴−3;计算需统一总体口径(AVG+STDDEV_POP),分两层实现:先算μ、σ,再归一化三/四阶中心矩,并处理NULL、除零及数据库兼容性问题。

偏度和峰度的统计定义必须先搞清
偏度(Skewness)衡量分布左右不对称程度,公式是 E[(X−μ)³]/σ³;峰度(Kurtosis)衡量尾部厚重程度,常用“超额峰度”(excess kurtosis),即 E[(X−μ)⁴]/σ⁴ − 3。注意:很多工具(如 Python 的 scipy.stats.skew、kurtosis)默认返回的就是这个减去 3 的结果,但原始矩定义不含 −3 —— SQL 里必须自己决定是否减,否则和 pandas 或 Excel 对不上。
关键点在于:所有计算都依赖分组内的 COUNT、AVG、STDDEV_POP(或 STDDEV_SAMP),且分子必须用三阶/四阶中心矩,不能直接套用 POWER(x - avg, 3) 后再 AVG —— 因为 AVG 是除以 n,而标准差分母也是 n(总体)或 n−1(样本),必须统一口径。
用窗口函数+聚合嵌套实现分组偏度
核心思路:先算每组的均值 μ 和标准差 σ,再用它们归一化三阶中心矩。不能在一个聚合层完成,需两层子查询或 CTE。
- 必须用
STDDEV_POP配合AVG(二者都是总体口径),否则分母不一致导致结果失真;若坚持用样本标准差(STDDEV_SAMP),则三阶矩也得用SUM((x - μ)³) / (COUNT(*) - 1),但这样偏离常规定义,易出错 -
POWER(x - μ, 3)在 NULL 值存在时会返回 NULL,务必用COALESCE(x, 0)或先过滤 - 示例(PostgreSQL/Oracle/SQL Server 支持):
SELECT
group_id,
AVG(val) AS mu,
STDDEV_POP(val) AS sigma,
CASE WHEN STDDEV_POP(val) = 0 THEN 0
ELSE SUM(POWER(val - AVG(val) OVER (PARTITION BY group_id), 3))
/ COUNT(*) OVER (PARTITION BY group_id)
/ POWER(STDDEV_POP(val), 3)
END AS skewness
FROM t
GROUP BY group_id;峰度计算要特别注意“超额”与否和数值稳定性
四阶矩比三阶更敏感,小样本下容易爆炸(比如某组只有 2 行,STDDEV_POP 极小,分母接近零)。实际中建议加保护分支。
- 如果目标是和
pandas.DataFrame.skew()、kurtosis()对齐,必须用STDDEV_SAMP+ 样本三/四阶矩校正(即除以 n−1),再对峰度减去 3 - 但多数数据库原生聚合不支持“样本四阶中心矩”,所以更稳妥的是:先用 CTE 算出每组
μ和σ²,再在主查询中用SUM(POWER(val - μ, 4)) / COUNT(*)得到四阶原点矩,再代入公式 - 避免
POWER(..., 4)导致整数溢出:对大数值列,先 CAST 为DOUBLE PRECISION或DECIMAL(38,10)
简化的可运行片段(假设已知 mu 和 sigma):
SELECT
group_id,
CASE WHEN sigma = 0 THEN 0
ELSE (SUM(POWER(CAST(val AS DOUBLE PRECISION) - mu, 4)) / COUNT(*))
/ POWER(sigma, 4) - 3
END AS kurtosis_excess
FROM (
SELECT group_id, val,
AVG(val) OVER (PARTITION BY group_id) AS mu,
STDDEV_POP(val) OVER (PARTITION BY group_id) AS sigma
FROM t
) t2
GROUP BY group_id, mu, sigma;不同数据库的兼容性陷阱
POWER 函数在 SQLite 中不支持小数指数,STDDEV_POP 在 MySQL 8.0+ 才有,旧版只能用 SQRT(VAR_POP(x));而 BigQuery 的 STDDEV 默认是样本标准差,和 PostgreSQL 的 STDDEV_SAMP 行为一致 —— 这意味着同样一段 SQL,在不同引擎跑出来的偏度可能差 5% 以上。
- MySQL 用户:用
SQRT(AVG(POWER(x - mu, 2)))替代STDDEV_POP,确保是总体标准差 - SQLite 用户:改用
x * x * x代替POWER(x, 3),并手动展开四次方(x*x*x*x) - BigQuery 用户:峰度公式中分母必须用
POW(STDDEV(x), 4),且确认输入是否已去 NULL —— 它的STDDEV遇到全 NULL 组会返回 NULL,而非 0
最麻烦的其实是空组或单值组:STDDEV_POP 返回 0,导致除零;此时偏度/峰度数学上无定义,但业务常要求返回 0 或 NULL,代码里必须显式判断,不能依赖数据库自动处理。

















