簡體   English   中英

熊貓數據框groupby + Apply +新列很慢

[英]Pandas dataframe groupby + apply + new column is slow

我有一個熊貓數據框。 我使用groupBy (在1列上)+ apply組合以將新列添加到數據框。 Apply調用帶有參數的自定義函數。 完整的調用如下所示:

df = df.groupby('id').apply(lambda x: customFunction(x,'searchString'))

自定義函數的工作方式如下:根據if else條件,新列將填充10 然后返回該組。 有點概括,自定義函數如下所示:

def customFunction(group,searchString):
    #print(group.iloc[[0]]['id'].values[0])
    if len(group[(group['name'] == searchString)) > 0:
        group['newColumn'] = 1
    else:
        group['newColumn'] = 0
    return group

我的問題是,即使我不處理太多數據,該腳本也會運行相對較長的時間。 這些是我的數據的統計數據:數據框具有3130行和49列。 groupBy生成1499個單獨的組。

如果我在customFunction輸出了一些調試文本, customFunction觀察到通過每個組進行的實際迭代速度相當快,但是最后,要花費更長時間(比迭代本身更長的時間),直到groupBy實際完成為止。 我認為這與從新列重新索引或重新分配新數據有關。

我的問題是:

  • 為什么groupBy + apply需要這么長時間? 為什么實際迭代已經完成的部分要花這么長時間?
  • 如何避免這個瓶頸? 如何改善我的代碼(見上文)以更快地執行?
  • 更籠統地說:如何最有效地實施“ 按特定列分組,然后根據條件添加新列 ”的模式? 也許一種方法是創建單獨的數據結構,而無需返回組。 然后,在一個單獨的步驟中,可以將新計算的數據結構與原始數據幀結合在一起。 但是,我不確定這是否會更好。

我應該避免讀返回組,因為它會花費很長時間,但是我認為這是必須的,因為我在我的customFunction顯式生成了新數據,這就需要返回數據。

df.groupby(...).apply(...)尚未完全向量化,因為在df.groupby(...).apply(...)它是for .. loop ,它將對每個組應用指定的功能(在您的情況下,它將執行1499 + 1次)。

請參閱說明中為什么要應用熊貓的文檔中的注釋 ,將為第一組調用func兩次

在當前的實現中,在第一個組上兩次應用func調用,以確定它可以采用快速還是慢速代碼路徑。 如果func有副作用,這可能導致意外的行為,因為它們將對第一組生效兩次。

建議首先使用向量化函數,如果無法使用.apply()作為最后的解決方案,則尋求解決方案。

IIUC可以使用以下矢量化方法:

In [43]: df
Out[43]:
   id name
0   1  aaa
1   1  bbb
2   1  aaa
3   2  ccc
4   2  bbb
5   2  ccc
6   3  aaa

In [44]: searchString = 'aaa'

In [45]: df['newColumn'] = df.groupby('id')['name'] \
                             .transform(lambda x: x.eq(searchString).any().astype(int))

In [46]: df
Out[46]:
   id name  newColumn
0   1  aaa          1
1   1  bbb          1
2   1  aaa          1
3   2  ccc          0
4   2  bbb          0
5   2  ccc          0
6   3  aaa          1

70.000行DF的計時

In [56]: df = pd.concat([df] * 10**4, ignore_index=True)

In [57]: df.shape
Out[57]: (70000, 2)

In [58]: %timeit df.groupby('id').apply(lambda x: customFunction(x,searchString))
10 loops, best of 3: 92.4 ms per loop

In [59]: %timeit df.groupby('id')['name'].transform(lambda x: x.eq(searchString).any().astype(int))
10 loops, best of 3: 53.5 ms per loop

這是沒有groupby另一種更有效的解決方案(針對此特定情況)

>> searchString = 'searchString'
>> df = pd.DataFrame({'id': np.random.choice(1000, 1000000)})
>> df['name'] = random_names  # 1000000 random strings of len 10
>> df.loc[np.random.choice(1000000, 1000, replace=False), 'name'] = searchString
>>
>> def solution_0(x):
>>   x = x.groupby('id').apply(lambda g: customFunction(g, searchString))
>>
>> def solution_1(x):
>>   x['newColumn'] = x.groupby('id')['name'].transform(lambda g: g.eq(searchString).any().astype(int))
>>
>> def solution_2(x):
>>   x['newColumn'] = 0
>>   x.loc[x['id'].isin(x.loc[x['name'] == searchString, 'id']), 'newColumn'] = 1
>> 
>> %timeit solution_0(df)
3.4 s ± 125 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
>> %timeit solution_1(df)
1.47 s ± 56.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
>> %timeit solution_2(df)
129 ms ± 4.33 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM