【问题标题】:Progress bar for pandas DataFrame multy operations with .agg()带有 .agg() 的 pandas DataFrame 多项操作的进度条
【发布时间】:2019-12-08 03:26:18
【问题描述】:

我想将 .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)

问题是我的原始数据集有 2.000.000 行。将其转换为 130.000 需要几分钟时间,我想看到一个进度条

我已尝试使用tqdm,但我不知道如何在此处应用它。有没有类似.progress_apply()的功能,但针对.agg()

【问题讨论】:

标签: python pandas tqdm


【解决方案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

另一种方法(虽然有点老套)是利用您使用自己的函数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 仅对一列有效...
  • 我刚刚对其进行了修改,以便您从 groupby 对象中获取组数。因此,如果您更改为 df.groupby(['A','B']) 例如,它会在您的示例中为您提供 9。
  • 再次修改以允许 groupby 语句中有多个列
  • 这是一个非常好的尝试,但是它大大延长了计算时间。我想不可能以有效的方式做到这一点。我使用tqdm pack 改进了您的代码。它有一个更友好的显示。非常感谢您的宝贵时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-12
  • 1970-01-01
  • 2022-12-22
  • 2015-09-25
  • 1970-01-01
  • 2013-02-05
相关资源
最近更新 更多