
本文详解scipy.stats.mannwhitneyu函数中nan_policy参数的作用与选择策略,重点说明如何安全、可靠地忽略或排除含NaN的临床响应标签(如"Resistant"/"Sensitive")对检验结果的影响,避免因默认传播NaN导致p值失效。
本文详解`scipy.stats.mannwhitneyu`函数中`nan_policy`参数的作用与选择策略,重点说明如何安全、可靠地忽略或排除含nan的临床响应标签(如"resistant"/"sensitive")对检验结果的影响,避免因默认传播nan导致p值失效。
在使用Mann-Whitney U检验分析药物敏感性数据(如logIC₅₀与临床响应类别)时,缺失值(NaN)是常见但极易被忽视的关键陷阱。正如提问者所遇:当test_dr_new.iloc[:,0]中存在大量NaN标签时,若未显式处理,mannwhitneyu将沿用默认参数 nan_policy='propagate' ——这意味着只要任一输入数组(如resistant_log_ic50或sensitive_log_ic50)中包含NaN,函数直接返回NaN作为p值,而非报错或跳过。这会导致后续统计汇总(如sum(p < 0.05))失效,甚至掩盖真实生物学信号。
✅ 正确做法:显式指定 nan_policy='omit'
nan_policy='omit' 是最符合科研实践需求的选项:它会在内部自动过滤掉所有含NaN的观测对,仅基于有效样本执行检验。其行为等价于手动清洗,但更安全、可复现且无需额外索引操作:
from scipy.stats import mannwhitneyu
for drug in logIC50_df.columns:
# 基于非NaN标签提取对应logIC50值(推荐:先对齐再过滤)
labels = test_dr_new.iloc[:, 0]
mask_valid = labels.notna() # 确保标签有效
valid_labels = labels[mask_valid]
valid_logIC50 = logIC50_df[drug][mask_valid]
# 分组提取(此时已无NaN干扰)
resistant_log_ic50 = valid_logIC50[valid_labels == "Resistant"]
sensitive_log_ic50 = valid_logIC50[valid_labels == "Sensitive"]
# 关键:显式设置 nan_policy='omit'(双重保险)
statistic, p_value = mannwhitneyu(
resistant_log_ic50,
sensitive_log_ic50,
alternative='greater',
nan_policy='omit' # ← 必须显式声明!
)
p_values[drug] = p_value⚠️ 注意:即使你已通过布尔索引过滤了NaN,仍建议保留 nan_policy='omit' ——它能防御潜在的数据加载错误(如浮点型NaN混入字符串列)、空组边界情况(如某药物下无"Resistant"样本),并确保行为与文档一致。
❌ 错误模式与风险提示
| nan_policy | 行为 | 风险 |
|---|---|---|
| 'propagate'(默认) | 输入含NaN → 输出statistic=nan, p_value=nan | 后续p < 0.05恒为False,显著性计数归零,静默失败 |
| 'raise' | 输入含NaN → 抛出ValueError | 中断流程,便于调试,但需额外try/except包裹,不适用于批量分析 |
| 'omit' | 自动丢弃含NaN的观测 | ✅ 安全、透明、符合统计惯例 |
此外,还需警惕逻辑顺序错误:原代码中logIC50_df[drug][test_dr_new.iloc[:,0] == "Resistant"]若test_dr_new.iloc[:,0]含NaN,布尔索引会生成pd.Series含NaN索引项,导致resistant_log_ic50本身携带NaN —— 此时即使设nan_policy='omit'也有效,但强烈建议先做notna()预过滤,确保输入数组纯净。
✅ 最佳实践总结
- 始终显式传入 nan_policy='omit',拒绝依赖默认行为;
- 预处理阶段主动清洗标签:用labels.notna()构建有效掩码,再分组提取,提升可读性与鲁棒性;
-
校验分组样本量:mannwhitneyu要求每组至少含1个有效观测,可在调用前添加断言:
assert len(resistant_log_ic50) > 0 and len(sensitive_log_ic50) > 0, \ f"Drug {drug}: insufficient samples for Mann-Whitney test" -
记录清洗日志:对每个药物打印有效样本数,便于质量控制:
print(f"{drug}: Resistant={len(resistant_log_ic50)}, Sensitive={len(sensitive_log_ic50)}")
通过以上规范操作,即可彻底规避NaN引发的检验失效问题,确保Mann-Whitney U检验结果真实反映“耐药肿瘤logIC₅₀是否显著高于敏感肿瘤”这一核心科学假设。

















