【问题标题】:pandas: complex filter on rows of DataFramepandas:对 DataFrame 行的复杂过滤器
【发布时间】:2020-06-15 21:21:01
【问题描述】:

我想通过每行的函数过滤行,例如

def f(row):
  return sin(row['velocity'])/np.prod(['masses']) > 5

df = pandas.DataFrame(...)
filtered = df[apply_to_all_rows(df, f)]

或者对于另一个更复杂、人为的例子,

def g(row):
  if row['col1'].method1() == 1:
    val = row['col1'].method2() / row['col1'].method3(row['col3'], row['col4'])
  else:
    val = row['col2'].method5(row['col6'])
  return np.sin(val)

df = pandas.DataFrame(...)
filtered = df[apply_to_all_rows(df, g)]

我该怎么做?

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    您可以使用DataFrame.apply 执行此操作,它沿给定轴应用函数,

    In [3]: df = pandas.DataFrame(np.random.randn(5, 3), columns=['a', 'b', 'c'])
    
    In [4]: df
    Out[4]: 
              a         b         c
    0 -0.001968 -1.877945 -1.515674
    1 -0.540628  0.793913 -0.983315
    2 -1.313574  1.946410  0.826350
    3  0.015763 -0.267860 -2.228350
    4  0.563111  1.195459  0.343168
    
    In [6]: df[df.apply(lambda x: x['b'] > x['c'], axis=1)]
    Out[6]: 
              a         b         c
    1 -0.540628  0.793913 -0.983315
    2 -1.313574  1.946410  0.826350
    3  0.015763 -0.267860 -2.228350
    4  0.563111  1.195459  0.343168
    

    【讨论】:

    • 在这种情况下不需要apply。常规布尔索引可以正常工作。 df[df['b] > df['c']]。真正需要apply 的情况很少,甚至很少需要axis=1
    • @TedPetrou 如果您不确定数据框中的每个元素的类型是否正确,该怎么办。常规布尔索引是否支持异常处理?
    【解决方案2】:

    假设我有一个如下的 DataFrame:

    In [39]: df
    Out[39]: 
          mass1     mass2  velocity
    0  1.461711 -0.404452  0.722502
    1 -2.169377  1.131037  0.232047
    2  0.009450 -0.868753  0.598470
    3  0.602463  0.299249  0.474564
    4 -0.675339 -0.816702  0.799289
    

    我可以使用 sin 和 DataFrame.prod 创建一个布尔掩码:

    In [40]: mask = (np.sin(df.velocity) / df.ix[:, 0:2].prod(axis=1)) > 0
    
    In [41]: mask
    Out[41]: 
    0    False
    1    False
    2    False
    3     True
    4     True
    

    然后使用掩码从DataFrame中进行选择:

    In [42]: df[mask]
    Out[42]: 
          mass1     mass2  velocity
    3  0.602463  0.299249  0.474564
    4 -0.675339 -0.816702  0.799289
    

    【讨论】:

    • 实际上,这可能是一个不好的例子:np.sin 自动广播到所有元素。如果我将其替换为一次只能处理一个输入的不太智能的函数会怎样?
    【解决方案3】:

    指定 reduce=True 也可以处理空的 DataFrame。

    import pandas as pd
    
    t = pd.DataFrame(columns=['a', 'b'])
    t[t.apply(lambda x: x['a'] > 1, axis=1, reduce=True)]
    

    https://crosscompute.com/n/jAbsB6OIm6oCCJX9PBIbY5FECFKCClyV/-/apply-custom-filter-on-rows-of-dataframe

    【讨论】:

      【解决方案4】:

      我发现的最佳方法是,不要使用 reduce=True 来避免空 df 的错误(因为无论如何不推荐使用此 arg),只需在应用过滤器之前检查 df size > 0:

      def my_filter(row):
          if row.columnA == something:
              return True
      
          return False
      
      if len(df.index) > 0:
          df[df.apply(my_filter, axis=1)]
      

      【讨论】:

        【解决方案5】:

        我无法对duckworthd's answer 发表评论,但它并不完美。数据框为空时崩溃:

        df = pandas.DataFrame(columns=['a', 'b', 'c'])
        df[df.apply(lambda x: x['b'] > x['c'], axis=1)]
        

        输出:

        ValueError: Must pass DataFrame with boolean values only
        

        对我来说,这看起来像是 pandas 中的一个错误,因为 { } 绝对是一组有效的布尔值。如需解决方案,请参阅Roy Hyunjin Han's answer

        【讨论】:

          【解决方案6】:

          您可以使用loc 属性对数据框进行切片。

          根据documentationloc 可以有 callable function 作为参数。

          In [3]: df = pandas.DataFrame(np.random.randn(5, 3), columns=['a', 'b', 'c'])
          
          In [4]: df
          Out[4]: 
                    a         b         c
          0 -0.001968 -1.877945 -1.515674
          1 -0.540628  0.793913 -0.983315
          2 -1.313574  1.946410  0.826350
          3  0.015763 -0.267860 -2.228350
          4  0.563111  1.195459  0.343168
          
          # define lambda function
          In [5]: myfilter = lambda x: x['b'] > x['c']
          
          # use my lambda in loc
          In [6]: df1 = df.loc[fif]
          

          如果您想将您的过滤功能 fif 与其他过滤条件结合起来

          df1 = df.loc[fif].loc[(df.b >= 0.5)]
          

          【讨论】:

            猜你喜欢
            • 2013-01-29
            • 1970-01-01
            • 1970-01-01
            • 2013-09-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-08-25
            相关资源
            最近更新 更多