【问题标题】:pandas: filter rows of DataFrame with operator chainingpandas:使用运算符链接过滤 DataFrame 的行
【发布时间】:2012-08-05 21:09:09
【问题描述】:

pandas 中的大多数操作都可以通过运算符链接(groupbyaggregateapply 等)来完成,但我发现过滤行的唯一方法是通过普通括号索引

df_filtered = df[df['column'] == value]

这并不吸引人,因为它需要我将df 分配给一个变量,然后才能过滤其值。有没有更类似于以下的内容?

df_filtered = df.mask(lambda x: x['column'] == value)

【问题讨论】:

  • df.querypd.eval 似乎很适合这个用例。有关pd.eval() 系列函数、它们的特性和用例的信息,请访问Dynamic Expression Evaluation in pandas using pd.eval()
  • 动态表达式不允许任何解释器上下文帮助,并且通常会降低生产力/可靠性。

标签: python pandas dataframe


【解决方案1】:

所以我的看法是,在对数据进行子设置以供分析时做两件事。

  • 获取行
  • 获取列

Pandas 有多种方法来完成这些操作,以及一些有助于获取行列的技术。对于 Pandas 的新用户来说,这可能会让人感到困惑,因为选择太多了。

你会使用iloc、loc、括号、query、isin、np.where、mask等吗?

方法链

现在,方法链是数据处理时的一种很好的工作方式。在 R 中,他们有一个简单的方法,你 select() 列和你 filter() 行。

因此,如果我们想在 Pandas 中保持简单,为什么不使用 filter() 表示列,使用 query() 表示行。这两个都返回数据帧,因此不需要弄乱布尔索引,也不需要在返回值周围添加df[ ]

那是什么样子的:-

df.filter(['col1', 'col2', 'col3']).query("col1 == 'sometext'")

然后您可以链接任何其他方法,例如 groupbydropna()sort_values()reset_index() 等。

通过保持一致并使用filter() 获取您的列并使用query() 获取您的行,一段时间后返回代码时会更容易阅读代码。

但是过滤器可以选择行吗?

是的,这是真的,但默认情况下query() 获取行,filter() 获取列。因此,如果您坚持使用默认值,则无需使用axis= 参数。

查询()

query() 可以与and/or &/| 一起使用,您也可以使用比较运算符> , < , >= , <=, ==, !=。你也可以在里面使用 Python,而不是在里面。

您可以使用@my_list 将列表传递给查询

使用查询获取行的一些示例

df.query('A > B')

df.query('a not in b')

df.query("series == '2206'")

df.query("col1 == @mylist")

df.query('Salary_in_1000 >= 100 & Age < 60 & FT_Team.str.startswith("S").values')

过滤器()

所以过滤器基本上就像使用括号df[]df[[]] 一样,因为它使用标签来选择列。但它不仅仅是括号符号。

过滤器具有like= 参数,以便帮助选择具有部分名称的列。

df.filter(like='partial_name',)

过滤器也有正则表达式来帮助选择

df.filter(regex='reg_string')

所以总结一下这种工作方式可能不适用于任何情况,例如如果你想使用索引/切片,那么 iloc 是要走的路。但这似乎是一种可靠的工作方式,可以简化您的工作流程和代码。

【讨论】:

    【解决方案2】:

    pandas 为 Wouter Overmeire 的答案提供了两种替代方案,不需要任何覆盖。一个是.loc[.],带有一个可调用对象,如

    df_filtered = df.loc[lambda x: x['column'] == value]
    

    另一个是.pipe(),如

    df_filtered = df.pipe(lambda x: x.loc[x['column'] == value])
    

    【讨论】:

    • 这是迄今为止我找到的最佳答案。这允许轻松链接,并且它完全独立于数据框名称,同时保持最小的语法检查(与“查询”不同)。非常巧妙的方法,谢谢。
    • +1 这应该是公认的答案。它是 pandas 内置的,不需要猴子补丁,并且是最灵活的。我还要补充一点,你可以让你的可调用对象返回一个可迭代的索引,而不仅仅是一个布尔系列。
    • 很好的答案,如果有人需要两列,请遵循:pandasDF.loc[lambda n: (n['col1'] == 'value') | (n[col2']=='值')]
    【解决方案3】:

    过滤器可以使用 Pandas query 链接:

    df = pd.DataFrame(np.random.randn(30, 3), columns=['a','b','c'])
    df_filtered = df.query('a > 0').query('0 < b < 2')
    

    过滤器也可以组合在一个查询中:

    df_filtered = df.query('a > 0 and 0 < b < 2')
    

    【讨论】:

    • 如果您需要在查询中引用 python 变量,documentation 说,“您可以通过在环境中使用像 @a + b 这样的 '@' 字符作为前缀来引用环境中的变量” .请注意,以下内容有效:df.query('a in list([1,2])')s = set([1,2]); df.query('a in @s')
    • 另一方面,如果您的列名包含某些特殊字符,查询评估将失败:例如“地名”。
    • 链接是查询的目的。
    • @teichert 您可以按照本文所述使用反引号(stackoverflow.com/questions/59167183/…)
    • @KHKim 不错!看起来在 v1.0.0 中添加了对反引号中的虚线名称的支持。
    【解决方案4】:

    这并不吸引人,因为它需要我将 df 分配给一个变量,然后才能过滤其值。

    df[df["column_name"] != 5].groupby("other_column_name")
    

    似乎有效:您也可以嵌套[] 运算符。也许他们在您提出问题后添加了它。

    【讨论】:

    • 这在链中没有什么意义,因为df 现在不一定引用 te 链的前一部分的输出。
    • @DaanLuttik:同意,不是链接,而是嵌套。对你更好?
    【解决方案5】:

    我提供此作为其他示例。这与https://stackoverflow.com/a/28159296/ 的答案相同

    我将添加其他编辑以使这篇文章更有用。

    pandas.DataFrame.query
    query 正是为此目的而制作的。考虑数据框df

    import pandas as pd
    import numpy as np
    
    np.random.seed([3,1415])
    df = pd.DataFrame(
        np.random.randint(10, size=(10, 5)),
        columns=list('ABCDE')
    )
    
    df
    
       A  B  C  D  E
    0  0  2  7  3  8
    1  7  0  6  8  6
    2  0  2  0  4  9
    3  7  3  2  4  3
    4  3  6  7  7  4
    5  5  3  7  5  9
    6  8  7  6  4  7
    7  6  2  6  6  5
    8  2  8  7  5  8
    9  4  7  6  1  5
    

    让我们使用query 过滤所有D &gt; B 所在的行

    df.query('D > B')
    
       A  B  C  D  E
    0  0  2  7  3  8
    1  7  0  6  8  6
    2  0  2  0  4  9
    3  7  3  2  4  3
    4  3  6  7  7  4
    5  5  3  7  5  9
    7  6  2  6  6  5
    

    我们的链接

    df.query('D > B').query('C > B')
    # equivalent to
    # df.query('D > B and C > B')
    # but defeats the purpose of demonstrating chaining
    
       A  B  C  D  E
    0  0  2  7  3  8
    1  7  0  6  8  6
    4  3  6  7  7  4
    5  5  3  7  5  9
    7  6  2  6  6  5
    

    【讨论】:

    【解决方案6】:

    这个解决方案在实现方面更老套,但我发现它在使用方面更简洁,而且它肯定比其他建议的更通用。

    https://github.com/toobaz/generic_utils/blob/master/generic_utils/pandas/where.py

    你不需要下载整个 repo:保存文件并做

    from where import where as W
    

    应该足够了。然后你像这样使用它:

    df = pd.DataFrame([[1, 2, True],
                       [3, 4, False], 
                       [5, 7, True]],
                      index=range(3), columns=['a', 'b', 'c'])
    # On specific column:
    print(df.loc[W['a'] > 2])
    print(df.loc[-W['a'] == W['b']])
    print(df.loc[~W['c']])
    # On entire - or subset of a - DataFrame:
    print(df.loc[W.sum(axis=1) > 3])
    print(df.loc[W[['a', 'b']].diff(axis=1)['b'] > 1])
    

    一个稍微不那么愚蠢的用法示例:

    data = pd.read_csv('ugly_db.csv').loc[~(W == '$null$').any(axis=1)]
    

    顺便说一句:即使您只是使用布尔值,

    df.loc[W['cond1']].loc[W['cond2']]
    

    可能比

    更有效率
    df.loc[W['cond1'] & W['cond2']]
    

    因为它仅在 cond1True 时评估 cond2

    免责声明:我首先给出了这个答案elsewhere,因为我没有看到这个。

    【讨论】:

      【解决方案7】:

      只想添加一个使用loc 的演示,不仅可以按行过滤,还可以按列过滤,以及链式操作的一些优点。

      下面的代码可以按值过滤行。

      df_filtered = df.loc[df['column'] == value]
      

      通过稍微修改一下,您也可以过滤列。

      df_filtered = df.loc[df['column'] == value, ['year', 'column']]
      

      那么为什么我们需要一个链式方法呢?答案是,如果你有很多操作,它很容易阅读。例如,

      res =  df\
          .loc[df['station']=='USA', ['TEMP', 'RF']]\
          .groupby('year')\
          .agg(np.nanmean)
      

      【讨论】:

        【解决方案8】:

        您还可以利用 numpy 库进行逻辑操作。它相当快。

        df[np.logical_and(df['A'] == 1 ,df['B'] == 6)]
        

        【讨论】:

          【解决方案9】:

          由于version 0.18.1 .loc 方法接受一个可供选择的调用。与 lambda 函数一起,您可以创建非常灵活的可链接过滤器:

          import numpy as np
          import pandas as pd
          
          df = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD'))
          df.loc[lambda df: df.A == 80]  # equivalent to df[df.A == 80] but chainable
          
          df.sort_values('A').loc[lambda df: df.A > 80].loc[lambda df: df.B > df.A]
          

          如果您所做的只是过滤,您也可以省略.loc

          【讨论】:

            【解决方案10】:

            如果您将列设置为作为索引进行搜索,则可以使用DataFrame.xs() 获取横截面。这不像query 的答案那样通用,但在某些情况下可能很有用。

            import pandas as pd
            import numpy as np
            
            np.random.seed([3,1415])
            df = pd.DataFrame(
                np.random.randint(3, size=(10, 5)),
                columns=list('ABCDE')
            )
            
            df
            # Out[55]: 
            #    A  B  C  D  E
            # 0  0  2  2  2  2
            # 1  1  1  2  0  2
            # 2  0  2  0  0  2
            # 3  0  2  2  0  1
            # 4  0  1  1  2  0
            # 5  0  0  0  1  2
            # 6  1  0  1  1  1
            # 7  0  0  2  0  2
            # 8  2  2  2  2  2
            # 9  1  2  0  2  1
            
            df.set_index(['A', 'D']).xs([0, 2]).reset_index()
            # Out[57]: 
            #    A  D  B  C  E
            # 0  0  2  2  2  2
            # 1  0  2  1  1  0
            

            【讨论】:

              【解决方案11】:

              我不完全确定你想要什么,你的最后一行代码也无济于事,但无论如何:

              “链接”过滤是通过“链接”布尔索引中的条件来完成的。

              In [96]: df
              Out[96]:
                 A  B  C  D
              a  1  4  9  1
              b  4  5  0  2
              c  5  5  1  0
              d  1  3  9  6
              
              In [99]: df[(df.A == 1) & (df.D == 6)]
              Out[99]:
                 A  B  C  D
              d  1  3  9  6
              

              如果你想链接方法,你可以添加你自己的掩码方法并使用那个。

              In [90]: def mask(df, key, value):
                 ....:     return df[df[key] == value]
                 ....:
              
              In [92]: pandas.DataFrame.mask = mask
              
              In [93]: df = pandas.DataFrame(np.random.randint(0, 10, (4,4)), index=list('abcd'), columns=list('ABCD'))
              
              In [95]: df.ix['d','A'] = df.ix['a', 'A']
              
              In [96]: df
              Out[96]:
                 A  B  C  D
              a  1  4  9  1
              b  4  5  0  2
              c  5  5  1  0
              d  1  3  9  6
              
              In [97]: df.mask('A', 1)
              Out[97]:
                 A  B  C  D
              a  1  4  9  1
              d  1  3  9  6
              
              In [98]: df.mask('A', 1).mask('D', 6)
              Out[98]:
                 A  B  C  D
              d  1  3  9  6
              

              【讨论】:

              • 很好的答案!那么在(df.A == 1) &amp; (df.D == 6) 中,“&”是 Pandas 中的重载运算符吗?
              • 这是一个非常好的解决方案——我什至不知道你可以在 python 中使用类似的方法。像这样的功能在 Pandas 本身中会非常好。
              • 我唯一遇到的问题是使用pandas.。你应该import pandas as pd
              • 确实import pandas as pd 是现在的普遍做法。我怀疑是在我回答问题的时候。
              【解决方案12】:

              我的答案与其他人相似。如果您不想创建新功能,您可以使用 pandas 已经为您定义的功能。使用管道方法。

              df.pipe(lambda d: d[d['column'] == value])
              

              【讨论】:

              • THIS 是您想要链接诸如 a.join(b).pipe(lambda df: df[df.column_to_filter == 'VALUE']) 之类的命令时想要的
              【解决方案13】:

              我有同样的问题,只是我想将条件组合成 OR 条件。 Wouter Overmeire 给出的格式将标准组合成一个 AND 条件,使得两者都必须满足:

              In [96]: df
              Out[96]:
                 A  B  C  D
              a  1  4  9  1
              b  4  5  0  2
              c  5  5  1  0
              d  1  3  9  6
              
              In [99]: df[(df.A == 1) & (df.D == 6)]
              Out[99]:
                 A  B  C  D
              d  1  3  9  6
              

              但我发现,如果您将每个条件包装在 (... == True) 中并用管道连接条件,则条件将组合在 OR 条件中,只要其中任何一个为真就满足:

              df[((df.A==1) == True) | ((df.D==6) == True)]
              

              【讨论】:

              • df[(df.A==1) | (df.D==6)] 不足以完成您想要完成的工作吗?
              • 不,它不会,因为它会给出布尔结果(True vs False),而不是像上面那样过滤所有满足条件的数据。希望我说清楚了。
              【解决方案14】:

              如果您想应用所有常见的布尔掩码以及通用掩码,您可以将以下内容放入文件中,然后简单地将它们全部分配如下:

              pd.DataFrame = apply_masks()
              

              用法:

              A = pd.DataFrame(np.random.randn(4, 4), columns=["A", "B", "C", "D"])
              A.le_mask("A", 0.7).ge_mask("B", 0.2)... (May be repeated as necessary
              

              这有点老套,但如果您根据过滤器不断地切分和更改数据集,它可以让事情变得更简洁。 在 gen_mask 函数中还有一个改编自 Daniel Velkov 的通用过滤器,您可以将其与 lambda 函数一起使用,或者如果需要。

              要保存的文件(我使用masks.py):

              import pandas as pd
              
              def eq_mask(df, key, value):
                  return df[df[key] == value]
              
              def ge_mask(df, key, value):
                  return df[df[key] >= value]
              
              def gt_mask(df, key, value):
                  return df[df[key] > value]
              
              def le_mask(df, key, value):
                  return df[df[key] <= value]
              
              def lt_mask(df, key, value):
                  return df[df[key] < value]
              
              def ne_mask(df, key, value):
                  return df[df[key] != value]
              
              def gen_mask(df, f):
                  return df[f(df)]
              
              def apply_masks():
              
                  pd.DataFrame.eq_mask = eq_mask
                  pd.DataFrame.ge_mask = ge_mask
                  pd.DataFrame.gt_mask = gt_mask
                  pd.DataFrame.le_mask = le_mask
                  pd.DataFrame.lt_mask = lt_mask
                  pd.DataFrame.ne_mask = ne_mask
                  pd.DataFrame.gen_mask = gen_mask
              
                  return pd.DataFrame
              
              if __name__ == '__main__':
                  pass
              

              【讨论】:

                【解决方案15】:

                @lodagro 的回答很棒。我会通过将掩码函数概括为:

                def mask(df, f):
                  return df[f(df)]
                

                然后您可以执行以下操作:

                df.mask(lambda x: x[0] < 0).mask(lambda x: x[1] > 0)
                

                【讨论】:

                • 一个有用的概括!我希望它已经直接集成到DataFrames 中!
                猜你喜欢
                • 2022-07-06
                • 1970-01-01
                • 2021-06-28
                • 1970-01-01
                • 2014-02-06
                • 2013-09-02
                • 2020-11-29
                • 1970-01-01
                相关资源
                最近更新 更多