【问题标题】:Delete rows with conditions (Multi-Index case)删除有条件的行(多索引情况)
【发布时间】:2021-09-13 13:59:08
【问题描述】:

我是 Stack Overflow 的新手,我有这个数据集:

df=pd.DataFrame({'ID': {0: 4, 1: 4, 2: 4, 3: 88, 4: 88, 5: 323, 6: 323},
         'Step': {0: 'A', 1: 'Bar', 2: 'F', 3: 'F', 4: 'Bar', 5: 'F', 6: 'A'},
         'Num': {0: 38, 1: 38, 2: 38, 3: 320, 4: 320, 5: 433, 6: 432},
         'Date': {0: '2018-08-02',
          1: '2018-12-02',
          2: '2019-03-02',
          3: '2017-03-02',
          4: '2018-03-02',
          5: '2020-03-04',
          6: '2020-02-03'},
         'Occurence': {0: 3, 1: 3, 2: 3, 3: 2, 4: 2, 5: 2, 6: 2}})

变量 'ID' 和 'Step' 是多索引的

我想做两件事:

第一:

如果同一“ID”的“Num”不同,则删除该ID的行。

其次:

对于相同的 ID,步骤“F”应该是最后一个(日期最近)。如果不是,则删除该 ID 的行。

我遇到了一些困难,因为命令 df['Step'] 和 df['ID'] 不起作用('ID' 和 'Step' 是最近 groupby() 的多索引原因)。

我尝试了在Multi index dataframe delete row with maximum value per group 上找到的 groupby(level=0)

但我还是有一些困难。

有人可以帮帮我吗?

预期输出:

df=pd.DataFrame({'ID': {0: 4, 1: 4, 2: 4},
         'Step': {0: 'A', 1: 'Bar', 2: 'F'},
         'Num': {0: 38, 1: 38, 2: 38},
         'Date': {0: '2018-08-02',
          1: '2018-12-02',
          2: '2019-03-02',
         'Occurence': {0: 3, 1: 3, 2: 3}})

ID 88 已被删除,因为步骤“F”不是最后一步(日期最近)。 ID 323 已被删除,因为 Num 433!=Num 432。

【问题讨论】:

  • 请添加您的预期输出。
  • 感谢您的回答。完成。

标签: python pandas multi-index


【解决方案1】:

我不知道我是否理解正确。 不过你可以试试这个

import os
import pandas as pd 

sheet = pd.read_excel(io="you_file", sheet_name='sheet_name', na_filter=False, header=0 )

list_objects          = []

for index,row in sheet.iterrows():
    if (row['ID'] != index):
        list_objects.append(row)

list_objects 将是一个字典列表

【讨论】:

    【解决方案2】:
    • 按列对数据框进行分组ID
    • TransformNum 列使用nunique 来标识唯一值
    • 使用last 转换Step 列以检查每个组的最后一个值是否为F
    • 使用逻辑与组合布尔掩码并过滤行
    g = df.groupby('ID')
    m = g['Num'].transform('nunique').eq(1) & g['Step'].transform('last').eq('F')
    
    print(df[m])
    
       ID Step  Num        Date  Occurence
    0   4    A   38  2018-08-02          3
    1   4  Bar   38  2018-12-02          3
    2   4    F   38  2019-03-02          3
    

    groupbyfilter 的替代方法,但效率可能低于上述方法

    df.groupby('ID').filter(lambda g: g['Step'].iloc[-1] == 'F' and g['Num'].nunique() == 1)
    
       ID Step  Num        Date  Occurence
    0   4    A   38  2018-08-02          3
    1   4  Bar   38  2018-12-02          3
    2   4    F   38  2019-03-02          3
    

    注意:如果 IDStep 是 MultiIndex,您必须在使用上述建议的解决方案之前 reset 索引。

    【讨论】:

    • 感谢您的回答,对您有很大帮助!但是我认为仍然存在错误: g['Step'].transform('last').eq('F') 不起作用,因为它不依赖于'F'的位置,而是关于日期。例如,在我的帖子中查看我的 df。由于 g['Num'].transform('nunique').eq(1),最后 2 行将被删除。但是,如果两个 num 错误相同,则使用您的代码,行将被删除,因为 'F' 不在最后一个位置。但是在这个例子中,'F' 是最近的日期,所以它不应该被删除
    • 哦,我认为这取决于F 的位置。但是,如果您想检查对应于最大日期的最后一个值,我们可以预先对这些值进行排序。请查看g = df.sort_values('Date').groupby('ID')
    • 是的,它有效!感谢您的所有回答! Scott Boston 也做得很好 O_o 不使用 reset_index 并将变量保留在 Multi-Index 中
    【解决方案3】:

    既然你说 ID 和 Step 在索引中,我们可以这样做:

    df1[df1.sort_values('Date').groupby('ID')['Num']\
           .transform(lambda x: (x.nunique() == 1) & 
                                (x.index.get_level_values(1)[-1] == 'F'))]
    

    输出:

             Num        Date  Occurence
    ID Step                            
    4  A      38  2018-08-02          3
       Bar    38  2018-12-02          3
       F      38  2019-03-02          3
    

    如何?

    • 首先按“日期”对数据框进行排序
    • 然后按 ID 对数据帧进行分组
    • 获取每组数据帧并使用“Num”列转换为布尔系列,我们 首先获取 'Num' 中唯一元素的数量 组,如果该数字等于 1,那么您知道在该组中 所有 'Num's 都是一样的,这是真的
    • 其次,我们得到 MultiIndex 的内层 (level=1) 和 我们使用带有 [-1] 的索引检查最后一个值,如果该值是 等于 'F' 然后也有一个 True

    【讨论】:

    • 哇,谢谢它完美地工作,并且您将变量保留在索引中。吉!
    • 好吧,我还有一个问题......你的代码做得很好,但我不明白它是如何工作的,尤其是这部分:\ .transform(lambda x: (x.nunique() == 1) & (x.index.get_level_values(1)[-1] == 'F'))] 你能解释一下吗?
    【解决方案4】:

    使用 groupby 查找出现 1 次的行。我根据 groupby 结果返回的 ID 删除数据框中的行。我排除了出现一次的 ID,但不包括删除的 ID。

    df=pd.DataFrame({'ID': {0: 4, 1: 4, 2: 4, 3: 88, 4: 88, 5: 323, 6: 323},
         'Step': {0: 'A', 1: 'Bar', 2: 'F', 3: 'F', 4: 'Bar', 5: 'F', 6: 'A'},
         'Num': {0: 38, 1: 38, 2: 38, 3: 320, 4: 320, 5: 433, 6: 432},
         'Date': {0: '2018-08-02',
          1: '2018-12-02',
          2: '2019-03-02',
          3: '2017-03-02',
          4: '2018-03-02',
          5: '2020-03-04',
          6: '2020-02-03'},
         'Occurence': {0: 3, 1: 3, 2: 3, 3: 2, 4: 2, 5: 2, 6: 2}})
    df.set_index(['ID','Step'],inplace=True)
    print(df)
    print("If 'Num' is different for the same 'ID', then delete the rows of this ID.")
    
     #exclude id with single occurrences
     grouped=df.groupby([df.index.get_level_values(0)]).size().eq(1)
     labels=set([x for x,y in (grouped[grouped.values==True].index)])
    
     filter=[x for x in df.index.get_level_values(0) if x not in labels]
    
     grouped = df[df.index.get_level_values(0).isin(filter)].groupby([df.index.get_level_values(0),'Num']).size().eq(1)
    
     labels=set([x for x,y in (grouped[grouped.values==True].index)])
     if len(labels)>0:
         df = df.drop(labels=labels, axis=0,level=0)    
     print(df)
    

    输出:

      Num        Date  Occurence
      ID Step                            
      4  A      38  2018-08-02          3
         Bar    38  2018-12-02          3
         F      38  2019-03-02          3
      88 F     320  2017-03-02          2
         Bar   320  2018-03-02          2
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-05
      • 1970-01-01
      • 1970-01-01
      • 2016-05-09
      • 2020-05-10
      相关资源
      最近更新 更多