【问题标题】:Pandas: complex filtering with applyPandas:使用 apply 进行复杂过滤
【发布时间】:2016-02-18 17:56:21
【问题描述】:

让我们假设这个数据帧,我想以这样一种方式过滤它,我从最后一个索引向后迭代,直到找到两个连续的 'a' = 0。一旦发生这种情况,数据帧的其余部分(包括两个零)应被过滤:

    a
1   6.5
2   0
3   0
4   4.0
5   0
6   3.2

想要的结果:

    a
4   4.0
5   0
6   3.2

我最初的想法是使用apply 进行过滤,并在apply 函数内部使用shift(1) == 0 & shift(2) == 0,但基于此我可以单独过滤每一行,但在找到双零后不会为其余行返回false,除非我使用全局变量或类似的讨厌的东西。

有什么聪明的方法吗?

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    您可以使用sort_indexascending=Falsecumsumdropna 来做到这一点:

    In [89]: df[(df.sort_index(ascending=False) == 0).cumsum() < 2].dropna()
    Out[89]:
         a
    4  4.0
    5  0.0
    6  3.2
    

    一步一步:

    In [99]: df.sort_index(ascending=False)
    Out[99]:
         a
    6  3.2
    5  0.0
    4  4.0
    3  0.0
    2  0.0
    1  6.5
    
    In [100]: df.sort_index(ascending=False) == 0
    Out[100]:
           a
    6  False
    5   True
    4  False
    3   True
    2   True
    1  False
    
    In [101]: (df.sort_index(ascending=False) == 0).cumsum()
    Out[101]:
       a
    6  0
    5  1
    4  1
    3  2
    2  3
    1  3
    
    In [103]: (df.sort_index(ascending=False) == 0).cumsum() < 2
    Out[103]:
           a
    6   True
    5   True
    4   True
    3  False
    2  False
    1  False
    
    In [104]: df[(df.sort_index(ascending=False) == 0).cumsum() < 2]
    Out[104]:
         a
    1  NaN
    2  NaN
    3  NaN
    4  4.0
    5  0.0
    6  3.2
    

    编辑

    IIUC 如果您的索引从 1 开始,您可以使用 pd.rolling_sumfirst_valid_index 类似的东西:

    df_sorted = df.sort_index(ascending=False)
    df[df_sorted[(pd.rolling_sum((df_sorted==0), window=2) == 2)].first_valid_index()+1:]
    

    以@jezrael 为例:

    In [208]: df
    Out[208]:
          a
    1   6.5
    2   0.0
    3   0.0
    4   7.0
    5   0.0
    6   0.0
    7   0.0
    8   4.0
    9   0.0
    10  0.0
    11  3.2
    12  5.0
    
    df_sorted = df.sort_index(ascending=False)
    
    In [210]: df[df_sorted[(pd.rolling_sum((df_sorted==0), window=2) == 2)].first_valid_index()+1:]
    Out[210]:
          a
    11  3.2
    12  5.0
    

    【讨论】:

    • 我可能对自己的解释有点糟糕:在这种情况下,您的结果是正确的,但我想检测何时发生 2 个连续的零,然后停在那里,同时留下双零。现在,当发生 2 个非连续零时,它会停止留下最后一个零。对于这种特殊情况,结果是相同的,但是如果值 [6, 0, 5, 0, 4, 0, 0],我希望 [6, 0, 5, 0, 4] 而您的代码会给出 [ 6, 0, 5]
    • @RomanRdgz 是的,很抱歉误解了。看看editev版本。
    • @jezrael 它只适用于从 1 开始的索引,如果从 0 开始的索引你应该添加 2 而不是 1。对于你的例子,它运行良好。
    • @jezrael 为什么不呢?它只返回一个元素,index = 12 和 value = 5。预期输出应该是什么?
    【解决方案2】:

    您可以将groupbycumcountcumsum 一起使用,然后反转df 并再次使用cumsum

    print df
          a
    1   6.5
    2   0.0
    3   0.0
    4   7.0
    5   0.0
    6   0.0
    7   0.0
    8   4.0
    9   0.0
    10  0.0
    11  3.2
    12  5.0
    
    print df[df.groupby((df['a'].diff(1)!=0).astype('int').cumsum()).cumcount()[::-1].cumsum()[::-1]== 0]
    
          a
    11  3.2
    12  5.0
    

    解释:

    print (df['a'].diff(1) != 0)
    1      True
    2      True
    3     False
    4      True
    5      True
    6     False
    7     False
    8      True
    10     True
    11     True
    12     True
    Name: a, dtype: bool
    
    print  (df['a'].diff(1) != 0).astype('int') 
    1     1
    2     1
    3     0
    4     1
    5     1
    6     0
    7     0
    8     1
    10    1
    11    1
    12    1
    Name: a, dtype: int32
    
    print  (df['a'].diff(1) != 0).astype('int') .cumsum()
    1     1
    2     2
    3     2
    4     3
    5     4
    6     4
    7     4
    8     5
    10    6
    11    7
    12    8
    Name: a, dtype: int32
    
    print  df.groupby( (df['a'].diff(1) != 0).astype('int').cumsum() ).cumcount()
    1     0
    2     0
    3     1
    4     0
    5     0
    6     1
    7     2
    8     0
    10    0
    11    0
    12    0
    dtype: int64
    
    print  df.groupby( (df['a'].diff(1) != 0).astype('int').cumsum() ).cumcount()[::-1].cumsum()[::-1]
    1     5
    2     5
    3     5
    4     4
    5     4
    6     4
    7     3
    8     1
    10    1
    11    1
    11    0
    12    0
    dtype: int64
    
    print  df.groupby( (df['a'].diff(1) != 0).astype('int').cumsum() ).cumcount()[::-1].cumsum()[::-1] == 0
    1     False
    2     False
    3     False
    4     False
    5     False
    6     False
    7     False
    8     False
    10    False
    11    False
    11     True
    12     True
    dtype: bool
    

    【讨论】:

    • 这确实有效,但是创建辅助列然后删除它们比使用 appli 方法更有效吗?如果“a”值也可以是负数怎么办?
    • 我认为这取决于您的df,但它可以更贴近apply
    • 解决方案已修改,临时列已删除,您也可以使用负值。
    • 恐怕我看不懂df.groupby().cumcount()。如果示例有 12 行,然后被分组,结果如何仍然有 12 行?我检查了文档中的 cumcount 并应该给出每个组的大小,我认为这个例子不适合那个。
    • 没问题。您可以查看print df.groupby( (df['a'].diff(1) != 0).astype('int').cumsum() ).cumcount()。它只计算groups 中从0len(group) - 1 的项目。请检查解释,第二块。
    【解决方案3】:

    Numpy 的ediff1d 函数在这里很有用

    inverted = a[::-1]
    index =  (numpy.ediff1d(inverted) == 0).argmax()
    a[index:] 
    

    【讨论】:

    • 有趣的函数,但它会检测任何两个相等的连续数字,而不仅仅是两个连续的零
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-07
    相关资源
    最近更新 更多