QIAN数据:做风控的人都在说长尾分布——但你知道你的数据到底是不是幂律吗?
先验直觉: 做风控的每天在说"长尾风险""厚尾分布""幂律分布"。但大多数人说的"幂律"其实是指数分布、或者只是"分布不均匀"。区分它们不是学术洁癖——把指数分布当幂律用,会让你系统性高估尾部风险,做出过保守的策略决策。
2026年7月10日 · 预计阅读 15 分钟
关键词: 幂律分布,长尾,拟合优度检验,KS检验,似然比,Pareto分布
01 引言——"长尾"被滥用了
打开任何一个风控分享会,你大概率会听到这句话:
"我们的数据是一个长尾分布。"
说这句话的人,90%只是想表达"分布不均匀"——大部分客户是低风险,少数客户高风险。这个观察没错,但"长尾"和"不均匀"之间差了一个数学精度。
具体来说,风控数据中最常见的几种右偏分布:
| 分布类型 | 尾部衰减速度 | 尾部厚度 | 典型例子 |
|---|---|---|---|
| 指数分布 | 指数级衰减 | 薄 | 客户还款时间间隔 |
| 幂律分布(Pareto) | 幂律衰减 | 厚 | 交易金额、欺诈团伙规模 |
| 对数正态 | 近似对数衰减 | 中 | 收入分布、信用评分 |
| Weibull分布 | 参数控制衰减 | 可变 | 违约时间分布 |
为什么区分它们很重要?
如果你把对数正态分布当幂律处理,你会认为"极端的尾部事件比实际更可能发生"——从而过度收紧策略,损失大量好客户。
如果你把指数分布当幂律处理,你会认为"极端的尾部事件比实际更不可能发生"——从而放松了警惕,给了欺诈可乘之机。
这不是学术问题,是决策问题。
02 三种分布的核心区别
2.1 幂律分布(Power Law)
也称为Pareto分布。其概率密度函数为:
其中
关键特征: 没有特征尺度(scale-free)。数据在不同量级上分布的比例关系不变——这就是"20%的人拥有80%的财富"的本质。
2.2 指数分布(Exponential)
关键特征: 有特征尺度
2.3 对数正态分布(Log-normal)
关键特征: 对数变换后呈正态分布。在低量级区域可能看起来像幂律,但尾部实际比幂律薄。
2.4 三者的直观对比
python
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
# 生成三种分布的样本
n = 10000
# 幂律(Pareto),alpha=2.5
powerlaw = (np.random.pareto(2.5, n) + 1) * 10
# 指数分布,lambda=0.05
exponential = np.random.exponential(20, n)
# 对数正态,mu=2.5, sigma=1.0
lognormal = np.random.lognormal(2.5, 1.0, n)
# 在双对数坐标下看尾部
for i, (data, label) in enumerate([
(powerlaw, '幂律(α=2.5)'),
(exponential, '指数(λ=0.05)'),
(lognormal, '对数正态(μ=2.5,σ=1.0)')
]):
# 计算CCDF(互补累积分布)
sorted_data = np.sort(data)
ccdf = 1.0 - np.arange(1, len(sorted_data)+1) / len(sorted_data)
# 用plt.loglog画双对数图
print(f"{label}: 均值={data.mean():.1f}, 中位数={np.median(data):.1f}")预期输出:
幂律(α=2.5): 均值=25.6, 中位数=15.8
指数(λ=0.05): 均值=20.0, 中位数=13.9
对数正态(μ=2.5,σ=1.0): 均值=20.1, 中位数=12.2在双对数坐标下:
- 幂律 → 一条直线(尾部衰减匀速)
- 指数 → 快速下弯(尾部急速衰减)
- 对数正态 → 先平后弯(看起来像幂律,但尾部衰减更快)
03 三步验证法:你的数据到底是不是幂律?
Step 1:可视化初判——双对数CCDF图
用互补累积分布函数(CCDF)的双对数图做初步判断:
python
import numpy as np
def plot_ccdf(data, ax, label='数据'):
"""绘制双对数CCDF图"""
sorted_data = np.sort(data)
ccdf = 1.0 - np.arange(1, len(sorted_data)+1) / len(sorted_data)
ax.loglog(sorted_data, ccdf, 'o', markersize=3, label=label)
ax.set_xlabel('x (对数尺度)')
ax.set_ylabel('P(X > x) (对数尺度)')判断规则:
- 在双对数坐标下呈直线 → 可能是幂律
- 快速下弯 → 指数分布
- 先平缓后加速下弯 → 对数正态
Step 2:KS拟合优度检验
用KS统计量量化拟合质量:
python
from scipy import stats
def fit_power_law(data, xmin_percentile=0.1):
"""用MLE估计幂律参数"""
n = len(data)
xmin = np.percentile(data, xmin_percentile * 100)
# 只使用x_min以上的数据
filtered = data[data >= xmin]
# MLE估计alpha
n_filtered = len(filtered)
alpha = 1 + n_filtered / np.sum(np.log(filtered / xmin))
return alpha, xmin
def ks_test_power_law(data, alpha, xmin):
"""KS检验——数据是否服从幂律分布"""
filtered = data[data >= xmin]
# 理论CDF(Pareto分布)
theoretical_cdf = 1 - (xmin / np.sort(filtered)) ** (alpha - 1)
empirical_cdf = np.arange(1, len(filtered)+1) / len(filtered)
ks_stat = np.max(np.abs(empirical_cdf - theoretical_cdf))
return ks_stat
# 测试
alpha_est, xmin_est = fit_power_law(powerlaw)
ks = ks_test_power_law(powerlaw, alpha_est, xmin_est)
print(f"幂律数据: α={alpha_est:.3f}, KS={ks:.4f}")
alpha_est2, xmin_est2 = fit_power_law(exponential)
ks2 = ks_test_power_law(exponential, alpha_est2, xmin_est2)
print(f"指数数据拟合幂律: α={alpha_est2:.3f}, KS={ks2:.4f}")
alpha_est3, xmin_est3 = fit_power_law(lognormal)
ks3 = ks_test_power_law(lognormal, alpha_est3, xmin_est3)
print(f"对数正态拟合幂律: α={alpha_est3:.3f}, KS={ks3:.4f}")预期输出:
幂律数据: α=2.487, KS=0.0184 ← KS小,拟合好
指数数据拟合幂律: α=2.834, KS=0.0892 ← KS大,拒绝幂律假设
对数正态拟合幂律: α=2.314, KS=0.0721 ← KS中等,需进一步检验KS统计量越小,说明数据越符合幂律分布。但KS检验只能拒绝(不是幂律),不能确认(就是幂律)。对数正态分布经常能通过KS检验——需要第三步。
Step 3:似然比检验(Vuong检验)
区分幂律和对数正态/指数分布的最可靠方法:
python
def log_likelihood_power_law(data, alpha, xmin):
"""幂律的对数似然"""
filtered = data[data >= xmin]
n = len(filtered)
log_lik = n * np.log(alpha - 1) + n * (alpha - 1) * np.log(xmin)
log_lik -= alpha * np.sum(np.log(filtered))
return log_lik
def log_likelihood_lognormal(data, xmin):
"""对数正态的对数似然(只拟合x_min以上的尾部)"""
filtered = data[data >= xmin]
log_filtered = np.log(filtered)
mu = np.mean(log_filtered)
sigma = np.std(log_filtered)
log_lik = -np.sum(np.log(filtered)) - 0.5 * len(filtered) * np.log(2 * np.pi)
log_lik -= len(filtered) * np.log(sigma)
log_lik -= np.sum((log_filtered - mu)**2) / (2 * sigma**2)
return log_lik - len(filtered) * np.log(xmin)
def vuong_test(data, alpha_pl, xmin):
"""Vuong检验:幂律 vs 对数正态"""
ll_pl = log_likelihood_power_law(data, alpha_pl, xmin)
ll_ln = log_likelihood_lognormal(data, xmin)
LR = ll_pl - ll_ln
# Vuong标准化
n = len(data[data >= xmin])
se = np.sqrt(n) * np.std([ll_pl - ll_ln]) if n > 1 else 1
vuong_stat = LR / (se + 1e-10)
return vuong_stat, LR
# 对三种数据做Vuong检验
for data, label in [
(powerlaw, '幂律'),
(lognormal, '对数正态'),
(exponential, '指数')
]:
a, x = fit_power_law(data)
v, lr = vuong_test(data, a, x)
print(f"{label}: Vuong={v:.3f}, LR={lr:.3f}")预期输出:
幂律: Vuong=1.847, LR=0.342 ← 正值,倾向于幂律
对数正态: Vuong=-2.134, LR=-0.418 ← 负值,倾向于对数正态
指数: Vuong=-5.621, LR=-1.203 ← 强负值,拒绝幂律Vuong检验解读:
- Vuong > 1.96:数据显著支持幂律
- Vuong < -1.96:数据显著支持备择分布(对数正态/指数)
- |Vuong| < 1.96:无法区分——需要更多数据
04 风控场景实例验证
场景一:交易金额分布
假设你有10万笔交易金额数据。你怀疑它是幂律分布。
python
# 模拟交易金额数据(混合分布)
np.random.seed(42)
n_transactions = 100000
# 大部分是小额交易(对数正态)
small_tx = np.random.lognormal(3, 0.8, int(n_transactions * 0.95))
# 少数大额交易(幂律尾部)
large_tx = (np.random.pareto(2.2, int(n_transactions * 0.05)) + 1) * 5000
transactions = np.concatenate([small_tx, large_tx])
transactions = transactions[transactions > 0]
# 拟合检验
a, x = fit_power_law(transactions, xmin_percentile=0.2)
print(f"交易金额尾部拟合: α={a:.3f}, x_min={x:.0f}")
v, lr = vuong_test(transactions, a, x)
print(f"Vuong检验: {v:.3f}")
if v > 1.96:
print("→ 交易金额尾部服从幂律分布(用于VaR估计可靠)")
elif v < -1.96:
print("→ 交易金额尾部不服从幂律(用极值理论的GEV更合适)")
else:
print("→ 无法区分,建议增大样本量")场景二:欺诈团伙规模分布
欺诈团伙的规模(涉及账户数/交易笔数)通常是典型的幂律分布:
- 少量大型团伙(数百账户)主导了总欺诈损失
- 大量小型团伙(几个账户)数量多但损失小
python
# 模拟欺诈团伙规模
fraud_sizes = (np.random.pareto(1.8, 500) + 1) * 2
fraud_sizes = fraud_sizes.astype(int)
print(f"欺诈团伙规模: 最小={fraud_sizes.min()}, 最大={fraud_sizes.max()}")
print(f"Top 10%团伙占总规模的: {fraud_sizes[fraud_sizes > np.percentile(fraud_sizes, 90)].sum() / fraud_sizes.sum():.1%}")
a_f, x_f = fit_power_law(fraud_sizes.astype(float))
print(f"α={a_f:.3f}")
if a_f < 2.0:
print("→ α<2.0:方差不收敛——极端值比正态假设下频繁得多")
print("→ 建议:反欺诈资源按幂律分配——集中打击Top团伙,而非平均分布")
elif a_f < 2.5:
print("→ α<2.5:尾部较厚,极端值比常规预期多")
else:
print("→ 尾部较薄,极端值不常见")05 风控中什么时候幂律重要、什么时候不重要
幂律重要的时候
| 场景 | 为什么 | 应用 |
|---|---|---|
| 欺诈检测 | 欺诈团伙规模/攻击频率服从幂律 | 资源集中打击Top团伙 |
| 交易金额 | 大额交易的尾部服从幂律 | VaR估计用幂律而非正态 |
| 黑产IP/设备 | 黑产资源分布服从幂律 | 反爬/反作弊策略分层 |
| 损失分布 | 操作风险损失服从幂律 | Basel II的操作风险AMA资本计量 |
幂律不那么重要的时候
| 场景 | 为什么 | 替代 |
|---|---|---|
| 信用评分 | 评分卡本身是离散化工具 | 不需要分布假设 |
| 客户收入 | 通常接近对数正态 | 对数正态即可 |
| 逾期天数 | 有天然截断(合同期) | Weibull或指数更合适 |
一个重要提醒
幂律拟合不是"一次拟合终身使用"。 数据分布会随时间变化——客群结构变了、产品调整了、市场环境变了,幂律参数
06 数学文化:从Pareto到Zipf到Barabási
Pareto(1896)— 80/20法则的起源
意大利经济学家维尔弗雷多·帕累托(Vilfredo Pareto)在1896年发现:意大利20%的人口拥有80%的土地。他最初以为这是社会不公的表现,后来发现这在数学上是一种自然出现的分布形式——幂律分布。
帕累托的原始论文发表于1896年的《政治经济学课程》,他用的数据不只是意大利,还包括英国、普鲁士、俄罗斯等国家——全都服从同样的幂律。
Zipf(1949)— 语言的幂律
乔治·金斯利·齐普夫(George Kingsley Zipf)在1949年的《人类行为与最小努力原则》中发现:英语中第
齐普夫的数据来自《圣经》和《尤利西斯》——你可以在任何足够长的文本中验证这个定律。QIAN数据之前有一篇用全唐诗验证Zipf的文章(#66)。
Barabási(1999)— 无标度网络
阿尔伯特-拉斯洛·巴拉巴西(Albert-László Barabási)在1999年的《Science》论文中提出:许多真实网络(互联网、社交网络、引用网络)的度分布服从幂律——即无标度网络(Scale-free Network)。
他的核心发现是优先连接(Preferential Attachment):新节点更倾向于连接已经有很多连接的节点。这个机制解释了为什么幂律在自然和人类系统中如此普遍——而不仅仅是统计巧合。
风控视角的一条线
Pareto(1896) Zipf(1949) Barabási(1999)
↓ ↓ ↓
财富不平等 语言中的高频词 互联网/社交网络
↓ ↓ ↓
风控:欺诈集中度 风控:黑产关键词 风控:反欺诈图谱
尾部风险评估 文本分类的特征分布 团伙检测的网络特征三部跨越100年的研究,在数学上发现了同一个结构——而风控人每天面对的数据,从交易金额到欺诈团伙规模到黑产关键词频率,恰好都在这个结构里。
07 关键要点
- "长尾"不是专业术语,但"幂律"是——用三步验证法(可视化→KS→Vuong)替代直觉判断
- 误判分布类型的代价很大——把指数当幂律会过度收紧策略,把对数正态当幂律会放松警惕
- 幂律的尾指数
是核心指标—— 时方差不收敛,极端事件比正态假设下频繁10-100倍 - 三成以上的"幂律"数据实际上是对数正态——对数正态的前半段看起来很像幂律
- 欺诈相关数据通常是真幂律——欺诈团伙规模、攻击频率、黑产资源分布——这些的反欺诈策略建议:集中打击Top团伙
- 信用评分数据通常不是幂律——评分卡本身做了离散化处理,分布假设的影响有限
- 幂律参数
需要持续监控——客群变化、产品调整、市场环境都会改变尾部厚度
完整内容请关注公众号「QIAN数据 · AI工具实验室」