首页 > 解决方案 > 在特定条件下聚合 Dataframe 中的行值

问题描述

我有一个dataframe有两列的。我想构建 value 为的所有连续行的column 0总和-1

我的dataframe样子…… 像这样:

 0   2
 1   3
-1   4
-1   7
 0   2
-1   0
-1   1
-1   3
 5   0

所需的输出应该是这样的:

 0   2
 1   3
-1   11
 0   2
-1   4
 5   0

第二列中的值都等于或大于零。如果有帮助,第一列的值等于或大于-1。我的方法将是一个循环,当我发现 a 时,我创建第二个dataframe然后推回每个不等于-1并累积的值-1,但我想,这种方法效率不高。伪代码:

sum = 0
found = False
for row in dataframe:
   if row[0] != -1:
       if found:
           new_df.append([-1, sum])
           sum = 0
           found = False
       new_df.append(row)
   elif row[0] == -1:
       found = True
       sum += row[1]

我可以使用内置的 python 或 pandas 函数来实现我的目标吗?

标签: pythonpandasdataframe

解决方案


在我看来,这里有必要创建Series100% 的帮助组的值是否与外部值相同-1,因此添加0.5index区分:

df = df.reset_index(drop=True)

m = df['a'] == -1
s = m.ne(m.shift()).cumsum()[m].reindex(df.index).fillna(df.index.to_series().add(.5))
df = df.groupby(s).agg({'a':'first', 'b':'sum'}).reset_index(drop=True)
print (df)
   a   b
0  0   2
1  1   3
2 -1  11
3 -1   4
4  0   2
5  5   0

说明

df = pd.DataFrame({'a': [0, 1, -1, -1, 0, -1, -1, -1, 5],
                   'b': [2, 3, 4, 7, 2, 0, 1, 3, 0]})
print (df)
   a  b
0  0  2
1  1  3
2 -1  4
3 -1  7
4  0  2
5 -1  0
6 -1  1
7 -1  3
8  5  0

如有必要,首先创建默认索引,因为解决方案中使用了唯一的索引值:

df = df.reset_index(drop=True)

-1然后为和另一个值创建连续组:

m = df['a'] == -1
print (df.assign(groups = m.ne(m.shift()).cumsum()))
   a  b  groups
0  0  2       1
1  1  3       1
2 -1  4       2
3 -1  7       2
4  0  2       3
5 -1  0       4
6 -1  1       4
7 -1  3       4
8  5  0       5

然后仅使用(by mask )过滤-1值,另一个不匹配的值通过以下方式转换为s :boolean indexingbNaNSeries.reindex

m = df['a'] == -1
print (df.assign(groups = m.ne(m.shift()).cumsum(),
                 filtered = m.ne(m.shift()).cumsum()[m].reindex(df.index)))

   a  b  groups  filtered
0  0  2       1       NaN
1  1  3       1       NaN
2 -1  4       2       2.0
3 -1  7       2       2.0
4  0  2       3       NaN
5 -1  0       4       4.0
6 -1  1       4       4.0
7 -1  3       4       4.0
8  5  0       5       NaN

然后用索引值替换缺失值0.5- 永远不会在组 for-1和被替换NaNs 的值之间发生碰撞:

m = df['a'] == -1
print (df.assign(groups = m.ne(m.shift()).cumsum(),
                 filtered = m.ne(m.shift()).cumsum()[m].reindex(df.index),
                 idx = df.index.to_series().add(.5),
                 groups1 = m.ne(m.shift()).cumsum()[m].reindex(df.index).fillna(df.index.to_series().add(.5))))

   a  b  groups  filtered  idx  groups1
0  0  2       1       NaN  0.5      0.5
1  1  3       1       NaN  1.5      1.5
2 -1  4       2       2.0  2.5      2.0
3 -1  7       2       2.0  3.5      2.0
4  0  2       3       NaN  4.5      4.5
5 -1  0       4       4.0  5.5      4.0
6 -1  1       4       4.0  6.5      4.0
7 -1  3       4       4.0  7.5      4.0
8  5  0       5       NaN  8.5      8.5

然后将助手Series传递给groupby并聚合sum第二列和first第一列GroupBy.agg,最后通过DataFrame.reset_indexwith删除索引drop=True

df = df.groupby(s).agg({'a':'first', 'b':'sum'}).reset_index(drop=True)
print (df)
   a   b
0  0   2
1  1   3
2 -1  11
3 -1   4
4  0   2
5  5   0

另一种更简单且性能更好的解决方案:

df = df.reset_index(drop=True)

m = df['a'] == -1
s = df.reset_index()
      .groupby(m.ne(m.shift()).cumsum()[m])
      .agg({'index':'first', 'b':'sum'})
      .set_index('index')
      .assign(a = -1)

df = df[~m].append(s, sort=True).sort_index()
print (df)
   a   b
0  0   2
1  1   3
2 -1  11
4  0   2
5 -1   4
8  5   0

说明

首先是必要的默认索引:

df = df.reset_index(drop=True)

然后将 column by-1与布尔掩码进行比较:

m = df['a'] == -1

将索引转换为reset_index不带drop参数的列:

print (df.reset_index())
   index  a  b
0      0  0  2
1      1  1  3
2      2 -1  4
3      3 -1  7
4      4  0  2
5      5 -1  0
6      6 -1  1
7      7 -1  3
8      8  5  0

使用 和创建连续的组,shift并按组cumsum的掩码过滤-1

print (m.ne(m.shift()).cumsum()[m])
2    2
3    2
5    4
6    4
7    4
Name: a, dtype: int32

first索引列和列聚合:sumb

print (df.reset_index()
        .groupby(m.ne(m.shift()).cumsum()[m])
        .agg({'index':'first', 'b':'sum'}))

     index   b
a             
2.0      2  11
4.0      5   4

index列转换为indexby DataFrame.set_index

print(df.reset_index()
      .groupby(m.ne(m.shift()).cumsum()[m])
      .agg({'index':'first', 'b':'sum'})
      .set_index('index'))
        b
index    
2      11
5       4

添加a具有常量-1的列DataFrame.assign

s = (df.reset_index()
      .groupby(m.ne(m.shift()).cumsum()[m])
      .agg({'index':'first', 'b':'sum'})
      .set_index('index')
      .assign(a = -1))
print (s)
        b  a
index       
2      11 -1
5       4 -1

最后用反掩码过滤掉-1行by :boolean indexing~

print (df[~m])
  a  b
0  0  2
1  1  3
4  0  2
8  5  0

然后通过以下方式将新数据添加到原始数据DataFrame.append

print (df[~m].append(s, sort=True))
   a   b
0  0   2
1  1   3
4  0   2
8  5   0
2 -1  11
5 -1   4

最后DataFrame.sort_index是相同的顺序:

print (df[~m].append(s, sort=True).sort_index())
   a   b
0  0   2
1  1   3
2 -1  11
4  0   2
5 -1   4
8  5   0

推荐阅读