在pandas中计算当前行之前空格的数量

3

我有一个DataFrame,其中一行是is_blank,表示该行是否为NaN。我想生成一个新特征,计算每组记录中当前行之前的NaN行数,这些记录被分组并按id分组。

下面是一个示例:

import pandas as pd
is_blank = [0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1]
id = [1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2]
outval = [0, 0, 1, 2, 0, 1, 2, 0, 0, 0, 0]
test_df = pd.DataFrame({'id': id, 'is_blank': is_blank, 'outval': outval})

这是一个玩具数据集的样子。我想要生成outval列。outval[3]为2,因为它之前有两个空行。然后outval[4]重置为零,因为在那一行之前没有空行。
In[2]: test_df
Out[2]: 
    id  is_blank  outval
0    1         0       0
1    1         1       0
2    1         1       1
3    1         0       2
4    1         1       0
5    1         1       1
6    1         1       2
7    2         0       0
8    2         0       0
9    2         0       0
10   2         1       0

目前我正在尝试一些类似于累积计数的形式,像这样:

In[3]: test_df.groupby(['id'])['is_blank'].cumsum().shift(1)
Out[3]: 
0     NaN
1     0.0
2     1.0
3     2.0
4     2.0
5     3.0
6     4.0
7     5.0
8     0.0
9     0.0
10    0.0

但显然计数器不会在组内重置,最终会计算所有空行。我正在研究expanding_apply选项,但无法完全理解其工作原理。

有没有关于如何高效解决这个问题的想法?

2个回答

3
您可以基于 is_blank 创建另一个组变量来重置 cumsum:
test_df['outval'] = (test_df.groupby([test_df.id, (test_df.is_blank.diff() != 0).cumsum()])
                     .is_blank.cumsum().groupby(test_df.id).shift().fillna(0))
test_df

在此输入图片描述

解析:

# create a group variable whose id increases when the blanks are not consecutive
g = (test_df.is_blank.diff() != 0).cumsum()
g

#0     1
#1     2
#2     2
#3     3
#4     4
#5     4
#6     4
#7     5
#8     5
#9     5
#10    6
#Name: is_blank, dtype: int64

# group data frame on both id and g, then do cumsum on the is_blank column
test_df.groupby([test_df.id, g]).is_blank.cumsum().groupby(test_df.id).shift().fillna(0)

#0     0.0
#1     0.0
#2     1.0
#3     2.0
#4     0.0
#5     1.0
#6     2.0
#7     0.0
#8     0.0
#9     0.0
#10    0.0
#Name: is_blank, dtype: float64

这个测试用例的输出很奇怪。假设只有一个 idis_blank = [0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0]; outval= [0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1];然而,期望的输出是: outval=[0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0]似乎在1后面有多个0的情况下会失败。 - sriramn
尝试这个代码:test_df['outval'] = (test_df.groupby([test_df.id, (test_df.is_blank.diff() != 0).cumsum()]).is_blank.cumsum().groupby(test_df.id).shift().fillna(0)) - Psidom
@Psiodom:这个很好用。我可以看出之前的那个有问题。你想用这个解决方案更新主答案吗? - sriramn

3
一种矢量化的方法
b = np.append(0, test_df.is_blank.values[:-1])
i = test_df.id.values

bc = b.cumsum()
w1 = np.where(b == 0)[0]
w2 = np.append(0, np.where(i[:-1] != i[1:])[0] + 1)
bd2 = bc[w2].repeat(np.diff(np.append(r[w2], k)))
bd1 = bc[w1].repeat(np.diff(np.append(r[w1], k)))

test_df.assign(outval=bc - np.fmax(bd1, bd2))

    id  is_blank  outval
0    1         0       0
1    1         1       0
2    1         1       1
3    1         0       2
4    1         1       0
5    1         1       1
6    1         1       2
7    2         0       0
8    2         0       0
9    2         0       0
10   2         1       0

这是为了找到“重置”的位置,也就是当id发生改变且is_blank为零时的位置。
bcis_blank的累计总和,并减去适当的值以进行“重置”。
通过牺牲一些直观性,你可以获得更快的运行时间。 简单测试时间 enter image description here

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