【问题标题】:selecting across multiple columns with python pandas?使用 python pandas 在多个列中进行选择?
【发布时间】:2012-02-13 12:52:28
【问题描述】:

我在 pandas 中有一个数据框 df,它是使用 csv 文件中的 pandas.read_table 构建的。数据框有几列,并由其中一列索引(这是唯一的,因为每一行都有一个用于索引的列的唯一值。)

如何根据应用于多列的“复杂”过滤器选择数据框的行?例如,我可以轻松选择列 colA 大于 10 的数据框切片:

df_greater_than10 = df[df["colA"] > 10]

但是如果我想要一个过滤器,比如:选择df 的切片,其中任何列大于 10?

或者colA 的值大于10 但colB 的值小于5?

这些是如何在 pandas 中实现的? 谢谢。

【问题讨论】:

    标签: python numpy csv pandas


    【解决方案1】:

    我鼓励您在mailing list 上提出这些问题,但无论如何,使用底层 NumPy 数组仍然是一件非常低级的事情。例如,要选择任何列中的值超过(例如,本例中的 1.5)的行:

    In [11]: df
    Out[11]: 
                A        B        C        D      
    2000-01-03 -0.59885 -0.18141 -0.68828 -0.77572
    2000-01-04  0.83935  0.15993  0.95911 -1.12959
    2000-01-05  2.80215 -0.10858 -1.62114 -0.20170
    2000-01-06  0.71670 -0.26707  1.36029  1.74254
    2000-01-07 -0.45749  0.22750  0.46291 -0.58431
    2000-01-10 -0.78702  0.44006 -0.36881 -0.13884
    2000-01-11  0.79577 -0.09198  0.14119  0.02668
    2000-01-12 -0.32297  0.62332  1.93595  0.78024
    2000-01-13  1.74683 -1.57738 -0.02134  0.11596
    2000-01-14 -0.55613  0.92145 -0.22832  1.56631
    2000-01-17 -0.55233 -0.28859 -1.18190 -0.80723
    2000-01-18  0.73274  0.24387  0.88146 -0.94490
    2000-01-19  0.56644 -0.49321  1.17584 -0.17585
    2000-01-20  1.56441  0.62331 -0.26904  0.11952
    2000-01-21  0.61834  0.17463 -1.62439  0.99103
    2000-01-24  0.86378 -0.68111 -0.15788 -0.16670
    2000-01-25 -1.12230 -0.16128  1.20401  1.08945
    2000-01-26 -0.63115  0.76077 -0.92795 -2.17118
    2000-01-27  1.37620 -1.10618 -0.37411  0.73780
    2000-01-28 -1.40276  1.98372  1.47096 -1.38043
    2000-01-31  0.54769  0.44100 -0.52775  0.84497
    2000-02-01  0.12443  0.32880 -0.71361  1.31778
    2000-02-02 -0.28986 -0.63931  0.88333 -2.58943
    2000-02-03  0.54408  1.17928 -0.26795 -0.51681
    2000-02-04 -0.07068 -1.29168 -0.59877 -1.45639
    2000-02-07 -0.65483 -0.29584 -0.02722  0.31270
    2000-02-08 -0.18529 -0.18701 -0.59132 -1.15239
    2000-02-09 -2.28496  0.36352  1.11596  0.02293
    2000-02-10  0.51054  0.97249  1.74501  0.20525
    2000-02-11  0.10100  0.27722  0.65843  1.73591
    
    In [12]: df[(df.values > 1.5).any(1)]
    Out[12]: 
                A       B       C        D     
    2000-01-05  2.8021 -0.1086 -1.62114 -0.2017
    2000-01-06  0.7167 -0.2671  1.36029  1.7425
    2000-01-12 -0.3230  0.6233  1.93595  0.7802
    2000-01-13  1.7468 -1.5774 -0.02134  0.1160
    2000-01-14 -0.5561  0.9215 -0.22832  1.5663
    2000-01-20  1.5644  0.6233 -0.26904  0.1195
    2000-01-28 -1.4028  1.9837  1.47096 -1.3804
    2000-02-10  0.5105  0.9725  1.74501  0.2052
    2000-02-11  0.1010  0.2772  0.65843  1.7359
    

    必须使用&|(和括号!)组合多个条件:

    In [13]: df[(df['A'] > 1) | (df['B'] < -1)]
    Out[13]: 
                A        B       C        D     
    2000-01-05  2.80215 -0.1086 -1.62114 -0.2017
    2000-01-13  1.74683 -1.5774 -0.02134  0.1160
    2000-01-20  1.56441  0.6233 -0.26904  0.1195
    2000-01-27  1.37620 -1.1062 -0.37411  0.7378
    2000-02-04 -0.07068 -1.2917 -0.59877 -1.4564
    

    我很想拥有某种查询 API 来简化这些事情

    【讨论】:

    • 再次感谢。将在邮件列表中发布未来的问题。但是现在,如果您想以编程方式执行此操作怎么办?你有一个列标签列表......你怎么能把它放到'|'符号?例如。如果labels = ['A', 'B', 'C', ...']
    • 澄清一下:如果表中有其他不想过滤的值,any(1) 方法将不起作用。假设有很多列,而您只希望 any 应用于其中的一个子集(您知道子集的标签)。
    【解决方案2】:

    在 Pandas 中至少有一些方法可以缩短它的语法,直到它得到一个完整的查询 API (也许我会尝试加入 github 项目并且这样做是时间允许的,如果没有的话else 已经开始了)。

    下面给出了一种稍微缩短语法的方法:

    inds = df.apply(lambda x: x["A"]>10 and x["B"]<5, axis=1) 
    print df[inds].to_string()
    

    要完全解决这个问题,需要在 Pandas 中构建 SQL select 和 where 子句之类的东西。这一点都不是微不足道的,但我认为可能适用的一种方法是使用 Python operator 内置模块。这使您可以将大于之类的东西视为函数而不是符号。因此,您可以执行以下操作:

    def pandas_select(dataframe, select_dict):
    
        inds = dataframe.apply(lambda x: reduce(lambda v1,v2: v1 and v2, 
                               [elem[0](x[key], elem[1]) 
                               for key,elem in select_dict.iteritems()]), axis=1)
        return dataframe[inds]
    

    然后像您这样的测试示例将执行以下操作:

    import operator
    select_dict = {
                   "A":(operator.gt,10),
                   "B":(operator.lt,5)                  
                  }
    
    print pandas_select(df, select_dict).to_string()
    

    您可以进一步缩短语法,方法是向pandas_select 构建更多参数以自动处理不同的常见逻辑运算符,或者将它们导入名称更短的命名空间。

    请注意,上面的 pandas_select 函数仅适用于逻辑和约束链。您必须对其进行修改以获得不同的逻辑行为。或者使用not 和德摩根定律。

    【讨论】:

    • 如果我有一个列表 ['Alice', 'Bob', 'Carl'] 如何生成字典来选择数据框 ['A'] 在我的列表中的项目?
    • 如果列表是a = ['Alice', 'Bob', 'Carl'],并且整个数据框被称为df,那么你可以这样做:df[df.A.isin(a)],它将子选择设置成员条件所在的行索引对于列 A 的元素为 true。扩展我在上面制作的用于表达逻辑的迷你领域特定语言,以使用简单的语法来使用此选项可能会是一件令人不舒服的苦差事。
    • 或许还可以看到即将推出的(pandas 0.13)查询方法:pandas.pydata.org/pandas-docs/dev/…stackoverflow.com/questions/18521037/…
    • df.apply(lambda row: ..., axis=1) 灵活但速度慢。
    • @FreekWiekmeijer 是的。以我的经验,许多人试图过早地优化 lambda 的使用或 pandas 中的显式迭代,试图立即将代码重构为脆弱且难以辨认的矢量化操作。大多数用例并没有从中受益太多,坦率地说,你最好以“愚蠢”“明显”的方式编写代码并拆分数据帧以使用多处理来加速某些事情等,而不是提交太多早点使用 pandas 的法律术语。
    【解决方案3】:

    自从提出并回答了这个问题以来,Pandas 中添加了一个查询功能。下面给出一个例子。

    鉴于此示例数据框:

    periods = 8
    dates = pd.date_range('20170101', periods=periods)
    rand_df = pd.DataFrame(np.random.randn(periods,4), index=dates, 
          columns=list('ABCD'))
    

    以下查询语法允许您使用多个过滤器,例如选择语句中的“WHERE”子句。

    rand_df.query("A < 0 or B < 0")
    

    有关更多详细信息,请参阅Pandas documentation

    【讨论】:

      猜你喜欢
      • 2023-03-14
      • 2019-10-06
      • 2016-07-31
      • 1970-01-01
      • 1970-01-01
      • 2022-10-13
      • 2017-03-05
      • 1970-01-01
      相关资源
      最近更新 更多