Pandas DataFrame多列移除异常值:apply函数报错及循环实现求助
问题与解决方案
问题背景
我有一个大型Pandas DataFrame,以下是可复现的示例数据:
data = {'X1': [3,5,8,3,5,19,5], 'X2': [2,7,9,1,14,2,10], 'X3': [1,8,23,14,4,9,5], 'X4': [5,3,1,6,9,23,12]} df = pd.DataFrame.from_dict(data)
我想给所有特征移除异常值,试了apply函数但报错,同时也想知道怎么用for循环实现。下面是我写的两段代码:
第一段:apply函数尝试代码
def remove(df): for i in df: Q1 = df[i].quantile(0.25) Q3 = df[i].quantile(0.75) IQR = Q3 - Q1 lower = (Q1 - 1.5*IQR) upper = (Q3 + 1.5*IQR) upper_array = np.where(df[i] >= upper)[0] lower_array = np.where(df[i] <= lower)[0] df.drop(index=upper_array, inplace=True) df.drop(index=lower_array, inplace=True) return df df = df.apply(remove,axis=1) df.head()
第二段:for循环尝试代码
lower = [] upper = [] for i in df: Q1 = df[i].quantile(0.25) Q3 = df[i].quantile(0.75) IQR = Q3 - Q1 lower.append(Q1 - 1.5*IQR) upper.append(Q3 + 1.5*IQR) for i in df: upper_array = np.where(df[i] >= upper)[0] lower_array = np.where(df[i] <= lower)[0] df.drop(index=upper_array, inplace=True) df.drop(index=lower_array, inplace=True)
错误分析与修复
一、apply代码的问题
你用df.apply(remove, axis=1)时,axis=1会把每一行作为Series传入remove函数,但你的函数是针对整个DataFrame的列计算分位数,逻辑完全不匹配,肯定报错。另外,函数里的循环也有问题:每次循环处理一列,但你在循环外才执行drop,最后只会保留最后一列的异常值索引,前面列的处理全被覆盖了。
正确实现(不用apply更高效)
其实不需要用apply,直接对整个DataFrame计算每列的上下界,然后过滤行:
import pandas as pd import numpy as np data = {'X1': [3,5,8,3,5,19,5], 'X2': [2,7,9,1,14,2,10], 'X3': [1,8,23,14,4,9,5], 'X4': [5,3,1,6,9,23,12]} df = pd.DataFrame.from_dict(data) # 计算所有列的Q1、Q3和IQR Q1 = df.quantile(0.25) Q3 = df.quantile(0.75) IQR = Q3 - Q1 # 定义每列的异常值上下界 lower_bound = Q1 - 1.5 * IQR upper_bound = Q3 + 1.5 * IQR # 过滤掉任意一列超出上下界的行 df_filtered = df[~((df < lower_bound) | (df > upper_bound)).any(axis=1)] print(df_filtered)
二、for循环代码的问题
- 你把每列的上下界存在列表
lower和upper里,但后续循环时,df[i] >= upper是把列和整个列表比较,维度不匹配,直接报错。 - 第二个循环里,每次循环都会覆盖
upper_array和lower_array,最后只会处理最后一列的异常值,前面列的异常值完全没被处理。 - 直接用
inplace=True多次drop会导致索引混乱,因为drop后行索引会变化,后续drop的索引可能对应错误的行。
正确的for循环实现
先收集所有需要删除的行索引,最后一次性删除:
import pandas as pd import numpy as np data = {'X1': [3,5,8,3,5,19,5], 'X2': [2,7,9,1,14,2,10], 'X3': [1,8,23,14,4,9,5], 'X4': [5,3,1,6,9,23,12]} df = pd.DataFrame.from_dict(data) # 用集合存要删除的索引,自动去重 drop_indices = set() for col in df.columns: Q1 = df[col].quantile(0.25) Q3 = df[col].quantile(0.75) IQR = Q3 - Q1 lower = Q1 - 1.5 * IQR upper = Q3 + 1.5 * IQR # 找到当前列的异常值索引 upper_indices = df[df[col] >= upper].index lower_indices = df[df[col] <= lower].index # 加入待删除集合 drop_indices.update(upper_indices) drop_indices.update(lower_indices) # 一次性删除所有异常值行 df_filtered = df.drop(drop_indices) print(df_filtered)
内容的提问来源于stack exchange,提问作者Raagib khan
相关产品推荐
相关产品推荐

