使用np.random.normal替换缺失NaN值时所有值相同,如何优化代码?
问题原因
你当前代码输出所有替换值相同的核心原因是循环逻辑错误:每遍历到一个符合范围的随机值,就调用replace把整列所有的0统一替换为当前值,后续遍历的随机值会不断覆盖之前的替换结果,最终整列所有待替换值都等于循环最后一个符合要求的随机值。另外你要替换的是NaN,代码里写的替换目标是0,二者也存在不匹配的问题。
优化方案
高效向量化实现(推荐)
不需要循环或者列表推导,用pandas向量化操作性能更高,逻辑也更清晰:
- 首先将列中字符串格式的"NaN"转为pandas可识别的空值,统计需要填充的空值数量
import pandas as pd import numpy as np # 转成数值类型,无效值(比如字符串"NaN")会自动转为np.nan data['Insulin'] = pd.to_numeric(data['Insulin'], errors='coerce') # 统计需要填充的空值总数 na_cnt = data['Insulin'].isna().sum()
- 生成符合0-400范围的随机值,数量和待填充空值数一致
mu, sigma = 155, 118 # 多生成50%的随机值避免筛选后数量不足 random_arr = np.random.normal(mu, sigma, int(na_cnt * 1.5)) # 筛选出符合范围的值,取前na_cnt个 valid_random = random_arr[(random_arr > 0) & (random_arr < 400)][:na_cnt]
- 直接给空值位置赋值
data.loc[data['Insulin'].isna(), 'Insulin'] = valid_random
列表推导实现(按需求)
如果一定要用列表推导实现,可以写成如下形式:
mu, sigma = 155, 118 data['Insulin'] = [ val if not pd.isna(val) # 每次取10个随机值直到拿到符合范围的,避免无限循环 else next(x for x in np.random.normal(mu, sigma, 10) if 0 < x < 400) for val in data['Insulin'] ]
注意事项
- 提前确认数据集里的缺失值是真实的
np.nan还是字符串格式的"NaN",后者需要先做类型转换才能被isna()识别 - 生成随机值时建议多生成一部分,避免正态分布生成的值超出范围后不够填充使用
- 非必要不建议用循环或者列表推导处理DataFrame列,向量化操作的执行效率会高很多
内容的提问来源于stack exchange,提问作者İzzet Türkalp Akbaşlı
相关产品推荐
相关产品推荐

