You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

卡方分布拟合:自由度估计与拟合参数合理性验证问题

卡方分布参数拟合问题及解决方案

问题描述

我有一批理论上服从卡方(chi2)分布的数据,由卡方分布抽样生成,已知生成时的参数(如均值、自由度dof)。现在需要从实测数据中拟合估计这些参数(均值相关参数、自由度估计值)。

尝试用以下PDF函数拟合:

def chi2_pdf(x, dof, loc = 0, scale=1):
    return chi2.pdf(x, df=dof, loc = loc, scale=scale)

但得到的参数难以解读,拟合结果出现不合理值(如负loc、远大于实际值的dof),例如:

Estimated dof, LS: 7793.619571961899
Estimated scale: 0.1006724349475489
Estimated loc:-720.5363121803032

已知卡方分布均值等于自由度,但上述结果完全不符。也尝试用curve_fit估计scipy.stats.chi2.pdf的参数,但无法解读结果。

想知道:

  • 如何正确拟合分布以得到合理的参数估计?
  • 是否需要先对数据进行缩放?
  • 如何确保估计的参数正确?

附可复现代码:

from distfit import distfit
from scipy.stats import chi2
from scipy.optimize import curve_fit
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 获取数据
data_csv_link = 'https://drive.google.com/uc?export=download&id=1Cs7-dTs6TesVvSNd7g0aJOj4IQqsOuCb'
data_df: pd.DataFrame = pd.read_csv(data_csv_link, ' ', header=None)

data = data_df[0].to_list()

# 绘制数据分布
fig, ax = plt.subplots()
ax.hist(data , density=True, bins=50, alpha=0.5, cumulative=False, label = 'Data')

x = np.linspace(min(data), max(data), 100)

# 真实参数生成的卡方分布曲线
df = 64 # 生成数据时用的真实自由度
loc = 0
scale = 1
ax.plot(x, chi2.pdf(x, df=df, loc = loc, scale=scale), 'r-', label = '真实分布曲线')

ax.set_title('数据分布')
ax.legend()

# 用distfit尝试拟合
dist = distfit(distr=['chi2', 'norm'])
results = dist.fit_transform(data_df[0])
dist.plot()
dist.plot_summary()
print(dist.summary)

解决方案

1. 明确scipy.stats.chi2中loc和scale的含义

scipy提供的卡方分布是位置-尺度修正后的卡方分布,并非标准卡方分布:

  • 标准卡方分布取值范围为[0, +∞),均值为dof,方差为2*dof
  • 修正后的分布满足公式:Y = loc + scale * X,其中X是标准卡方分布(自由度为dof)
  • 修正后分布的均值为:loc + scale * dof,方差为:scale² * 2*dof

你之前误将loc当作均值,这是导致拟合参数不合理的核心原因之一。

2. 不合理拟合结果的成因

  • curve_fit默认不对参数做约束,但卡方分布的自由度dof、缩放系数scale必须为正;结合卡方分布非负的特性,loc需满足loc + scale*0 >= 数据最小值,无约束优化容易跳出合理范围。
  • 直接拟合PDF易受数据局部波动影响,当数据与标准卡方分布存在偏移/缩放时,无约束优化会生成荒谬参数来适配局部特征。

3. 正确拟合步骤

步骤1:数据预处理(可选但推荐)

如果数据是缩放/平移后的卡方分布,可先通过矩估计获取参数初始值,为后续拟合提供合理起点:

# 计算数据的均值和方差
data_mean = np.mean(data)
data_var = np.var(data)

# 基于矩估计推导初始参数
init_scale = data_var / (2 * data_mean)
init_dof = 2 * (data_mean ** 2) / data_var
init_loc = data_mean - init_scale * init_dof

步骤2:带约束的参数拟合

优先使用scipy.stats.chi2.fit方法,它内置了参数合理性约束,比手动用curve_fit拟合PDF更可靠:

from scipy.stats import chi2

# 若数据无平移(符合生成参数设定),强制loc=0以提升拟合合理性
dof_fit, loc_fit, scale_fit = chi2.fit(data, floc=0)

print(f"拟合自由度dof: {dof_fit:.2f}")
print(f"拟合loc: {loc_fit:.2f}")
print(f"拟合scale: {scale_fit:.2f}")
  • floc=0:强制位置参数为0,符合标准卡方分布定义(若数据确实存在平移,可移除该参数,但需保证loc + scale*0 >= 数据最小值)。

步骤3:验证拟合结果

通过以下方式确认参数合理性:

  1. 矩匹配验证:对比拟合分布与实测数据的均值、方差:
    fit_mean = loc_fit + scale_fit * dof_fit
    fit_var = (scale_fit ** 2) * 2 * dof_fit
    print(f"数据均值: {data_mean:.2f}, 拟合分布均值: {fit_mean:.2f}")
    print(f"数据方差: {data_var:.2f}, 拟合分布方差: {fit_var:.2f}")
    
  2. 可视化对比:绘制实测数据直方图与拟合PDF曲线:
    fig, ax = plt.subplots()
    ax.hist(data, density=True, bins=50, alpha=0.5, label='实测数据')
    x = np.linspace(min(data), max(data), 1000)
    ax.plot(x, chi2.pdf(x, df=dof_fit, loc=loc_fit, scale=scale_fit), 'r-', label='拟合分布')
    ax.legend()
    ax.set_title('实测数据与拟合分布对比')
    plt.show()
    
  3. 统计检验:用KS检验判断数据是否来自拟合分布:
    from scipy.stats import kstest
    
    stat, p_value = kstest(data, 'chi2', args=(dof_fit, loc_fit, scale_fit))
    print(f"KS检验统计量: {stat:.4f}, p值: {p_value:.4f}")
    # p值大于0.05则无法拒绝“数据来自该分布”的假设
    

4. distfit工具的正确使用

若用distfit专注拟合卡方分布,需指定distr=['chi2'],其结果中的params对应(dof, loc, scale),与scipy参数定义一致,可直接参考解读。


内容的提问来源于stack exchange,提问作者twistfire

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 13:23:13