【问题标题】:how to use pandas filter with IQR如何使用带有 IQR 的 pandas 过滤器
【发布时间】:2016-01-14 05:03:34
【问题描述】:

是否有内置方法可以按 IQR(即 Q1-1.5IQR 和 Q3+1.5IQR 之间的值)对列进行过滤? 另外,建议使用 pandas 中任何其他可能的广义过滤。

【问题讨论】:

    标签: python pandas data-processing iqr


    【解决方案1】:

    据我所知,最紧凑的符号似乎是由query 方法带来的。

    # Some test data
    np.random.seed(33454)
    df = (
        # A standard distribution
        pd.DataFrame({'nb': np.random.randint(0, 100, 20)})
            # Adding some outliers
            .append(pd.DataFrame({'nb': np.random.randint(100, 200, 2)}))
            # Reseting the index
            .reset_index(drop=True)
        )
    
    # Computing IQR
    Q1 = df['nb'].quantile(0.25)
    Q3 = df['nb'].quantile(0.75)
    IQR = Q3 - Q1
    
    # Filtering Values between Q1-1.5IQR and Q3+1.5IQR
    filtered = df.query('(@Q1 - 1.5 * @IQR) <= nb <= (@Q3 + 1.5 * @IQR)')
    

    然后我们可以绘制结果来检查差异。我们观察到左侧箱线图中的异常值(183 处的十字)不再出现​​在过滤后的系列中。

    # Ploting the result to check the difference
    df.join(filtered, rsuffix='_filtered').boxplot()
    

    自从有了这个答案,我就这个话题写了post,如果你可以找到更多信息。

    【讨论】:

    • 我的 DF 有很多列,我只想绘制其中的一列(例如“nb”)。 df.join(filtered, rsuffix='_filtered').boxplot() 对此不起作用。
    • 这个解决方案可以用Q1, Q3 = df['nb'].quantile([.25, .75])来缩短
    【解决方案2】:

    使用Series.between()的另一种方法:

    iqr = df['col'][df['col'].between(df['col'].quantile(.25), df['col'].quantile(.75), inclusive=True)]
    

    抽出:

    # Select the first quantile
    q1 = df['col'].quantile(.25)
    
    # Select the third quantile
    q3 = df['col'].quantile(.75)
    
    # Create a mask inbeetween q1 & q3
    mask = df['col'].between(q1, q3, inclusive=True)
    
    # Filtering the initial dataframe with a mask
    iqr = df.loc[mask, 'col']
            
    

    【讨论】:

    • 要清楚,这将返回第 25 和第 75 个百分位数(Q1 和 Q3)之间的值。它过滤 Q1-1.5IQR 和 Q3+1.5IQR。因此,如果您想使用 Q1-1.5IQR 和 Q3+1.5IQR 进行异常值分类:请在此处使用其他灵魂之一。
    【解决方案3】:

    这将为您提供df 的子集,它位于column 列的 IQR:

    def subset_by_iqr(df, column, whisker_width=1.5):
        """Remove outliers from a dataframe by column, including optional 
           whiskers, removing rows for which the column value are 
           less than Q1-1.5IQR or greater than Q3+1.5IQR.
        Args:
            df (`:obj:pd.DataFrame`): A pandas dataframe to subset
            column (str): Name of the column to calculate the subset from.
            whisker_width (float): Optional, loosen the IQR filter by a
                                   factor of `whisker_width` * IQR.
        Returns:
            (`:obj:pd.DataFrame`): Filtered dataframe
        """
        # Calculate Q1, Q2 and IQR
        q1 = df[column].quantile(0.25)                 
        q3 = df[column].quantile(0.75)
        iqr = q3 - q1
        # Apply filter with respect to IQR, including optional whiskers
        filter = (df[column] >= q1 - whisker_width*iqr) & (df[column] <= q3 + whisker_width*iqr)
        return df.loc[filter]                                                     
    
    # Example for whiskers = 1.5, as requested by the OP
    df_filtered = subset_by_iqr(df, 'column_name', whisker_width=1.5)
    

    【讨论】:

    • 请更新您的公式,因为 IQR 是第 25 个和第 75 个百分位值。但是在删除时,我们会删除小于 q1-1.5IQR 或大于 q3+1.5IQR 的值
    • @MNA 你把&gt;=&lt;=改成&gt;&lt;吗?
    • 这有什么帮助?
    • 啊,你的意思是包括宽度为1.5的胡须。我现在将这些作为一个选项包含在内,因为这是一个非常依赖于数据集的超参数。
    • 谢谢。我问这个是因为如果你使用whisker_width = 0,那么你最终会删除一些数据。此外,whisker_width = 1.5 是标准做法。
    【解决方案4】:

    使用df.quantile 查找第一个和第三个四分位数,然后在数据帧上使用掩码。 如果您想删除它们,请使用no_outliers 并反转掩码中的条件以获得outliers

    Q1 = df.col.quantile(0.25)
    Q3 = df.col.quantile(0.75)
    IQR = Q3 - Q1
    no_outliers = df.col[(Q1 - 1.5*IQR < df.BMI) &  (df.BMI < Q3 + 1.5*IQR)]
    outliers = df.col[(Q1 - 1.5*IQR >= df.BMI) |  (df.BMI >= Q3 + 1.5*IQR)]
    

    【讨论】:

      【解决方案5】:

      另一种方法使用 Series.clip:

      q = s.quantile([.25, .75])
      s = s[~s.clip(*q).isin(q)]
      

      详情如下:

      s = pd.Series(np.randon.randn(100))
      q = s.quantile([.25, .75])  # calculate lower and upper bounds
      s = s.clip(*q)  # assigns values outside boundary to boundary values
      s = s[~s.isin(q)]  # take only observations within bounds
      

      使用它来过滤整个数据框df 很简单:

      def iqr(df, colname, bounds = [.25, .75]):
          s = df[colname]
          q = s.quantile(bounds)
          return df[~s.clip(*q).isin(q)]
      

      注意:该方法本身不包括边界。

      【讨论】:

        【解决方案6】:

        您也可以通过计算 IQR 来尝试使用以下代码。基于 IQR、下限和上限,它将替换每列中显示的异常值。此代码将遍历数据框中的每一列,并通过单独过滤异常值来逐一工作,而不是遍历行中的所有值来查找异常值。

        功能:

            def mod_outlier(df):
                df1 = df.copy()
                df = df._get_numeric_data()
        
        
                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)
        
        
                for col in col_vals:
                    for i in range(0,len(df[col])):
                        if df[col][i] < lower_bound[col]:            
                            df[col][i] = lower_bound[col]
        
                        if df[col][i] > upper_bound[col]:            
                            df[col][i] = upper_bound[col]    
        
        
                for col in col_vals:
                    df1[col] = df[col]
        
                return(df1)
        

        函数调用:

        df = mod_outlier(df)
        

        【讨论】:

        • 我收到一个错误:NameError: name 'col_vals' is not defined for that.
        • col_vals 只是列 avalailable ,为此输入一个条目,或者在代码中将 col_vals 替换为 df.columns
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-02
        • 2015-12-08
        • 2019-03-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多