Pandas DataFrame多重操作的进度条与.agg()函数

3

我想对大型数据集应用 .agg pandas操作。

举个例子,我有以下代码:

from tqdm import tqdm
import pandas as pd
df = pd.DataFrame({"A":[1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0], 
                   "B":[1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0], 
                   "C":[1.0, 1.5, 2.0, 2.0, 3.0, 4.0, 5.0, 6.0, 10.0],
                   "D":[2.0, 5.0, 3.0, 6.0, 4.0, 2.0, 5.0, 1.0, 2.0],
                   "E":['a', 'a', 'b', 'a', 'b', 'b', 'b', 'a', 'a']}) 

df2 = df.groupby('B').agg({
                           'C': 'mean',
                           'D': 'sum',
                           'E': lambda x: x.mode()
                         })
print(df2)

问题在于我的原始数据集有200万行。将其转换为13万需要几分钟时间,并且我希望看到进度条
我尝试了`tqdm`,但不知道如何在这里应用它。是否有类似于.progress_apply()但适用于.agg()的函数?

1
https://dev59.com/JWMl5IYBdhLWcg3wMkko - BENY
我以前看过这个,但是我对它无能为力。我不需要使用apply()来执行简单的操作。相反,我需要使用agg()来执行多个操作。 - Joracosu
1个回答

1
这将在进行计算时打印进度,进度是通过计算统计数据的组数的比例来衡量的。但我不确定循环会减慢计算速度多少。
agger = {
   'C': 'mean',
   'D': 'sum',
   'E': lambda x: x.mode()}


gcols = ['B'] # columns defining the groups
groupby = df.groupby(gcols)

ngroups = len(groupby)
gfrac = 0.3 # fraction of groups for which you want to print progress
gfrac_size = max((1, int(ngroups*gfrac)))
groups = []
rows = []
for i,g in enumerate(groupby):

    if (i+1)%gfrac_size == 0:
        print('{:.0f}% complete'.format(100*(i/ngroups)))

    gstats = g[1].agg(agger)
    if i==0:
        if gstats.ndim==2:
            newcols = gstats.columns.tolist()
        else:
            newcols = gstats.index.tolist()

    groups.append(g[0])
    rows.append(gstats.values.flat)

df3 = pd.DataFrame(np.vstack(rows), columns=newcols)
if len(gcols) == 1:
    df3.index = groups
else:
    df3.index = pd.MultiIndex.from_tuples(groups, names=gcols)
df3 = df3.astype(df[newcols].dtypes)
df3
       C     D  E
1.0  1.5  10.0  a
2.0  3.0  12.0  b
3.0  7.0   8.0  a

一个替代方案(虽然有点 hacky)是利用你自己的函数 lambda x: x.mode 的使用权。由于您已经通过使用此函数来妥协速度,因此可以编写一个存储进度信息的类。例如,
import pandas as pd
import numpy as np
df = pd.DataFrame({"A":[1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0], 
                   "B":[1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0], 
                   "C":[1.0, 1.5, 2.0, 2.0, 3.0, 4.0, 5.0, 6.0, 10.0],
                   "D":[2.0, 5.0, 3.0, 6.0, 4.0, 2.0, 5.0, 1.0, 2.0],
                   "E":['a', 'a', 'b', 'a', 'b', 'b', 'b', 'a', 'a']}) 

df2 = df.groupby('B').agg({
                           'C': 'mean',
                           'D': 'sum',
                           'E': lambda x: x.mode()
                         })
print(df2)

class ModeHack:

    def __init__(self, size=5, N=10):
        self.ix = 0
        self.K = 1 
        self.size = size
        self.N = N

    def mode(self, x):
        self.ix = self.ix + x.shape[0]
        if self.K*self.size <= self.ix:
            print('{:.0f}% complete'.format(100*self.ix/self.N))
            self.K += 1

        return x.mode()

    def reset(self):    
        self.ix = 0
        self.K = 1

mymode = ModeHack(size=int(.1*df.shape[0]), N=df.shape[0])
mymode.reset()

agger = {
   'C': 'mean',
   'D': 'sum',
   'E': lambda x: mymode.mode(x)}

df3 = df.groupby('B').agg(agger)

对于这个例子它工作得非常好呵呵呵,但是如何在筛选了两列的数据框中应用相同的方法呢?df.B.nunique仅适用于一个列... - Joracosu
我刚刚修改了代码,以便您可以从groupby对象中获取组的数量。因此,如果您将其更改为df.groupby(['A','B'])等,则在您的示例中它将给出9。 - jtorca
修改后的代码允许在groupby语句中使用多列。 - jtorca
这是一个非常不错的尝试,但它会大大延长计算时间。我猜想以高效的方式完成可能是不可能的。我使用了tqdm包来改进您的代码。它具有更友好的显示效果。非常感谢您的时间。 - Joracosu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接