控制Kruskal-Wallis H检验模拟数据

19 阅读5分钟

控制Kruskal-Wallis H检验模拟数据的样本量,核心是调整分组数据的长度参数,同时保证组间样本量的可配置性(均衡/不均衡均可)。以下是具体方法、代码示例和实操技巧,结合之前的场景逐一拆解:

一、核心控制逻辑:样本量的3种设置方式

模拟数据的样本量完全由分组数据的元素个数决定,主要有3种实现思路,适用于不同需求:

控制方式适用场景核心代码逻辑
直接指定每组长度样本量固定(如A组50,B组60)['A']*50 + ['B']*60
变量参数化需灵活调整样本量n1=50; ['A']*n1
比例分配按比例拆分总样本量total=200; n1=int(total*0.3)

二、分场景实操代码(附带样本量控制)

1. 基础场景:固定每组样本量(均衡/不均衡均可)

需求:设置 CHB 组 50 例、NAFLD 组 50 例、合并组 100 例,总样本量 200。

import pandas as pd
import numpy as np

# 1. 定义每组样本量(参数化,方便修改)
n_chb = 50
n_nafld = 50
n_combined = 100

# 2. 生成分组列:通过列表乘法控制样本量
groups = ['CHB'] * n_chb + ['NAFLD'] * n_nafld + ['CHB+NAFLD'] * n_combined

# 3. 生成对应长度的因变量数据
alp_data = np.concatenate([
    np.random.lognormal(4.3, 0.5, n_chb),      # 长度匹配 n_chb
    np.random.lognormal(4.5, 0.6, n_nafld),    # 长度匹配 n_nafld
    np.random.lognormal(4.4, 0.55, n_combined) # 长度匹配 n_combined
])

# 4. 构建DataFrame
data = pd.DataFrame({
    'group': groups,
    'ALP': alp_data
})

# 验证样本量
print("各组样本量:")
print(data['group'].value_counts())
print(f"总样本量:{len(data)}")

关键点:列表乘法 ['组名']*样本量 是最直接的方式,因变量数组长度必须和分组列长度完全一致,否则会报错。

2. 进阶场景:按比例分配总样本量

需求:总样本量 300,对照组占 20%、调查诱导组 25%、脚本机器人组 25%、生成式机器人组 30%。

# 1. 定义总样本量和比例
total_n = 300
props = [0.2, 0.25, 0.25, 0.3]  # 四组比例
groups_name = ['对照组', '调查诱导组', '脚本机器人组', '生成式机器人组']

# 2. 计算每组样本量(四舍五入取整)
n_list = [int(total_n * p) for p in props]
# 修正:避免四舍五入后总样本量不一致
n_list[-1] = total_n - sum(n_list[:-1])

# 3. 生成分组列和因变量
groups = []
score_data = []
for i, name in enumerate(groups_name):
    groups += [name] * n_list[i]
    # 按组生成评分数据
    mu = 3 + i * 0.8  # 组间mu递增,制造差异
    scores = np.random.poisson(mu, n_list[i]) + np.random.normal(0, 0.5, n_list[i])
    score_data.extend(scores)

# 4. 构建DataFrame
data = pd.DataFrame({
    'group': groups,
    'confidence_score': score_data
})

# 验证
print("各组样本量(按比例分配):")
print(data['group'].value_counts())
print(f"总样本量:{len(data)}")

关键点:按比例分配后,最后一组样本量用 总样本量 - 其他组之和 修正,避免因四舍五入导致总样本量偏差。

3. 实用场景:批量生成多指标数据(统一样本量)

需求:3 个污染等级,每组样本量 40,共 120 样本,包含 3 个生物多样性指标。

# 1. 统一设置每组样本量
n_per_group = 40
groups = ['轻度污染', '中度污染', '重度污染']
n_groups = len(groups)

# 2. 生成分组列
groups_col = []
for g in groups:
    groups_col += [g] * n_per_group

# 3. 生成多指标数据(每组长度都是n_per_group)
alpha = np.concatenate([np.random.normal(6+i*(-1.5), 0.8, n_per_group) for i in range(n_groups)])
beta = np.concatenate([np.random.normal(3.5+i*(-1), 0.6, n_per_group) for i in range(n_groups)])
species = np.concatenate([np.random.poisson(45-i*10, n_per_group) for i in range(n_groups)])

# 4. 构建DataFrame
data = pd.DataFrame({
    'pollution_level': groups_col,
    'alpha_diversity': alpha,
    'beta_diversity': beta,
    'species_richness': species
})

# 验证
print("各组样本量(多指标):")
print(data['pollution_level'].value_counts())

关键点:多指标场景下,所有因变量的长度必须和分组列一致,用列表推导式+np.concatenate 可高效生成。

三、实操注意事项

  1. 长度一致性:分组列的长度 ≡ 每个因变量数组的长度,这是构建DataFrame的硬性要求,否则会触发 ValueError: arrays must all be same length
  2. 随机种子固定:若需结果可复现,在生成数据前添加 np.random.seed(42)样本量不影响随机性,只影响数据量大小
  3. 样本量合理性
    • 每组样本量不宜过小(建议≥20),否则K-W检验的检验效能(power)会下降,难以检测到真实差异;
    • 不等样本量完全不影响K-W检验的执行,该检验对样本量不均衡的鲁棒性很强。

四、拓展:动态调整样本量的函数封装

如果需要反复调整样本量,可封装成函数,一键生成数据:

def generate_kw_data(n_per_group_list, group_names, dist_type='lognormal', dist_params=None):
    """
    生成K-W检验模拟数据
    :param n_per_group_list: 每组样本量列表,如[50,60,100]
    :param group_names: 组名列表,如['A','B','C']
    :param dist_type: 分布类型,lognormal/poisson/normal
    :param dist_params: 每组分布参数列表,如[(4.3,0.5), (4.5,0.6)]
    :return: DataFrame
    """
    groups = []
    values = []
    for i, n in enumerate(n_per_group_list):
        groups += [group_names[i]] * n
        params = dist_params[i]
        if dist_type == 'lognormal':
            val = np.random.lognormal(params[0], params[1], n)
        elif dist_type == 'poisson':
            val = np.random.poisson(params[0], n)
        elif dist_type == 'normal':
            val = np.random.normal(params[0], params[1], n)
        values.extend(val)
    return pd.DataFrame({'group': groups, 'value': values})

# 调用函数:生成3组数据,样本量[40,50,60]
data = generate_kw_data(
    n_per_group_list=[40,50,60],
    group_names=['HIIT', '持续有氧', '不运动'],
    dist_type='lognormal',
    dist_params=[(3.2,0.4), (3.5,0.5), (3.8,0.6)]
)
print(data['group'].value_counts())