如何基于最大和分配组?

Cle*_*leb 5 python dataframe pandas

我有一个这样的数据框:

df = pd.DataFrame({'keys': list('aaaabbbbccccc'), 'values': [1, 5, 6, 8, 2, 4, 7, 7, 1, 1, 1, 1, 5]})

   keys  values
0     a       1
1     a       5
2     a       6
3     a       8
4     b       2
5     b       4
6     b       7
7     b       7
8     c       1
9     c       1
10    c       1
11    c       1
12    c       5
Run Code Online (Sandbox Code Playgroud)

此外,我有一个变量max_sum = 10

我想根据keys(ii)中的值为每行分配一个组(ii)max_sum每组不应该超过该值。

我的预期结果如下所示:

   keys  values  group
0     a       1      1
1     a       5      1
2     a       6      2
3     a       8      3
4     b       2      4
5     b       4      4
6     b       7      5
7     b       7      6
8     c       1      7
9     c       1      7
10    c       1      7
11    c       1      7
12    c       5      7
Run Code Online (Sandbox Code Playgroud)

因此,a组(15)中的前两个值的总和6小于10,因此它们在同一组中。如果现在也添加6max_sum将被超出,因此该值将归入组2。我们不能添加8到该组,否则max_sum将再次超出该组,因此我们定义了一个组3。值b和相同c

一个可以做

df['cumsum'] = df.groupby('keys')['values'].cumsum()

   keys  values  cumsum
0     a       1       1
1     a       5       6
2     a       6      12
3     a       8      20
4     b       2       2
5     b       4       6
6     b       7      13
7     b       7      20
8     c       1       1
9     c       1       2
10    c       1       3
11    c       1       4
12    c       5       9
Run Code Online (Sandbox Code Playgroud)

但我不知道如何从中获取组信息。

cs9*_*s95 6

我们要基于行的累加总和对行进行分区,因此我们使用cumsum,相对于取模数max_sum,然后找到差异以找到差异为负的点(以标记下一组)。我们还需要针对每个键执行此操作,因此上述整个操作都在GroupBy.apply调用内完成。

(df.groupby('keys')['values']
   .apply(lambda x: x.cumsum().mod(max_sum).diff())
   .fillna(-1)
   .lt(0)
   .cumsum())                 

0     1
1     1
2     2
3     3
4     4
5     4
6     5
7     6
8     7
9     7
10    7
11    7
12    7
Name: values, dtype: int64
Run Code Online (Sandbox Code Playgroud)

在下面的评论中,我写道:

@Cleb似乎我的回答是错误的。对于4、4、9、2,输出应为1、1、2、3,但我的代码将分配1、1、2、2,因为cumsum会折减值。

因此,这是我解决这个极端情况的解决方案。定义一个分配组的函数:

grp = {'grp': 0}  # better than `global`, at least
def func(V):
    cumsum = 0
    grp['grp'] += 1
    grps = []
    for v in V.tolist():
        cumsum += v
        if cumsum > max_sum:
            cumsum = v
            grp['grp'] += 1
        grps.append(grp['grp'])

    return pd.Series(grps)
Run Code Online (Sandbox Code Playgroud)

现在,致电apply

df.groupby('keys')['values'].apply(func).values
# array([1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 7, 7, 7])
Run Code Online (Sandbox Code Playgroud)


Erf*_*fan 4

我们可以创建两个掩码,并基于它创建一个True/False数组。

  • m1:所有大于的值max_sum标记为TrueelseFalse
  • m2:前一行的值keys与当前行不同的行。

我们np.where基本上有以下伪代码:

当 m1m2 为 True 时,返回 True,否则返回 False

现在我们可以将Trueand转换False为 1 / 0,因为它们是布尔值:

True + True

2
Run Code Online (Sandbox Code Playgroud)

cumsum这就是最后一行的原因。

代码

max_sum = 10

m1 = df.groupby('keys')['values'].cumsum().gt(max_sum)  # all values which are greater than max_sum 
m2 = df['keys'].ne(df['keys'].shift())                  # all rows where keys change

df['group'] = np.where(m1 | m2, True, False).cumsum()


   keys  values  group
0     a       1      1
1     a       5      1
2     a       6      2
3     a       8      3
4     b       2      4
5     b       4      4
6     b       7      5
7     b       7      6
8     c       1      7
9     c       1      7
10    c       1      7
11    c       1      7
12    c       5      7
Run Code Online (Sandbox Code Playgroud)