
如下一个表,想每当 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 1 apake 2021-03-11 11:33:22 +08:00 为什么要 groubby('A'), 问题中没提到与 A 列有关 |
2 shyrock 2021-03-11 11:36:33 +08:00 df['D'] = np.where(df.C, df.groupby('B')['B'].cumsum(), 0) print(df) |
3 cassidyhere 2021-03-11 12:3355 +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) |
4 yaleyu OP @apake 问题没描述得太清楚,应该是对每个 A 列的数据,当 C 列从 false 变化为 true 时候,加总 false 到 true 对应的行的 B 列 |
5 yaleyu OP @shyrock 这个不行,B 列的值是不一样的,不能 groupby('B'),这里为了简化写成了 1, 1, 2, 2, 22, 22 等 |
6 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 |
7 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 |
8 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 |
9 milkpuff 2021-03-11 17:44:08 +08:00 我选择 for 循环。。 而且不要频繁对 pandas 切片和赋值,转成 numpy 操作速度提升一个数量级,最后结果再转到 pandas |
10 yaleyu OP @necomancer 这个牛逼,完全实现了需求,谢谢谢谢。 |
11 yaleyu OP @cassidyhere 这个好深奥,不过完全实现了需求,万分感谢。 |
12 yaleyu OP |