如何在pandas数据框中计算连续有序值的数量

5
我正在尝试从一个包含id、date和value列的数据框中获取连续0值的最大计数,在pandas上看起来像这样:
id    date       value
354   2019-03-01 0
354   2019-03-02 0
354   2019-03-03 0
354   2019-03-04 5
354   2019-03-05 5 
354   2019-03-09 7
354   2019-03-10 0
357   2019-03-01 5
357   2019-03-02 5
357   2019-03-03 8
357   2019-03-04 0
357   2019-03-05 0
357   2019-03-06 7
357   2019-03-07 7
540   2019-03-02 7
540   2019-03-03 8
540   2019-03-04 9
540   2019-03-05 8
540   2019-03-06 7
540   2019-03-07 5
540   2019-03-08 2 
540   2019-03-09 3
540   2019-03-10 2

期望的结果将按Id分组,并且看起来像这样:

id   max_consecutive_zeros
354  3
357  2
540  0

我用for循环实现了自己想要的功能,但是当你处理巨大的pandas数据框时,速度会变得非常慢。我找到了一些类似的解决方案,但完全无法解决我的问题。

3个回答

1

这是我们需要创建groupby的另一种方法,只需要使用id和这个新的键进行groupby

s=df.groupby('id').value.apply(lambda x : x.ne(0).cumsum())
df[df.value==0].groupby([df.id,s]).size().max(level=0).reindex(df.id.unique(),fill_value=0)
Out[267]: 
id
354    3
357    2
540    0
dtype: int64

对我来说太抽象了(我不习惯像lambda等一些东西),你能解释一下它是做什么的吗? - Wel

1
创建连续相同值的行的groupID m。然后,在idm上进行groupby,并调用value_counts,在多级索引上使用.loc仅切片右侧最后一个索引级别的0值。最后,通过duplicated过滤出重复的索引id并重新索引以创建id0计数为零的值。
m = df.value.diff().ne(0).cumsum().rename('gid')    
#Consecutive rows having the same value will be assigned same IDNumber by this command. 
#It is the way to identify a group of consecutive rows having the same value, so I called it groupID.

df1 = df.groupby(['id', m]).value.value_counts().loc[:,:,0].droplevel(-1)
#this groupby groups consecutive rows of same value per ID into separate groups.
#within each group, count number of each value and `.loc` to pick specifically only `0` because we only concern on the count of value `0`.

df1[~df1.index.duplicated()].reindex(df.id.unique(), fill_value=0)
#There're several groups of value `0` per `id`. We want only group of highest count. 
#`value_count` already sorted number of count descending, so we just need to pick 
#the top one of duplicates by slicing on True/False mask of `duplicated`.
#finally, `reindex` adding any `id` doesn't have value 0 in original `df`.
#Note: `id` is the column `id` in `df`. It is different from groupID `m` we create to use with groupby

Out[315]:
id
354    3
357    2
540    0
Name: value, dtype: int64

我不明白 df m 在那里的作用,你能否再解释一下? - Wel
@Wel:我不太善于言辞。但是,我会尽力在代码中添加一些解释。请查看更新内容。你也可以在控制台上解除每个命令的绑定,并分别运行每个命令以查看每个输出,从而更好地了解它们的用途。 - Andy L.
@Wel:仅出于美观的原因。groupbyvalue_count返回2级多索引系列。droplevel(-1)删除最后一级,仅保留第一级即id以显示所需的输出。 - Andy L.
将 m = df.value.diff().ne(0).cumsum().rename('gid') 解释一下:
  1. rename('gid') 重命名结果系列
  2. cumsum() 是累加函数(总是将前一个数加到当前数上)
  3. ne(0) 表示“对象是否不等于零?”,对象是前面函数的结果。因此它返回一个布尔值(True或False)
  4. diff() 是numpy方法,返回数组中每个数字与其左边邻居的差异(例如 [diff(arr[1,2,3,4,6]) = [1,1,1,2])
  5. df.value 指代在此计算中使用的Series。
- Nesha25
因此,目标是在数字相同的同时,差值与前一个值之间的ne()将继续返回False(即它等于零,即value减去previous_value为0)。但是,一旦value减去previous value不为0,我们就会得到一个True。一旦发生这种情况,cumsum就会获得一个非零值添加到列表中(1)。因此,GID增加1。保持在1个单位,直到出现另一个True值,此时GID再次上升。 - Nesha25
显示剩余3条评论

0

你可以做:

df.groupby('id').value.apply(lambda x : ((x.diff() !=0).cumsum()).where(x ==0,\
                                       np.nan).value_counts().max()).fillna(0)

输出

id
354    3.0
357    2.0
540    0.0
Name: value, dtype: float64

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接