检测并排除 pandas DataFrame 中的异常值

2024-12-12 08:40:00
admin
原创
169
摘要:问题描述:我有一个包含几列的 pandas 数据框。现在我知道根据某个列值,某些行是异常值。例如,列的Vol所有值都在 12xx 左右,并且一个值为 4000(异常值)。我想排除那些具有Vol此类列的行。因此,本质上我需要在数据框上放置一个过滤器,以便我们选择某一列的值在平均值 3 个标准差以内的所有行。有什...

问题描述:

我有一个包含几列的 pandas 数据框。现在我知道根据某个列值,某些行是异常值。例如,列的Vol所有值都在 12xx 左右,并且一个值为 4000(异常值)。我想排除那些具有Vol此类列的行。

因此,本质上我需要在数据框上放置一个过滤器,以便我们选择某一列的值在平均值 3 个标准差以内的所有行。

有什么优雅的方法可以实现这一目标?


解决方案 1:

使用scipy.stats.zscore

删除至少有一列包含异常值的所有行

如果您的数据框中有多个列,并且想要删除至少一列中有异常值的所有行,则以下表达式可以一次性完成此操作:

import pandas as pd
import numpy as np
from scipy import stats

df = pd.DataFrame(np.random.randn(100, 3))

df[(np.abs(stats.zscore(df)) < 3).all(axis=1)]

描述:

  • 对于每一列,它首先计算列中每个值相对于列平均值和标准差的 Z 分数。

  • 然后它采用绝对 Z 分数,因为方向并不重要,只要它低于阈值即可。

  • ( < 3).all(axis=1)检查每一行的所有列值是否都在平均值的 3 个标准差范围内

  • 最后,该条件的结果用于索引数据框。

根据单个列过滤其他列

与上面相同,但是为 指定一列zscoredf[0]例如,并删除.all(axis=1)

df[np.abs(stats.zscore(df[0])) < 3]

解决方案 2:

对于每个数据框列,您可以使用以下方法获取分位数:

q = df["col"].quantile(0.99)

然后过滤:

df[df["col"] < q]

如果需要删除下限和上限异常值,请将条件与 AND 语句结合起来:

q_low = df["col"].quantile(0.01)
q_hi  = df["col"].quantile(0.99)

df_filtered = df[(df["col"] < q_hi) & (df["col"] > q_low)]

解决方案 3:

使用boolean索引,就像在numpy.array

df = pd.DataFrame({'Data':np.random.normal(size=200)})
# example dataset of normally distributed data. 

df[np.abs(df.Data-df.Data.mean()) <= (3*df.Data.std())]
# keep only the ones that are within +3 to -3 standard deviations in the column 'Data'.

df[~(np.abs(df.Data-df.Data.mean()) > (3*df.Data.std()))]
# or if you prefer the other way around

对于一个系列来说,情况类似:

S = pd.Series(np.random.normal(size=200))
S[~((S-S.mean()).abs() > 3*S.std())]

解决方案 4:

在回答实际问题之前,我们应该根据您的数据的性质询问另一个非常相关的问题:

什么是异常值?

想象一系列值[3, 2, 3, 4, 999](其中999似乎不适合)并分析各种异常值检测方法

Z 分数

这里的问题是,所讨论的值严重扭曲了我们的测量值meanstd导致 z 分数不明显,大约为[-0.5, -0.5, -0.5, -0.5, 2.0],使每个值保持在平均值的两个标准差内。因此,一个非常大的异常值可能会扭曲您对异常值的整个评估。我不鼓励这种方法。

分位数过滤器

给出的更稳健的方法是这个答案,消除底部和顶部 1% 的数据。但是,如果这些数据真的是异常值,这会消除与问题无关的固定分数。您可能会丢失大量有效数据,另一方面,如果您的数据中有超过 1% 或 2% 是异常值,您仍会保留一些异常值。

IQR-与中位数的距离

分位数原理的更稳健版本:消除距离数据中f位数超过四分位距倍数sklearn所有数据。这也是使用的转换RobustScaler。IQR 和中位数对异常值具有稳健性,因此您可以巧妙地解决 z 分数方法的问题。

在正态分布中,我们大致有iqr=1.35*s,因此您可以将z=3z 分数过滤器的转换为f=2.22iqr 过滤器的。这将删除999上例中的。

基本假设是,至少数据的“中间一半”是有效的并且与分布很相似,而如果分布的尾部较宽且 q_25% 到 q_75% 间隔较窄,那么您也会搞砸。

高级统计方法

当然,还有一些奇特的数学方法,例如Peirce 标准、Grubb 检验或Dixon Q 检验,这些方法也适用于非正态分布的数据。这些方法都不容易实现,因此不再赘述。

代码

np.nan用示例数据框替换所有数值列的所有异常值。该方法对 Pandas 提供的所有 dtype都很稳健,并且可以轻松应用于混合类型的数据框:

import pandas as pd
import numpy as np                                     

# sample data of all dtypes in pandas (column 'a' has an outlier)         # dtype:
df = pd.DataFrame({'a': list(np.random.rand(8)) + [123456, np.nan],       # float64
                   'b': [0,1,2,3,np.nan,5,6,np.nan,8,9],                  # int64
                   'c': [np.nan] + list("qwertzuio"),                     # object
                   'd': [pd.to_datetime(_) for _ in range(10)],           # datetime64[ns]
                   'e': [pd.Timedelta(_) for _ in range(10)],             # timedelta[ns]
                   'f': [True] * 5 + [False] * 5,                         # bool
                   'g': pd.Series(list("abcbabbcaa"), dtype="category")}) # category
cols = df.select_dtypes('number').columns  # limits to a (float), b (int) and e (timedelta)
df_sub = df.loc[:, cols]


# OPTION 1: z-score filter: z-score < 3
lim = np.abs((df_sub - df_sub.mean()) / df_sub.std(ddof=0)) < 3

# OPTION 2: quantile filter: discard 1% upper / lower values
lim = np.logical_and(df_sub < df_sub.quantile(0.99, numeric_only=False),
                     df_sub > df_sub.quantile(0.01, numeric_only=False))

# OPTION 3: iqr filter: within 2.22 IQR (equiv. to z-score < 3)
iqr = df_sub.quantile(0.75, numeric_only=False) - df_sub.quantile(0.25, numeric_only=False)
lim = np.abs((df_sub - df_sub.median()) / iqr) < 2.22


# replace outliers with nan
df.loc[:, cols] = df_sub.where(lim, np.nan)

删除包含至少一个 nan 值的所有行:

df.dropna(subset=cols, inplace=True) # drop rows with NaN in numerical columns
# or
df.dropna(inplace=True)  # drop rows with NaN in any column

使用 pandas 1.3 函数:

  • pandas.DataFrame.select_dtypes()

  • pandas.DataFrame.quantile()

  • pandas.DataFrame.where()

  • pandas.DataFrame.dropna()

解决方案 5:

这个答案与@tanemaki 提供的答案类似,但使用lambda表达式而不是scipy stats

df = pd.DataFrame(np.random.randn(100, 3), columns=list('ABC'))

standard_deviations = 3
df[df.apply(lambda x: np.abs(x - x.mean()) / x.std() < standard_deviations)
   .all(axis=1)]

要过滤仅一列(例如'B')在三个标准差内的DataFrame:

df[((df['B'] - df['B'].mean()) / df['B'].std()).abs() < standard_deviations]

请参阅此处以了解如何滚动应用此 z 分数:将滚动 Z 分数应用于 Pandas 数据框

解决方案 6:

#------------------------------------------------------------------------------
# accept a dataframe, remove outliers, return cleaned data in a new dataframe
# see http://www.itl.nist.gov/div898/handbook/prc/section1/prc16.htm
#------------------------------------------------------------------------------
def remove_outlier(df_in, col_name):
    q1 = df_in[col_name].quantile(0.25)
    q3 = df_in[col_name].quantile(0.75)
    iqr = q3-q1 #Interquartile range
    fence_low  = q1-1.5*iqr
    fence_high = q3+1.5*iqr
    df_out = df_in.loc[(df_in[col_name] > fence_low) & (df_in[col_name] < fence_high)]
    return df_out

解决方案 7:

由于我还没有看到处理数字非数字属性的答案,所以这里有一个补充答案。

您可能只想删除数值属性上的异常值(分类变量几乎不能是异常值)。

函数定义

我扩展了@tanemaki 的建议,以在存在非数字属性时处理数据:

from scipy import stats

def drop_numerical_outliers(df, z_thresh=3):
    # Constrains will contain `True` or `False` depending on if it is a value below the threshold.
    constrains = df.select_dtypes(include=[np.number]) \n        .apply(lambda x: np.abs(stats.zscore(x)) < z_thresh, reduce=False) \n        .all(axis=1)
    # Drop (inplace) values set to be rejected
    df.drop(df.index[~constrains], inplace=True)

用法

drop_numerical_outliers(df)

例子

想象一个df包含一些有关房屋的值的数据集:小巷、土地轮廓、销售价格……例如:数据文档

首先,您想要在散点图上可视化数据(z 分数 Thresh=3):

# Plot data before dropping those greater than z-score 3. 
# The scatterAreaVsPrice function's definition has been removed for readability's sake.
scatterAreaVsPrice(df)

之前 - Gr Liv 面积与销售价格对比

# Drop the outliers on every attributes
drop_numerical_outliers(train_df)

# Plot the result. All outliers were dropped. Note that the red points are not
# the same outliers from the first plot, but the new computed outliers based on the new data-frame.
scatterAreaVsPrice(train_df)

之后 - Gr Liv 面积与销售价格

解决方案 8:

对于数据框中的每个系列,您可以使用betweenquantile删除异常值。

x = pd.Series(np.random.normal(size=200)) # with outliers
x = x[x.between(x.quantile(.25), x.quantile(.75))] # without outliers

解决方案 9:

scipy.stats有方法trim1()trimboth()根据排名和引入的删除值百分比将异常值从单行中切除。

解决方案 10:

如果您喜欢方法链,您可以像这样获取所有数字列的布尔条件:

df.sub(df.mean()).div(df.std()).abs().lt(3)

True/False每列的每个值都将根据其距离平均值是否小于三个标准差进行转换。

解决方案 11:

另一个选择是转换数据,以减轻异常值的影响。您可以通过对数据进行缩尾处理来实现这一点。

import pandas as pd
from scipy.stats import mstats
%matplotlib inline

test_data = pd.Series(range(30))
test_data.plot()

原始数据

# Truncate values to the 5th and 95th percentiles
transformed_test_data = pd.Series(mstats.winsorize(test_data, limits=[0.05, 0.05])) 
transformed_test_data.plot()

缩尾数据

解决方案 12:

您可以使用布尔掩码:

import pandas as pd

def remove_outliers(df, q=0.05):
    upper = df.quantile(1-q)
    lower = df.quantile(q)
    mask = (df < upper) & (df > lower)
    return mask

t = pd.DataFrame({'train': [1,1,2,3,4,5,6,7,8,9,9],
                  'y': [1,0,0,1,1,0,0,1,1,1,0]})

mask = remove_outliers(t['train'], 0.1)

print(t[mask])

输出:

   train  y
2      2  0
3      3  1
4      4  1
5      5  0
6      6  0
7      7  1
8      8  1

解决方案 13:

由于我的数据科学之旅还处于早期阶段,因此我使用下面的代码来处理异常值。

#Outlier Treatment

def outlier_detect(df):
    for i in df.describe().columns:
        Q1=df.describe().at['25%',i]
        Q3=df.describe().at['75%',i]
        IQR=Q3 - Q1
        LTV=Q1 - 1.5 * IQR
        UTV=Q3 + 1.5 * IQR
        x=np.array(df[i])
        p=[]
        for j in x:
            if j < LTV or j>UTV:
                p.append(df[i].median())
            else:
                p.append(j)
        df[i]=p
    return df

解决方案 14:

将第 98 和第 2 个百分位数作为异常值的限度

upper_limit = np.percentile(X_train.logerror.values, 98) 
lower_limit = np.percentile(X_train.logerror.values, 2) # Filter the outliers from the dataframe
data[‘target’].loc[X_train[‘target’]>upper_limit] = upper_limit data[‘target’].loc[X_train[‘target’]<lower_limit] = lower_limit

解决方案 15:

以下是包含数据和 2 个组的完整示例:

进口:

from StringIO import StringIO
import pandas as pd
#pandas config
pd.set_option('display.max_rows', 20)

包含 2 个组的数据示例:G1:第 1 组。G2:第 2 组:

TESTDATA = StringIO("""G1;G2;Value
1;A;1.6
1;A;5.1
1;A;7.1
1;A;8.1

1;B;21.1
1;B;22.1
1;B;24.1
1;B;30.6

2;A;40.6
2;A;51.1
2;A;52.1
2;A;60.6

2;B;80.1
2;B;70.6
2;B;90.6
2;B;85.1
""")

将文本数据读取到 Pandas 数据框中:

df = pd.read_csv(TESTDATA, sep=";")

使用标准差定义异常值

stds = 1.0
outliers = df[['G1', 'G2', 'Value']].groupby(['G1','G2']).transform(
           lambda group: (group - group.mean()).abs().div(group.std())) > stds

定义过滤的数据值和异常值:

dfv = df[outliers.Value == False]
dfo = df[outliers.Value == True]

打印结果:

print '
'*5, 'All values with decimal 1 are non-outliers. In the other hand, all values with 6 in the decimal are.'
print '
Def DATA:
%s

Filtred Values with %s stds:
%s

Outliers:
%s' %(df, stds, dfv, dfo)

解决方案 16:

我的剔除异常值函数

def drop_outliers(df, field_name):
    distance = 1.5 * (np.percentile(df[field_name], 75) - np.percentile(df[field_name], 25))
    df.drop(df[df[field_name] > distance + np.percentile(df[field_name], 75)].index, inplace=True)
    df.drop(df[df[field_name] < np.percentile(df[field_name], 25) - distance].index, inplace=True)

解决方案 17:

我更喜欢剪辑而不是放弃。以下内容将在第 2 和第 98 个百分位数处剪辑到位。

df_list = list(df)
minPercentile = 0.02
maxPercentile = 0.98

for _ in range(numCols):
    df[df_list[_]] = df[df_list[_]].clip((df[df_list[_]].quantile(minPercentile)),(df[df_list[_]].quantile(maxPercentile)))

解决方案 18:

我认为删除和丢弃异常值在统计上是错误的。它使数据与原始数据不同。也使数据形状不均匀,因此最好的方法是通过对数据进行对数变换来减少或避免异常值的影响。这对我有用:

np.log(data.iloc[:, :])
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用