【问题标题】:Remove Outliers in Pandas DataFrame using Percentiles [duplicate]使用百分位数删除 Pandas DataFrame 中的异常值 [重复]
【发布时间】:2016-06-20 01:35:09
【问题描述】:

我有一个包含 40 列和许多记录的 DataFrame df。

df:

User_id | Col1 | Col2 | Col3 | Col4 | Col5 | Col6 | Col7 |...| Col39

对于除 user_id 列之外的每一列,如果出现异常值,我想检查异常值并删除整条记录。

对于每一行的异常值检测,我决定简单地使用第 5 和第 95 个百分位数(我知道这不是最好的统计方法):

编码我目前所拥有的:

P = np.percentile(df.Col1, [5, 95])
new_df = df[(df.Col1 > P[0]) & (df.Col1 < P[1])]

问题:如何在不手动操作的情况下将此方法应用于所有列(User_id 除外)?我的目标是获得一个没有异常值记录的数据框。

谢谢!

【问题讨论】:

    标签: python pandas outliers


    【解决方案1】:

    要基于单个列修剪整个 DataFrame,这是一种更简单的方法。排序后从顶部和底部删除n行。

    nb_to_trim = round(len(df.index) * 0.05)
    df = df.sort_values(col1).iloc[nb_to_trim:-nb_to_trim, :]
    

    【讨论】:

      【解决方案2】:

      使用此代码,不要浪费您的时间:

      Q1 = df.quantile(0.25)
      Q3 = df.quantile(0.75)
      IQR = Q3 - Q1
      
      df = df[~((df < (Q1 - 1.5 * IQR)) |(df > (Q3 + 1.5 * IQR))).any(axis=1)]
      

      如果您需要特定的列:

      cols = ['col_1', 'col_2'] # one or more
      
      Q1 = df[cols].quantile(0.25)
      Q3 = df[cols].quantile(0.75)
      IQR = Q3 - Q1
      
      df = df[~((df[cols] < (Q1 - 1.5 * IQR)) |(df[cols] > (Q3 + 1.5 * IQR))).any(axis=1)]
      

      【讨论】:

      • 没有足够的支持来表达我对您发布此内容的感谢。
      • 谢谢,这很有帮助
      • 为什么 + 和 - 1.5* IQR?
      • @Carl 异常值是超出四分位数范围 (Q3 - Q1) 1.5 倍的数据点。所以 + 和 - 1.5*IQR 意味着我们正在考虑约束范围内的数据
      • 出色的解决方案,谢谢!
      【解决方案3】:

      您所描述的内容类似于 Winsorizing 的过程,它会剪切值(例如,在第 5 和第 95 个百分位数)而不是完全消除它们。

      这是一个例子:

      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()
      

      【讨论】:

        【解决方案4】:

        初始数据集。

        print(df.head())
        
           Col0  Col1  Col2  Col3  Col4  User_id
        0    49    31    93    53    39       44
        1    69    13    84    58    24       47
        2    41    71     2    43    58       64
        3    35    56    69    55    36       67
        4    64    24    12    18    99       67
        

        首先删除User_id

        filt_df = df.loc[:, df.columns != 'User_id']
        

        然后,计算百分位数。

        low = .05
        high = .95
        quant_df = filt_df.quantile([low, high])
        print(quant_df)
        
               Col0   Col1  Col2   Col3   Col4
        0.05   2.00   3.00   6.9   3.95   4.00
        0.95  95.05  89.05  93.0  94.00  97.05
        

        接下来根据计算的百分位数过滤值。为此,我按列使用apply,就是这样!

        filt_df = filt_df.apply(lambda x: x[(x>quant_df.loc[low,x.name]) & 
                                            (x < quant_df.loc[high,x.name])], axis=0)
        

        User_id 带回来。

        filt_df = pd.concat([df.loc[:,'User_id'], filt_df], axis=1)
        

        最后,具有NaN 值的行可以像这样简单地删除。

        filt_df.dropna(inplace=True)
        print(filt_df.head())
        
           User_id  Col0  Col1  Col2  Col3  Col4
        1       47    69    13    84    58    24
        3       67    35    56    69    55    36
        5        9    95    79    44    45    69
        6       83    69    41    66    87     6
        9       87    50    54    39    53    40
        

        检查结果

        print(filt_df.head())
        
           User_id  Col0  Col1  Col2  Col3  Col4
        0       44    49    31   NaN    53    39
        1       47    69    13    84    58    24
        2       64    41    71   NaN    43    58
        3       67    35    56    69    55    36
        4       67    64    24    12    18   NaN
        
        print(filt_df.describe())
        
                  User_id       Col0       Col1       Col2       Col3       Col4
        count  100.000000  89.000000  88.000000  88.000000  89.000000  89.000000
        mean    48.230000  49.573034  45.659091  52.727273  47.460674  57.157303
        std     28.372292  25.672274  23.537149  26.509477  25.823728  26.231876
        min      0.000000   3.000000   5.000000   7.000000   4.000000   5.000000
        25%     23.000000  29.000000  29.000000  29.500000  24.000000  36.000000
        50%     47.000000  50.000000  40.500000  52.500000  49.000000  59.000000
        75%     74.250000  69.000000  67.000000  75.000000  70.000000  79.000000
        max     99.000000  95.000000  89.000000  92.000000  91.000000  97.000000
        

        如何生成测试数据集

        np.random.seed(0)
        nb_sample = 100
        num_sample = (0,100)
        
        d = dict()
        d['User_id'] = np.random.randint(num_sample[0], num_sample[1], nb_sample)
        for i in range(5):
            d['Col' + str(i)] = np.random.randint(num_sample[0], num_sample[1], nb_sample)
        
        df = DataFrame.from_dict(d)
        

        【讨论】:

        • 成功了!但就我而言,我不得不改变 (lambda x: x[(x>quant_df.loc[low,x.name]) & (x = quant_df.loc[low,x.name]) & (x
        • 太棒了!除了换行之外,我看不出两个 lambda 之间的区别。
        • 我用 ">=" 和 "" 和 "
        • 在那之后为什么我们会得到很多原始数据集中不存在的“NaN”。我们该如何处理?
        • 但是行不会被打乱到不同的列吗?
        【解决方案5】:

        使用内连接。像这样的东西应该可以工作

        cols = df.columns.tolist()
        cols.remove('user_id') #remove user_id from list of columns
        
        P = np.percentile(df[cols[0]], [5, 95])
        new_df = df[(df[cols[0] > P[0]) & (df[cols[0]] < P[1])]
        for col in cols[1:]:
            P = np.percentile(df[col], [5, 95])
            new_df = new_df.join(df[(df[col] > P[0]]) & (df[col] < P[1])], how='inner')
        

        【讨论】:

          猜你喜欢
          • 2017-11-20
          • 2018-09-05
          • 2019-03-22
          • 2021-01-12
          • 2016-06-15
          • 1970-01-01
          • 1970-01-01
          • 2023-03-28
          • 1970-01-01
          相关资源
          最近更新 更多