又来求教 pandas 大拿了

2021-03-11 10:11:14 +08:00
 yaleyu

如下一个表,想每当 C 列为 False 时候,D 列为 0,为 True 时候,D 列为 B 列的上一次 C 列为 False 到当前列的加总

df = pd.DataFrame([['S1', 1, False], ['S1', 1, True],
    ['S2', 2, False], ['S2', 2, True], ['S2', 22, False], ['S2', 22, True],
    ['S3', 222, False], ['S3', 222, True]],
    columns=list('ABC'))
print(df)
    A    B      C
0  S1    1  False
1  S1    1   True
2  S2    2  False
3  S2    2   True
4  S2   22  False
5  S2   22   True
6  S3  222  False
7  S3  222   True

用 for 循环切片每次的 False 到 True 再处理可以得到想要的结果,但是总觉得效率不高。

用了下面的方法,得出来的结果不对,5 行 D 列应该是 44(22+22)而不是 48(2+2+22+22)

df['D'] = np.where(df.C, df.groupby('A')['B'].cumsum(), 0)
print(df)
    A    B      C    D
0  S1    1  False    0
1  S1    1   True    2
2  S2    2  False    0
3  S2    2   True    4
4  S2   22  False    0
5  S2   22   True   48
6  S3  222  False    0
7  S3  222   True  444
2372 次点击
所在节点    Python
12 条回复
apake
2021-03-11 11:33:22 +08:00
为什么要 groubby('A'), 问题中没提到与 A 列有关
shyrock
2021-03-11 11:36:33 +08:00
df['D'] = np.where(df.C, df.groupby('B')['B'].cumsum(), 0)
print(df)
cassidyhere
2021-03-11 12:33:55 +08:00
如果 A/B 没规律的话,可以用自定义 window rolling
from pandas.api.indexers import BaseIndexer
window_size = df.C.groupby((df.C != df.C.shift(1)).cumsum()).agg('sum').max() # 最大连续次数
class CustomIndexer(BaseIndexer):
def get_window_bounds(self, num_values, min_periods, center, closed):
start = np.empty(num_values, dtype=np.int64)
end = np.empty(num_values, dtype=np.int64)
for i in range(num_values):
end[i] = i + 1
j = i
while j > 0 and self.use_expanding[j]:
j -= 1
start[i] = j
return start, end
indexer = CustomIndexer(window_size=window_size, use_expanding=df.C)
res = df.B.rolling(indexer, min_periods=2).sum().fillna(0)
yaleyu
2021-03-11 16:15:19 +08:00
@apake 问题没描述得太清楚,应该是对每个 A 列的数据,当 C 列从 false 变化为 true 时候,加总 false 到 true 对应的行的 B 列
yaleyu
2021-03-11 16:18:33 +08:00
@shyrock 这个不行,B 列的值是不一样的,不能 groupby('B'),这里为了简化写成了 1, 1, 2, 2, 22, 22 等
necomancer
2021-03-11 16:58:23 +08:00
df.D = df.groupby(df.C.eq(False).cumsum()).cumsum().D
df.D[df.D <0]=0
necomancer
2021-03-11 17:03:40 +08:00
df = pd.DataFrame([['S1', 1, False], ['S1', 1, True],
['S2', 2, False], ['S2', 2, True], ['S2', 22, False], ['S2', 22, True],
['S3', 222, False], ['S3', 222, True]],
columns=list('ABC'))

df['D'] = np.diff(np.where(df.C, df.groupby('A')['B'].cumsum(), 0), axis=0, prepend=0)

df.D = df.groupby(df.C.eq(False).cumsum()).cumsum().D
df.D[df.D <0]=0
df
A B C D
0 S1 1 False 0
1 S1 1 True 2
2 S2 2 False 0
3 S2 2 True 2
4 S2 22 False 0
5 S2 22 True 44
6 S3 222 False 0
7 S3 222 True 396
necomancer
2021-03-11 17:09:53 +08:00
……我是智障

df['D'] = np.where(df.C, df.groupby(df.C.eq(False).cumsum()).B.cumsum(), 0)


df


A B C D
0 S1 1 False 0
1 S1 1 True 2
2 S2 2 False 0
3 S2 2 True 4
4 S2 22 False 0
5 S2 22 True 44
6 S3 222 False 0
7 S3 222 True 444
milkpuff
2021-03-11 17:44:08 +08:00
我选择 for 循环。。
而且不要频繁对 pandas 切片和赋值,转成 numpy 操作速度提升一个数量级,最后结果再转到 pandas
yaleyu
2021-03-11 18:05:09 +08:00
@necomancer 这个牛逼,完全实现了需求,谢谢谢谢。
yaleyu
2021-03-11 18:06:03 +08:00
@cassidyhere 这个好深奥,不过完全实现了需求,万分感谢。
yaleyu
2021-03-11 18:50:06 +08:00
@necomancer @cassidyhere 哦,不对,把原始数据改一下更接近真实数据,好像就有点问题了,原贴编辑不了了,回复排版会乱,再开一贴求教大家吧
https://www.v2ex.com/t/760789

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/760567

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX