如何在 Pandas 中识别并移除“孤立值”(前后均为缺失值的单个非空值)

1次阅读

如何在 Pandas 中识别并移除“孤立值”(前后均为缺失值的单个非空值)

本文介绍一种基于 shift() 的高效方法,用于识别并清除 dataframe 中前后相邻行均为 nan 的“孤立非空值”,适用于数据清洗中常见的噪声点剔除场景。

本文介绍一种基于 shift() 的高效方法,用于识别并清除 dataframe 中前后相邻行均为 nan 的“孤立非空值”,适用于数据清洗中常见的噪声点剔除场景。

在时间序列或结构化表格数据中,偶尔会出现个别“突兀”的有效值——它本身非空,但其上一行和下一行在该列中均为缺失值(NaN)。这类值常被视为噪声或采集异常,需被置为 None(即 np.nan)以提升数据一致性。pandas 本身不提供直接的“孤立值检测”函数,但借助 shift() 方法可简洁、向量化地实现该逻辑。

核心思路:利用偏移比较上下文

判断某单元格是否为孤立值,关键在于检查其前一行(shift(1))与后一行(shift(-1))是否同时为 NaN。若成立,则当前值即为孤立值,应被替换。

以下为完整实现流程(以列 ‘A’ 为例,可同理扩展至其他列):

import pandas as pd import numpy as np  # 构造示例数据(注意:None 在 pandas 中自动转为 np.nan) df = pd.DataFrame({     "A": [None, 1, None, 1, 1, 1, 1],     "B": [1, 1, None, 1, None, 1, 1] })  # 步骤 1:生成前一行与后一行的值(按列操作) df["A_prev"] = df["A"].shift(1)   # 上一行值;首行变为 NaN df["A_next"] = df["A"].shift(-1) # 下一行值;末行变为 NaN  # 步骤 2:定位孤立值:当前非空 & 前为空 & 后为空 # 注意:使用 .notna() 判断“当前有值”,避免将原本就是 NaN 的行误判 mask_isolated = df["A"].notna() & df["A_prev"].isna() & df["A_next"].isna()  # 步骤 3:原地置空孤立值(推荐直接修改原列) df.loc[mask_isolated, "A"] = np.nan  # 清理辅助列(可选) df = df.drop(columns=["A_prev", "A_next"])  print(df)

输出结果:

A    B 0  NaN  1.0 1  NaN  1.0 2  NaN  NaN 3  1.0  1.0 4  1.0  NaN 5  1.0  1.0 6  1.0  1.0

✅ 对比原始数据可见:第 1 行(索引 1)的 A=1 被成功清除——因其前(索引 0)为 NaN、后(索引 2)也为 NaN,构成孤立值;而索引 3 的 A=1 未被清除,因其后(索引 4)为 1,不满足“前后均空”条件。

批量处理多列(推荐封装函数)

为提升复用性,可封装为通用函数,支持对指定列批量处理:

def remove_isolated_values(df: pd.DataFrame, columns: list) -> pd.DataFrame:     """     移除指定列中前后相邻行均为 NaN 的孤立非空值(原地修改)。      Parameters:     -----------     df : pd.DataFrame         输入 DataFrame     columns : list of str         需处理的列名列表      Returns:     --------     pd.DataFrame : 修改后的 DataFrame(返回引用,原 df 已变更)     """     for col in columns:         if col not in df.columns:             continue         prev = df[col].shift(1)         nxt = df[col].shift(-1)         mask = df[col].notna() & prev.isna() & nxt.isna()         df.loc[mask, col] = np.nan     return df  # 使用示例:同时清理列 A 和 B remove_isolated_values(df, columns=["A", "B"])

注意事项与边界说明

  • 首尾行天然不可孤立:由于首行无“前一行”、末行无“后一行”,它们的 shift() 结果恒为 NaN,因此需依赖 df[col].notna() 确保只处理当前有值的行,避免误清;
  • 缺失值类型统一:确保数据中缺失值为标准 np.nan(而非字符串 “None” 或空字符串 “”),否则 isna() 判断失效;
  • 性能优势:全程使用向量化操作,避免 apply() 或循环,适合大规模数据;
  • 扩展性:如需定义“近邻窗口”(如前后 2 行全空才视为孤立),可用 rolling().apply() 替代,但需权衡可读性与性能。

掌握此技巧,你便拥有了一个轻量、可靠的数据“去毛刺”工具,让清洗逻辑更精准、代码更健壮。

text=ZqhQzanResources