【问题标题】:Groupby Roll up or Roll Down for any kind of aggregatesGroupby Roll up 或 Roll Down 用于任何类型的聚合
【发布时间】:2021-10-19 16:43:35
【问题描述】:

TL;DR:我们如何在 pandas 中使用任何类型的聚合来实现类似于 Group By Roll Up 的效果? (此学期归功于@Scott Boston

我有以下数据框:

       P   Q  R     S  T
0   PLAC  NR  F   HOL  F
1   PLAC  NR  F  NHOL  F
2   TRTB  NR  M  NHOL  M
3   PLAC  NR  M  NHOL  M
4   PLAC  NR  F  NHOL  F
5   PLAC   R  M  NHOL  M
6   TRTA   R  F   HOL  F
7   TRTA  NR  F   HOL  F
8   TRTB  NR  F  NHOL  F
9   PLAC  NR  F  NHOL  F
10  TRTB  NR  F  NHOL  F
11  TRTB  NR  M  NHOL  M
12  TRTA  NR  F   HOL  F
13  PLAC  NR  F   HOL  F
14  PLAC   R  F  NHOL  F

对于['Q', 'R', 'S', 'T'] 的列列表,我想在以下 4 个分组列列表中计算P 列上的一些聚合:

  1. ['Q']
  2. ['Q', 'R']
  3. ['Q', 'R', 'S']
  4. ['Q', 'R', 'S', 'T']

我已经编写了代码以将上述数据帧分组到越来越多的列中,并计算每个 groupby 对象的聚合(使用 count 为简单起见),最后将它们连接起来:

cols = list('QRST')
aggCol = 'P'
groupCols = []
result = []
for col in cols:
    groupCols.append(col)
    result.append(df.groupby(groupCols)[aggCol].agg(count='count').reset_index())
result = pd.concat(result)[groupCols+['count']]

但是,我强烈感觉上述方法在 CPU 时间方面效率不高。有没有更有效的方法来对不断增加的列数应用聚合以进行分组?

为什么我认为它效率不高是因为:对于上述值,在第一次迭代中,它将数据框分组到 Q 列然后计算聚合。然后在下一次迭代中,它将数据帧分组到 QR,这意味着它再次需要按 Q 然后 R 分组,但在第一次迭代中它已经按 Q 分组,所以重复相同的操作。如果有一些方法可以利用之前创建的组,我认为它会很有效。

输出:

    Q    R     S    T  count
0  NR  NaN   NaN  NaN     12
1   R  NaN   NaN  NaN      3
0  NR    F   NaN  NaN      9
1  NR    M   NaN  NaN      3
2   R    F   NaN  NaN      2
3   R    M   NaN  NaN      1
0  NR    F   HOL  NaN      4
1  NR    F  NHOL  NaN      5
2  NR    M  NHOL  NaN      3
3   R    F   HOL  NaN      1
4   R    F  NHOL  NaN      1
5   R    M  NHOL  NaN      1
0  NR    F   HOL    F      4
1  NR    F  NHOL    F      5
2  NR    M  NHOL    M      3
3   R    F   HOL    F      1
4   R    F  NHOL    F      1
5   R    M  NHOL    M      1

我已经研究了Is there an equivalent of SQL GROUP BY ROLLUP in Python pandas?Pandas Pivot tables row subtotals,它们在我的情况下不起作用,我已经尝试过它们,即这些方法只能用于获取计数,并且即使对于唯一计数,当相同时也会立即失败标识符出现多个值:

pd.pivot_table(df, aggCol, columns=cols, aggfunc='count', margins=True).T.reset_index()
    Q    R     S  T  P
0  NR    F   HOL  F  4
1  NR    F  NHOL  F  5
2  NR    M  NHOL  M  3
3  NR  All           3
4   R    F   HOL  F  1
5   R    F  NHOL  F  1
6   R    M  NHOL  M  1
7   R  All           3

更新

为了避免根据评论中的建议获取 count 时产生不必要的混淆,我已将其添加为聚合的平均值,将 P 列更改为数字类型:

    P   Q  R     S  T
0   9  NR  F   HOL  F
1   7  NR  F  NHOL  F
2   3  NR  M  NHOL  M
3   9  NR  M  NHOL  M
4   1  NR  F  NHOL  F
5   0   R  M  NHOL  M
6   1   R  F   HOL  F
7   7  NR  F   HOL  F
8   2  NR  F  NHOL  F
9   2  NR  F  NHOL  F
10  1  NR  F  NHOL  F
11  2  NR  M  NHOL  M
12  3  NR  F   HOL  F
13  6  NR  F   HOL  F
14  0   R  F  NHOL  F

cols = list('QRST')
cols = list('QRST')
aggCol = 'P'
groupCols = []
result = []
for col in cols:
    groupCols.append(col)
    result.append(df.groupby(groupCols)[aggCol]
                  .agg(agg=np.mean)
                  .round(2).reset_index())
result = pd.concat(result)[groupCols+['agg']]
>>> result
    Q    R     S    T   agg
0  NR  NaN   NaN  NaN  4.33
1   R  NaN   NaN  NaN  0.33
0  NR    F   NaN  NaN  4.22
1  NR    M   NaN  NaN  4.67
2   R    F   NaN  NaN  0.50
3   R    M   NaN  NaN  0.00
0  NR    F   HOL  NaN  6.25
1  NR    F  NHOL  NaN  2.60
2  NR    M  NHOL  NaN  4.67
3   R    F   HOL  NaN  1.00
4   R    F  NHOL  NaN  0.00
5   R    M  NHOL  NaN  0.00
0  NR    F   HOL    F  6.25
1  NR    F  NHOL    F  2.60
2  NR    M  NHOL    M  4.67
3   R    F   HOL    F  1.00
4   R    F  NHOL    F  0.00
5   R    M  NHOL    M  0.00

【问题讨论】:

  • 只是一个想法:如何先制作['Q','R','S','T']-grouped 结果,然后使用索引计算“更高级别”组计数?
  • @Bill 感谢您的关注。这不仅仅是计数,而是几个不同的聚合。我只是使用count 来简单地理解实际的问题描述。而且,是的,你所说的似乎是正确的方法,用最多的列分组,然后计算一次下降一个级别的聚合。
  • 代替count,尝试使用mean进行说明;这将立即让读者关注困难的部分(因为mean 不具有关联性;有人会说它不能以单子方式组合)。
  • 太棒了;我有一个通用的解决方案。有时groupby 将输出压缩到Series 时会出现问题。耐心...
  • 在我的回答中,我还添加了一个可选的总计(可以使用total=False 禁用它)。这与 SQL 的“group by roll up”一致,其中包括总计(所有分组列NaN)。

标签: python pandas dataframe pandas-groupby


【解决方案1】:

我觉得这样效率高一点:

b = df.groupby(cols)[aggCol].count()
l = list(range(b.index.nlevels-1))
p = [b]
while l:
    p.append(b.groupby(level=l).sum())
    l.pop()

result = pd.concat(p)

时间安排:

每个循环 7.4 ms ± 55.5 µs(7 次运行的平均值 ± 标准偏差,每次 100 个循环)

对比原版

每个循环 20.7 ms ± 300 µs(平均值 ± 标准偏差,7 次运行,每次 10 个循环)

使用 sum 而不是每次计算每个所有元素。对所有元素计数一次,然后对索引减少的级别求和。


使用mean 或求平均值,我们可以使用@PierreD 建议加上总和和计数然后聚合:

from itertools import zip_longest
cols = list('QRST')
aggCol = 'P'
b = df.groupby(cols)[aggCol].agg(['sum', 'count'])
l = list(range(b.index.nlevels-1))
p = [b]
while l:
    p.append(b.groupby(level=l).sum())
    l.pop()

result = pd.concat(p)
result = result.assign(avg=result['sum']/result['count']).drop(['sum', 'count'], axis=1)
result 

result.index = pd.MultiIndex.from_arrays(list(zip_longest(*result.index)))

输出:

                       avg
(NR, F, HOL, F)   6.250000
(NR, F, NHOL, F)  2.600000
(NR, M, NHOL, M)  4.666667
(R, F, HOL, F)    1.000000
(R, F, NHOL, F)   0.000000
(R, M, NHOL, M)   0.000000
(NR, F, HOL)      6.250000
(NR, F, NHOL)     2.600000
(NR, M, NHOL)     4.666667
(R, F, HOL)       1.000000
(R, F, NHOL)      0.000000
(R, M, NHOL)      0.000000
(NR, F)           4.222222
(NR, M)           4.666667
(R, F)            0.500000
(R, M)            0.000000
NR                4.333333
R                 0.333333

【讨论】:

  • 感谢您的回答,是的,它非常有效。但是问题不在于仅仅获得计数,它可以是任何聚合,我使用计数来简化操作,这样实际的问题描述就不会被掩盖。我关心的是对数据框进行分组。无论如何,感谢您的努力。
  • @ThePyGuy 是的。除了在每个级别进行聚合之外,我认为没有其他有效的方法了。 Pandas,没有 'with rollup' feature 像 TransAct Sql 那样使用 is group by 子句。
  • @ThePyGuy 也许你可以写它并添加到熊猫库中!
  • 是的,pandas 不允许对 groupby 对象进行分组,也不允许从 group by 对象中提取级别,因为它被存储为字典,其中键作为值的元组索引。
  • 感谢更新的解决方案,这是倒数第二行的第二个索引中的错字吗?具有值R
【解决方案2】:

基于@ScottBoston 的思想(渐进式聚合,即在之前的聚合结果上反复聚合),我们可以对聚合函数做一些相对通用的事情,如果该函数可以表示为函数((f3 ∘ f2 ∘ f2 ∘ ... ∘ f1)(x),或者换句话说:f3(f2(f2(...(f1(x))))))。

例如,sum 可以正常工作,因为sum 是关联的,所以组和的总和就是整体的总和。

对于count,初始函数(f1)确实是count,但f2必须是sum,最后f3必须是身份。

对于mean,初始函数 (f1) 必须产生两个数量:sumcount。中间函数f2可以是sum,那么最终函数(f3)必须是两个量的比值。

这是一个粗略的模板,定义了一些函数。作为额外的奖励,该函数还可以选择生成总计:

# think map-reduce: first map, then reduce (arbitrary number of times), then map to result

myfuncs = {
    'sum': [sum, sum],
    'prod': ['prod', 'prod'],
    'count': ['count', sum],
    'set': [set, lambda g: set.union(*g)],
    'list': [list, sum],
    'mean': [[sum, 'count'], sum, lambda r: r[0]/r[1]],
    'var': [
        [lambda x: (x**2).sum(), sum, 'count'],
        sum,
        lambda r: (r[0].sum() - r[1].sum()**2 / r[2]) / (r[2] - 1)],
    'std': [
        [lambda x: (x**2).sum(), sum, 'count'],
        sum,
        lambda r: np.sqrt((r[0].sum() - r[1].sum()**2 / r[2]) / (r[2] - 1))],
}

totalCol = '__total__'
def agg(df, cols, aggCol, fun, total=True):
    if total:
        cols = [totalCol] + cols
        df = df.assign(__total__=0)
    funs = myfuncs[fun]
    b = df.groupby(cols).agg({aggCol: funs[0]})
    frames = [b.reset_index()]
    for k in range(1, len(cols)):
        b = b.groupby(cols[:-k]).agg(funs[1])
        frames.append(b.reset_index())
    result = pd.concat(frames).reset_index(drop=True)
    result = result[frames[0].columns]
    if len(funs) > 2:
        s = result[aggCol].apply(funs[2], axis=1)
        result = result.drop(aggCol, axis=1, level=0)
        result[aggCol] = s
        result.columns = result.columns.droplevel(-1)
    if total:
        result = result.drop(columns=[totalCol])
    return result

示例

cols = list('QRST')
aggCol = 'P'

>>> agg(df, cols, aggCol, 'count')
      Q    R     S    T   P
0    NR    F   HOL    F   4
1    NR    F  NHOL    F   5
2    NR    M  NHOL    M   3
3     R    F   HOL    F   1
..  ...  ...   ...  ...  ..
15    R    M   NaN  NaN   1
16   NR  NaN   NaN  NaN  12
17    R  NaN   NaN  NaN   3
18  NaN  NaN   NaN  NaN  15
>>> agg(df, cols, aggCol, 'mean')
      Q    R     S    T         P
0    NR    F   HOL    F  6.250000
1    NR    F  NHOL    F  2.600000
2    NR    M  NHOL    M  4.666667
3     R    F   HOL    F  1.000000
..  ...  ...   ...  ...       ...
15    R    M   NaN  NaN  0.000000
16   NR  NaN   NaN  NaN  4.333333
17    R  NaN   NaN  NaN  0.333333
18  NaN  NaN   NaN  NaN  3.533333
>>> agg(df, cols, aggCol, 'sum')
      Q    R     S    T   P
0    NR    F   HOL    F  25
1    NR    F  NHOL    F  13
2    NR    M  NHOL    M  14
3     R    F   HOL    F   1
..  ...  ...   ...  ...  ..
15    R    M   NaN  NaN   0
16   NR  NaN   NaN  NaN  52
17    R  NaN   NaN  NaN   1
18  NaN  NaN   NaN  NaN  53
>>> agg(df, cols, aggCol, 'set')
      Q    R     S    T                      P
0    NR    F   HOL    F           {9, 3, 6, 7}
1    NR    F  NHOL    F              {1, 2, 7}
2    NR    M  NHOL    M              {9, 2, 3}
3     R    F   HOL    F                    {1}
..  ...  ...   ...  ...                    ...
15    R    M   NaN  NaN                    {0}
16   NR  NaN   NaN  NaN     {1, 2, 3, 6, 7, 9}
17    R  NaN   NaN  NaN                 {0, 1}
18  NaN  NaN   NaN  NaN  {0, 1, 2, 3, 6, 7, 9}
>>> agg(df, cols, aggCol, 'std')
      Q    R     S    T         P
0    NR    F   HOL    F  2.500000
1    NR    F  NHOL    F  2.509980
2    NR    M  NHOL    M  3.785939
3     R    F   HOL    F       NaN
..  ...  ...   ...  ...       ...
15    R    M   NaN  NaN       NaN
16   NR  NaN   NaN  NaN  3.055050
17    R  NaN   NaN  NaN  0.577350
18  NaN  NaN   NaN  NaN  3.181793

注意事项

  • 代码并不像我希望的那样“纯粹”。有两个原因:

    1. groupby 喜欢对结果的形状做一些魔术。例如,在某些情况下(但并非总是如此,奇怪的是),如果只有一个结果组,则输出有时会被压缩到 Series

    2. set 上的 pandas 算法有时似乎是虚假的,或者充其量是挑剔的。我最初的定义是:'set': [set, sum],这运行得相当好(熊猫似乎有时明白.agg(sum)set对象的Series上,最好应用set.union ),但奇怪的是,在某些情况下,我们会得到 NaN 结果。

  • 这仅适用于单个aggCol

  • stdvar 的表达式相对幼稚。如需提高数值稳定性,请参阅Standard Deviation: Rapid calculation methods

速度

自从最初发布此答案以来,@U12-Forward 已提出another solution。经过一番清理(例如,不使用递归,并将 agg dtype 更改为所需的任何内容,而不是 object,此解决方案变为:

def v_u12(df, cols, aggCol, fun):
    newdf = pd.DataFrame(columns=cols)
    for count in range(1, len(cols)+1):
        groupcols = cols[:count]
        newdf = newdf.append(
            df.groupby(groupcols)[aggCol].agg(fun).reset_index().reindex(columns=groupcols + [aggCol]),
            ignore_index=True,
        )
    return newdf

为了比较速度,让我们生成任意大小的 DataFrame:

def gen_example(n, m=4, seed=-1):
    if seed >= 0:
        np.random.seed(seed)
    aggCol = 'v'
    cols = list(ascii_uppercase)[:m]
    choices = [['R', 'NR'], ['F', 'M'], ['HOL', 'NHOL']]
    df = pd.DataFrame({
        aggCol: np.random.uniform(size=n),
        **{
            k: np.random.choice(choices[np.random.randint(0, len(choices))], n)
            for k in cols
        }})
    return df

# example
>>> gen_example(8, 5, 0)
          v   A  B  C  D   E
0  0.548814   R  M  F  M  NR
1  0.715189   R  M  M  M   R
2  0.602763  NR  M  M  F   R
3  0.544883   R  F  F  M  NR
4  0.423655  NR  M  F  F  NR
5  0.645894  NR  F  M  M   R
6  0.437587   R  M  F  M  NR
7  0.891773   R  F  M  M   R

我们现在可以使用出色的 perfplot 包以及一些定义来比较各种大小的速度:

m = 4
aggCol, *cols = gen_example(2, m).columns
fun = 'mean'

def ours(df):
    funname = fun if isinstance(fun, str) else fun.__name__
    return agg(df, cols, aggCol, funname, total=False)

def u12(df):
    return v_u12(df, cols, aggCol, fun)

def equality_check(a, b):
    a = a.sort_values(cols).reset_index(drop=True)
    b = b.sort_values(cols).reset_index(drop=True)
    non_numeric = a[aggCol].dtype == 'object'
    if non_numeric:
        return a[cols+[aggCol]].equals(b[cols+[aggCol]])
    return a[cols].equals(b[cols]) and np.allclose(a[aggCol], b[aggCol])


perfplot.show(
    time_unit='auto',
    setup=lambda n: gen_example(n, m),
    kernels=[ours, u12],
    n_range=[2 ** k for k in range(4, 21)],
    equality_check=equality_check,
    xlabel=f'n rows\n(m={m} columns, fun={fun})'
)

下面是几个聚合函数和m值的比较(y轴是平均时间:越低越好):

m fun perfplot
4 'mean'
10 'mean'
10 'sum'
4 'set'

对于非关联函数(例如'mean'),我们的“渐进式重新聚合”需要跟踪多个值(例如,对于meansumcount),所以对于相对小DataFrames,速度大约是u12的两倍。但随着规模的增长,重新聚合的增益会克服这一点,ours 变得更快。

【讨论】:

  • 哇!好的。感谢分享!
【解决方案3】:

Pandas 的 groupby 功能用途广泛,可以自定义功能。我正在使用返回计数的 lambda 提供解决方案,但您可以轻松替换 np.min 或 np.max 或其他自定义函数。请记住,当在 groupby 的嵌套级别上递归应用时,这些函数中的任何一个都应该是有意义的(所以 count、min、max 都有意义;但是如果您有诸如 mean 之类的统计函数,您将丢失所需的信息计算更高分组的正确聚合)。

df=pd.DataFrame.from_records(
[['PLAC','NR','F','HOL','F'],
['PLAC','NR',  'F',  'NHOL',  'F'],
['TRTB','NR',  'M',  'NHOL',  'M'],
['PLAC','NR',  'M',  'NHOL',  'M'],
['PLAC','NR',  'F',  'NHOL',  'F'],
['PLAC','R', 'M', 'NHOL',  'M'],
['TRTA','R',  'F',   'HOL',  'F'],
['TRTA','NR',  'F',   'HOL',  'F'],
['TRTB','NR',  'F',  'NHOL',  'F'],
['PLAC','NR',  'F',  'NHOL',  'F'],
['TRTB','NR',  'F',  'NHOL',  'F'],
['TRTB','NR',  'M',  'NHOL',  'M'],
['TRTA','NR',  'F',   'HOL',  'F'],
['PLAC','NR',  'F',   'HOL',  'F'],
['PLAC','R',  'F',  'NHOL',  'F']],
columns = ['P','Q','R','S','T'])

首先,使用最精细的分组定义一个 groupby-dataframe:

grdf = df.groupby(['Q','R','S','T'])['P'].apply(lambda x:len(x)).to_frame()

现在使用 this 数据帧的 unstack() 方法来连续获取粒度较小的分组级别的聚合。例如,在上一级索引为 ['Q','R','S']:

df2 = df.unstack()
result2 = df2.sum(axis=1).rename(str(df2.index.names)).to_frame()

result2 将如下所示:

类似地,计算所需的所有分组级别的聚合,并使用这样的函数将它们全部附加到同一数据帧(理想情况下,您可以将其设为递归函数,但我保持简单,以便轻松查看流程):

def combine_aggregates(df):
    #if type(grdf) == pd.core.frame.DataFrame:
    df1 = df
    result1 = df.sum(axis=1).rename(str(df1.index.names)).to_frame()
    df2 = df1.unstack()
    result2 = df2.sum(axis=1).rename(str(df2.index.names)).to_frame()
    df3 = df2.unstack()
    result3 = df3.sum(axis=1).rename(str(df3.index.names)).to_frame()
    df4 = df3.unstack()
    result4 = df4.sum(axis=1).rename(str(df4.index.names)).to_frame()

    return result1.append(result2).append(result3).append(result4)



combine_aggregates(grdf)     

最终的输出是:

【讨论】:

  • 我认为你没有仔细阅读这个问题,我已经明确提到建议的方法应该适用于我们通常可以在 pandas groupby 对象上执行的任何类型的聚合。您只是在每个级别取消堆叠并调用sum,这只是计数而已。无论如何,感谢您的努力。
【解决方案4】:

除了索引和一个额外的引擎参数之外,大部分代码都是相同的

我预设了索引,然后一次一个地分组

为了提高性能,我尝试将 numba 用于数字类型 Enhancingperf。似乎取决于 df 的大小,您可以在 numba 中添加并行的 nogil 选项。

Numba 的第一次执行在编译时可能会很慢,但后续执行应该会更快

l = list('QRST')
df1 = df1.set_index(l)
result = [
    df1.groupby(level=l[:i+1])['P'].agg(np.mean, engine='numba').round(2).reset_index()
    for i in range(4)
]
pd.concat(result)

【讨论】:

  • 感谢您的回答,但这不是我想要的,使用numba 作为引擎是一个可选参数,我们大部分时间可以在现有优化之上使用它。
【解决方案5】:

解决方案:

也许你可以用递归来试试这个。

如下:

newdf = pd.DataFrame(columns=df.columns)
cols = list('QRST')
aggCol = 'P'
def aggregation(cols, origcols, aggCol, df, count=1):
    global newdf
    cols = origcols[:count]
    count += 1
    newdf = newdf.append(df.groupby(cols)[aggCol].agg('mean').round(2).reset_index().T.reindex(origcols + [aggCol]).T, ignore_index=True)
    if cols != origcols:
        aggregation(cols, origcols, aggCol, df, count)

aggregation(cols, cols, aggCol, df)
newdf['agg'] = newdf.pop(aggCol)
print(newdf)

输出:

     Q    R     S    T   agg
0   NR  NaN   NaN  NaN  4.33
1    R  NaN   NaN  NaN  0.33
2   NR    F   NaN  NaN  4.22
3   NR    M   NaN  NaN  4.67
4    R    F   NaN  NaN   0.5
5    R    M   NaN  NaN     0
6   NR    F   HOL  NaN  6.25
7   NR    F  NHOL  NaN   2.6
8   NR    M  NHOL  NaN  4.67
9    R    F   HOL  NaN     1
10   R    F  NHOL  NaN     0
11   R    M  NHOL  NaN     0
12  NR    F   HOL    F  6.25
13  NR    F  NHOL    F   2.6
14  NR    M  NHOL    M  4.67
15   R    F   HOL    F     1
16   R    F  NHOL    F     0
17   R    M  NHOL    M     0

时间安排:

使用以下代码计时(运行 5000 次):

import time

u11time1 = time.time()

for i in range(5000):
    df = pd.read_clipboard()
    newdf = pd.DataFrame(columns=df.columns)
    cols = list('QRST')
    aggCol = 'P'
    def aggregation(cols, origcols, aggCol, df, count=1):
        global newdf
        cols = origcols[:count]
        count += 1
        newdf = newdf.append(df.groupby(cols)[aggCol].agg('mean').round(2).reset_index().T.reindex(origcols + [aggCol]).T, ignore_index=True)
        if cols != origcols:
            aggregation(cols, origcols, aggCol, df, count)

    aggregation(cols, cols, aggCol, df)
    newdf['agg'] = newdf.pop(aggCol)

u11time2 = time.time()

print('u11 time:', u11time2 - u11time1)

thepyguytime1 = time.time()

for i in range(5000):
    df = pd.read_clipboard()
    cols = list('QRST')
    aggCol = 'P'
    groupCols = []
    result = []
    for col in cols:
        groupCols.append(col)
        result.append(df.groupby(groupCols)[aggCol].agg(count='count').reset_index())
    result = pd.concat(result)[groupCols+['count']]

thepyguytime2 = time.time()

print('ThePyGuy time:', thepyguytime2 - thepyguytime1)

给予:

u11 time: 120.2678394317627
ThePyGuy time: 153.01533579826355

我的代码快了 33 秒...

但如果你只运行它几次,即 10 次,我的代码通常仍然会获胜,但幅度不会那么大。但是对于更多的迭代,即 5000 次,我的代码执行得比你原来的 for 循环代码快得多。

结论是:我的解决方案运行得更快:)

【讨论】:

  • 我没有看到递归优于相同逻辑的优势,展开为循环 (for count in range(1, len(cols)+1): ...)。此外,您的版本会产生一个 agg dtype object 列,这是不可取的。初始化 newdf = pd.DataFrame(columns=cols) 可以解决这个问题。 groupby 表达式(附加到newdf 的结果)可以简化为:df.groupby(cols)[aggCol].agg(fun).reset_index().reindex(columns=cols + [aggCol])。最后,对于大于一定大小的df,这比“渐进式重新聚合”的方法要慢。
  • 查看我的updated answer,它对不同数据帧大小进行了速度比较... ;-)
  • @U12-Forward,由于某种原因,我不断收到 TypeError: 无法将 PLACPLACTRTBPLACPLACPLACTRTATRTBPLACTRTBRTTBTRTAPLAC 转换为数字
  • @ThePyGuy 哦,你在用字符串吗?您是否要合并字符串?如果是这样,请尝试newdf = newdf.append(df.groupby(cols)[aggCol].agg(''.join).reset_index().T.reindex(origcols + [aggCol]).T, ignore_index=True)
  • 原始数据框有P 列作为count 的字符串,好的,让我试试数值
猜你喜欢
  • 2019-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-11
  • 1970-01-01
相关资源
最近更新 更多