首页 > 解决方案 > 如何提高映射函数到熊猫中数据框列的计算速度

问题描述

我正在基于整个数据框中的其他值的数据框中创建一个新列。我已经找到了几种方法(如下所示),但是在处理大型数据集时它们非常慢(运行 500k 行需要 1 小时)。我希望提高这个过程的速度。

我尝试将 .apply 与 lambda 函数一起使用。我还使用 .map 获取要放入新列的列表。这两种方法都有效,但速度太慢。

values = {'ID': ['1','2','3','4','1','2','3'],
        'MOD': ['X','Y','Z','X','X','Z','Y'],
        'Period': ['Current','Current','Current','Current','Past','Past','Past']
        }

df = DataFrame(values,columns= ['ID', 'MOD','Period'])

df['ID_MOD']=df['ID']+df['MOD']

def funct(identifier, indentifier_modification,period):
    if period=="Current":
        if (df.ID==identifier).sum()==1:
            return "New"     
        elif (df.ID_MOD==indentifier_modification).sum()==1:
            return "Unique"
        else:
            return "Repeat"
    else:
        return "n/a"

初始df:

  ID MOD   Period ID_MOD
0  1   X  Current     1X
1  2   Y  Current     2Y
2  3   Z  Current     3Z
3  4   X  Current     4X
4  1   X     Past     1X
5  2   Z     Past     2Z
6  3   Y     Past     3Y

以下是两种太慢的方法:1)

df['new_column']=df.apply(lambda x:funct(x['ID'],x['ID_MOD'],x['Period']), axis=1)

2)

df['new_column']=list(map(funct,df['ID'],df['ID_MOD'],df['Period']))

预期的最终df:

  ID MOD   Period ID_MOD new_column
0  1   X  Current     1X     Repeat
1  2   Y  Current     2Y     Unique
2  3   Z  Current     3Z     Unique
3  4   X  Current     4X        New
4  1   X     Past     1X        n/a
5  2   Z     Past     2Z        n/a
6  3   Y     Past     3Y        n/a

没有错误信息;使用大型数据集运行代码只需约 1 小时。

标签: pythondataframe

解决方案


您当前的代码当前缩放为 O(N**2) 其中 N 是行数。如果你df真的是 500k 行,这将需要很长时间!你真的想使用计算复杂度低得多的 numpy 和 pandas 的代码。

pandas 中内置的聚合将大大有助于代替您使用. 在你的情况下,我可以很容易地将 500k 行减少到不到一秒钟。summerge

首先定义一个虚拟数据集:

import numpy as np
import pandas as pd

N = 500_000

df = pd.DataFrame({
    'id': np.random.choice(N//2, N),
    'a': np.random.choice(list('XYZ'), N),
    'b': np.random.choice(list('CP'), N),
})

接下来,我们可以进行聚合以计算您的各个组:

ids = df.groupby(['id']).size().rename('ids')
idas = df.groupby(['id','a']).size().rename('idas')

接下来我们可以将这些聚合连接回原始数据集

尽可能地减少数据总是一个好主意,在你的情况下,Past值总是得到一个值,n/a因为它们占用了一半的数据,所以工作量似乎是一半:

df2 = df.loc[df['b'] == 'C',]
df2 = pd.merge(df2, ids, left_on=['id'], right_index=True)
df2 = pd.merge(df2, idas, left_on=['id','a'], right_index=True)

最后,我们使用wherefrom numpy 对所有条件进行矢量化,因此工作速度更快,然后使用 pandas 索引将所有内容有效地重新组合在一起,然后修补缺失值

df2['out'] = np.where(
    df2['ids'] == 1, 'New',
    np.where(df2['idas'] == 1, 'Unique', 'Repeat'))

df['out'] = df2['out']
df['out'].fillna('n/a', inplace=True)

希望其中的一些帮助!作为参考,我的笔记本电脑上 500k 行的上述运行时间约为 320 毫秒


推荐阅读