我有这个函数,给定一个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它需要几个小时。
答案 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 担任数据科学家倡导者。