【问题标题】:Drop rows from original Pandas dataframe if some GroupBy condition is met如果满足某些 GroupBy 条件,则从原始 Pandas 数据框中删除行
【发布时间】:2020-02-10 23:30:33
【问题描述】:

我正在尝试构建一种快速的 Pandas 方法,用于在满足某些条件时从 Dataframe 中删除某些行。具体来说,如果该行中的某个其他值等于 0,我想删除数据框中某个变量的第一次出现。这可能是最容易通过示例解释的:

foo = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3])
bar = np.array([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1])
df = pd.DataFrame({'foo': foo, 'bar':bar}) 

# So df is:
idx | foo | bar
0   1   1
1   1   0
2   1   1
3   1   0
4   1   1
5   1   0
6   1   1
7   1   0
8   1   1
9   1   0
10  1   1
11  2   0
12  2   1
13  2   0
14  2   1
15  3   1
16  3   1
17  3   0
18  3   1

当 'foo' 列是新值时,我想查看第一行,然后如果该行中的 'bar' 值 = 0,则将其从数据框中删除。

我可以使用 groupby 找到何时满足此条件:

df.groupby('foo').first()

# Result:
    bar
foo 
1   1
2   0
3   1

所以我看到我需要在 foo = 2 时删除第一行(即,在我的原始数据框中删除索引 = 11 的行)。但是,由于形状/大小不同,我无法确定如何将此 groupby 结果用作原始数据框的掩码。

我发现了一个关于 groupby 修改的相关问题 (Drop pandas dataframe rows based on groupby() condition),但在此示例中,当满足此条件时,它们会删除所有行,而我只想删除第一行。

请问可以吗?

【问题讨论】:

    标签: python pandas dataframe pandas-groupby


    【解决方案1】:

    使用Series.shift:

    df.loc[~(df['foo'].ne(df['foo'].shift()) & df['bar'].eq(0))]
    

    df.loc[df.duplicated(subset = 'foo') | df['bar'].ne(0)]
    

    明显好多了

    %%timeit
    df.loc[~(df['foo'].ne(df['foo'].shift()) & df['bar'].eq(0))]
    #970 µs ± 51.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) each)
    

    %%timeit
    df.loc[df.duplicated(subset = 'foo') | df['bar'].ne(0)]
    #1.34 ms ± 34 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    %%timeit
    df.loc[~df.index.isin(df.drop_duplicates(subset='foo').loc[lambda x: x.bar==0].index)]
    #2.16 ms ± 109 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    如果foo 就像你的例子:

    %%timeit
    df.loc[~(df['foo'].diff().ne(0)&df['bar'].eq(0))]
    908 µs ± 15.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    %%timeit
    df.loc[df['foo'].duplicated().add(df['bar']).ne(0)]
    787 µs ± 15.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    【讨论】:

      【解决方案2】:

      可以先找到每一个新的foo的第一次出现,检查bar是否为0,然后用它作为掩码过滤掉原来的df。

      df.loc[~df.index.isin(df.drop_duplicates(subset='foo').loc[lambda x: x.bar==0].index)]
      

      或者使用 groupby:

      (
          df.groupby('foo').apply(lambda x: x.iloc[int(x.bar.iloc[0]==0):])
          .reset_index(level=0,drop=True)
      )
      

      在您的示例中,第一种方法(2.71 ms)比 groupby 方法(3.93 ms)更快。

      【讨论】:

      • 谢谢,这很好用而且很短。在 10M 大小的数据集上执行大约需要 2 秒。你认为这比使用 groupby/transform 操作更快吗?
      • I'm trying to construct a fast Pandas approach for dropping certain rows from a Dataframe 这个还有很大的改进空间
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-10-13
      • 1970-01-01
      • 2018-01-09
      • 2015-01-10
      • 1970-01-01
      • 1970-01-01
      • 2020-09-11
      相关资源
      最近更新 更多