因为LLM的倒退, 很多的数据集都是以DF的模式公布的,所以通过Pandas操作字符串的要求变得越来越高了,所以本文将对字符串操作方法进行基准测试,看看它们是如何影响pandas的性能的。因为一旦Pandas在解决数据时超过肯定限度,它们的行为就会很奇怪。
咱们用Faker创立了一个100,000行的测试数据。
测试方法
装置:
!pip install faker
生成测试数据的办法很简答:
import pandas as pd import numpy as np def gen_data(x): from faker import Faker fake = Faker() outdata = {} for i in range(0,x): outdata[i] = fake.profile() return pd.DataFrame(outdata).T n= 100000 basedata = gen_data(n)
而后把Google Colab将输入存储在Google drive中
from google.colab import drive drive.mount('/content/drive')
创立了非常简单的函数来测试连贯两个字符串的各种办法。
def process(a,b): return ''.join([a,b]) def process(a,b): return a+b def process(a,b): return f"{a}{b}" def process(a,b): return f"{a}{b}"*100
创立一个空DF,编写一个函数将输入%%timeit作为一行增加到数据框中
# add a row to the dataframe using %%timeit output def add_to_df(n, m, x, outputdf): outputdf.loc[len(outputdf.index)] = [m, n, x] # output frame outputdf = pd.DataFrame(columns=['method', 'n', 'timing']) outputdf
而后就是运行下面的每个函数并将数据导出到pandas的代码。
# get a sample of data n = 10000 suffix = 'fstring_100x' data = basedata.copy().sample(n).reset_index()
记录运行工夫
%%timeit -r 7 -n 1 -o data['newcol'] = '' for row in range(len(data)): data.at[row ,'newcol'] = process(data.at[row, 'job'], data.at[row, 'company']) # 451 ms ± 34 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) # <TimeitResult : 451 ms ± 34 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)>
残缺的函数调用
m = "Iterating over the rows" add_to_df(n = n, m = m, x = vars(_), outputdf = outputdf)
试验
下面是代码,上面开始用下面的代码进行试验:
Iterrows (pandas原生函数)每行相加
%%timeit -r 7 -n 1 -o data['newcol'] = '' for row, item in data.iterrows(): data.at[row ,'newcol'] = process(item['job'], item['company'])
Itertuples(因为不可变而更平安)每行相加
%%timeit -r 7 -n 1 -o data['newcol'] = '' for row, job, company in data[['job','company']].itertuples(): data.at[row ,'newcol'] = process(job, company)
应用pandas原生函数作为字符串相加
%%timeit -r 7 -n 1 -o data['newcol'] = data.job + data.company
应用原生函数pandas. series .add
%%timeit -r 7 -n 1 -o data['newcol'] = data.job.add(data.company)
应用dataframe.apply
%%timeit -r 7 -n 1 -o data['newcol'] = data.apply(lambda row: process(row['job'],row['company']), axis=1)
应用List Map
%%timeit -r 7 -n 1 -o data['newcol'] = list(map(process, data.job, data.company))
Pandas矢量化
%%timeit -r 7 -n 1 -o data['newcol'] = process(data.job, data.company)
numpy数组矢量化
%%timeit -r 7 -n 1 -o data['newcol'] = process(data.job.to_numpy(), data.company.to_numpy())
显式在numpy数组上应用numpy向量化
%%timeit -r 7 -n 1 -o data['newcol'] = np.vectorize(process)(data.job.to_numpy(), data.company.to_numpy())
优化后的列表推导式
%%timeit -r 7 -n 1 -o data['newcol'] = '' data['newcol'] =[process(i,j) for i,j in list(zip(data.job, data.company)) ]
最初是后果的输入:
outputdf.to_csv(f"./drive/MyDrive/{n}_{suffix}.csv")
后果
后果如下所示。我用了下面3种不同函数测试了后果。
原生的字符串加法C = a+b
从1000行扩大到100,000行所需的工夫;
可视化比照:
所有矢量化办法都十分快,而且pandas规范的str.add对numpy数组也进行了矢量化。可能看到Pandas的原生办法个别都是线性的。List-map仿佛以N的平方根的速度增长
应用fstring: c = f " {a}{b} "
应用fstring,后果很乏味,有的后果无法解释。
工夫
可视化
从工夫上看,长度超过10,000的DF时,向量化是正确执行的
下图是第三个函数,就是*100,这更能阐明问题,向量化操作的基本上工夫没有变动
总结
通过下面的测试,咱们能够总结一下后果:
1、还是陈词滥调的问题,不要应用iterrows(), itertuples(),尽量不要应用DataFrame.apply(),因为几个函数还是循环遍历的。
2、矢量化操作在字符串操作中也是能够应用的,然而为了平安起见,应用Numpy数组。
3、列表推导式就像它的名字一样,它还是一个list
4、还有一些奇怪的无法解释的问题,然而大部分的状况都是能够解释的
如果你有更好的了解,欢送留言
https://avoid.overfit.cn/post/2633908f89b14e0bb14bcaab443c3fec
作者:Dr. Mandar Karhade