【问题标题】:Get first row of dataframe in Python Pandas based on criteria根据标准获取 Python Pandas 中的第一行数据框
【发布时间】:2016-11-17 16:27:04
【问题描述】:

假设我有一个像这样的数据框

import pandas as pd
df = pd.DataFrame([[1, 2, 1], [1, 3, 2], [4, 6, 3], [4, 3, 4], [5, 4, 5]], columns=['A', 'B', 'C'])

>> df
   A  B  C
0  1  2  1
1  1  3  2
2  4  6  3
3  4  3  4
4  5  4  5

原始表格更复杂,列和行更多。

我想获得满足某些条件的第一行。例子:

  1. 获取 A > 3 的第一行(返回第 2 行)
  2. 获取 A > 4 AND B > 3 的第一行(返回第 4 行)
  3. 获取 A > 3 AND (B > 3 OR C > 2) 的第一行(返回第 2 行)

但是,如果没有任何行满足特定条件,那么我想在按 A 降序排序(或其他情况下按 B、C 等)排序后获得第一行

  1. 获取 A > 6 的第一行(通过 A desc 排序返回第 4 行并获取第一个)

我能够通过迭代数据框来做到这一点(我知道胡扯:P)。所以,我更喜欢用pythonic的方式来解决它。

【问题讨论】:

  • 第一行 A > 4 是 4,你能检查你正在搜索的测试
  • @Boud 抱歉。输入错误:(
  • 所以,@Kartik 我将使用布尔索引,如果返回的数据框为空,那么我手动对其进行排序并获取第一行?这是最合适的方式?
  • 布尔索引可能比你想要的要多,因为它必须贯穿整个 DataFrame。如果第一行是您想要的,您应该能够立即找到,而不管 DataFrame 的大小。

标签: python pandas


【解决方案1】:

This tutorial 非常适合用于熊猫切片。确保你检查出来。在一些 sn-ps 上...要使用条件对数据帧进行切片,请使用以下格式:

>>> df[condition]

这将返回您可以使用iloc 索引的数据帧切片。以下是您的示例:

  1. 获取 A > 3 的第一行(返回第 2 行)

    >>> df[df.A > 3].iloc[0]
    A    4
    B    6
    C    3
    Name: 2, dtype: int64
    

如果你真正想要的是行号,而不是使用iloc,它会是df[df.A > 3].index[0]

  1. 获取 A > 4 AND B > 3 的第一行:

    >>> df[(df.A > 4) & (df.B > 3)].iloc[0]
    A    5
    B    4
    C    5
    Name: 4, dtype: int64
    
  2. 获取 A > 3 AND (B > 3 OR C > 2) 的第一行(返回第 2 行)

    >>> df[(df.A > 3) & ((df.B > 3) | (df.C > 2))].iloc[0]
    A    4
    B    6
    C    3
    Name: 2, dtype: int64
    

现在,对于您的最后一个案例,我们可以编写一个函数来处理返回降序帧的默认情况:

>>> def series_or_default(X, condition, default_col, ascending=False):
...     sliced = X[condition]
...     if sliced.shape[0] == 0:
...         return X.sort_values(default_col, ascending=ascending).iloc[0]
...     return sliced.iloc[0]
>>> 
>>> series_or_default(df, df.A > 6, 'A')
A    5
B    4
C    5
Name: 4, dtype: int64

正如预期的那样,它返回第 4 行。

【讨论】:

  • 万一你的dataframe很大,这不是浪费吗?您正在计算整个系列只是为了提取它的第一个元素。
【解决方案2】:

对于现有匹配,使用query:

df.query(' A > 3' ).head(1)
Out[33]: 
   A  B  C
2  4  6  3

df.query(' A > 4 and B > 3' ).head(1)
Out[34]: 
   A  B  C
4  5  4  5

df.query(' A > 3 and (B > 3 or C > 2)' ).head(1)
Out[35]: 
   A  B  C
2  4  6  3

【讨论】:

    【解决方案3】:

    你可以用切片和头部处理前 3 个项目:

    1. df[df.A>=4].head(1)
    2. df[(df.A>=4)&(df.B>=3)].head(1)
    3. df[(df.A>=4)&((df.B>=3) * (df.C>=2))].head(1)

    万一没有返回的情况,你可以用 try 或 if 来处理......

    try:
        output = df[df.A>=6].head(1)
        assert len(output) == 1
    except: 
        output = df.sort_values('A',ascending=False).head(1)
    

    【讨论】:

      【解决方案4】:

      对于'在找到满足要求的第一行/记录并且迭代其他行'时立即返回值的点,以下代码将起作用:

      def pd_iter_func(df):
          for row in df.itertuples():
              # Define your criteria here
              if row.A > 4 and row.B > 3:
                  return row
      

      在处理大型数据帧时,它比Boolean Indexing 更有效。

      为了让上面的函数更适用,可以实现 lambda 函数:

      def pd_iter_func(df: DataFrame, criteria: Callable[[NamedTuple], bool]) -> Optional[NamedTuple]:
          for row in df.itertuples():
              if criteria(row):
                  return row
      
      pd_iter_func(df, lambda row: row.A > 4 and row.B > 3)
      

      正如the answer to the 'mirror' question 中提到的,pandas.Series.idxmax 也是一个不错的选择。

      def pd_idxmax_func(df, mask):
          return df.loc[mask.idxmax()]
      
      pd_idxmax_func(df, (df.A > 4) & (df.B > 3))
      

      【讨论】:

      • 我喜欢 itertuples()。但是使用 idxmax(),在运行 idxmax() 之前,不是每行上的 max eval'd 吗?
      猜你喜欢
      • 2019-08-12
      • 2017-12-10
      • 2018-05-05
      • 1970-01-01
      • 2022-06-22
      • 2019-07-25
      • 2017-10-17
      • 1970-01-01
      • 2023-03-14
      相关资源
      最近更新 更多