Pandas应用函数运行缓慢

时间:2017-12-14 13:24:01

标签: python pandas

我有这个函数,给定一个id,一个数字n和一个数据帧返回列“something”的第n个元素,其中“id”是参数中的id。

def find_something(id,n,df):
  table = df.loc[(df['id'] == id)]
  try:
      something = df['something'].iloc[n-1]
  except:
      something = float('NaN')
  return something

当我为1个id运行时(id的格式为np.int32,而参数中的df有20万行),它运行时间为11.4 ns,但是当我将它应用到具有60K行的数据帧列时运行时间:

my_table['new_column'] = my_table['id'].apply(find_something, args=(1,df,))

所以,如果我有:

df = pd.DataFrame({'id' : [1, 2, 2, 2,
                          2,1,2,2],
                   'something' : np.random.randn(8)})

my_table = pd.DataFrame({'id' : [1, 2]})

my_table['new_column'] = my_table['id'].apply(find_something, args=(1,df,))

my_table应如下所示:

    id  new_column
0   1   -0.396238
1   2    0.074007

有更有效的方法吗?我没有看到任何理由为什么1个元素需要11 ns但是对于60K它需要几个小时。

1 个答案:

答案 0 :(得分:1)

我生成了一个包含 2000 万行和 60K ID 的类似数据集,并通过您的代码运行它;花了大约一个小时才完成。通常,用户定义的函数缺乏速度,因为 apply() 没有利用 Pandas 的矢量化。如果对大型数据集执行 apply() 是您的主要痛点,您应该考虑替代解决方案,例如 Bodo。我通过 Bodo 运行了相同的代码;大约 1.5 分钟就完成了。从本质上讲,Bodo 优化了您的 apply() 代码以维护所提供的矢量化,同时提供对科学正确的并行化方法的访问。 Bodo 社区版使您能够在多达 4 个内核上运行。这是安装页面的链接:https://docs.bodo.ai/latest/source/install.html

#data generation

import pandas as pd
import numpy as np
import time

df = pd.DataFrame({'id' : np.random.randint(1,60000,20000000),
                   'something' : np.random.randn(20000000)})
my_table = pd.DataFrame({'id' : np.arange(1, 60000)})

my_table.to_parquet("table.pq")
df.to_parquet("df.pq")

使用 Pandas(我对您的代码进行了一些小改动以使其更加健壮):

def find_something(id,n,df):
    df = df.loc[(df['id'] == id)]
    if len(df) != 0:
        result = df['something'].iloc[n-1]
    else:
        result = np.nan
    return result

start = time.time()

df = pd.read_parquet("df.pq")
my_table = pd.read_parquet("table.pq")
my_table['new_column'] = my_table['id'].apply(find_something, args=(1,df,))

end = time.time()
print("computation time: ", end - start)

print(my_table.head())

output:
computation time:  3482.743801832199
   id  new_column
0   1   -1.096224
1   2    0.667792
2   3    1.069627
3   4    0.129955
4   5    0.150882

与博多:

%%px

import pandas as pd
import numpy as np
import time
import bodo

@bodo.jit(distributed = ['df', 'result'])
def find_something(id,n,df):
    df = df.loc[(df['id'] == id)]
    if len(df) != 0:
        result = df['something'].iloc[n-1]
    else:
        result = np.nan
    return result

@bodo.jit(distributed = ['my_table', 'df'])
def new_column():
    start = time.time()
    df = pd.read_parquet("df.pq")
    my_table = pd.read_parquet("table.pq")
    my_table['new_column'] = my_table['id'].apply(find_something, args=(1,df,))
    end = time.time()
    print("computation time: ", end - start)
    print(my_table.head())
    return my_table
    
my_table = new_column()

output:
[stdout:0] 
computation time:  103.9169020652771
 id  new_column
0   1   -1.096224
1   2    0.667792
2   3    1.069627
3   4    0.129955
4   5    0.150882

免责声明:我在 Bodo.ai 担任数据科学家倡导者。