如何提高python数据框架的嵌套for循环的性能



我一直使用嵌套for循环的数据框架,但我的文件包含了很多行(近96k行)。我该如何提高速度?现在运行我的代码需要几个小时。有人能帮帮我吗?我也试着使用Numba Jit,但它花了很长时间。我研究了很多关于如何优化我的代码,但还没有得到任何解决方案。我试着考虑使用lambda,但不确定如何在这里具体使用它。对矢量化不确定。如果可能的话,谁能帮我一下吗?

下面是我的代码——

import xlsxwriter
excelFile = xlsxwriter.Workbook ("FlagResult.xlsx")
workSheet = excelFile.add_worksheet ()
import pandas as pd
import numpy as np
import math
from datetime import datetime
from numba import jit

df= pd.read_csv('Coconut_data.csv')
site= df['Site']
month= df['Month']
year= df['Year']
data_source= df['Data Source']
source_type = df['Sourcetype']
# df19= df[df['Year']==2019]
df19= df[df['Year']==2019]
site19= df19['Site']
month19= df19['Month']
year19= df19['Year']
st19= df19['Sourcetype']
df20= df[df['Year']==2020]
site20= df20['Site']
month20= df20['Month']
year20= df20['Year']
st20= df20['Sourcetype']
df21= df[df['Year']==2021]
site21= df21['Site']
month21= df21['Month']
year21= df21['Year']
st21= df21['Sourcetype']
df19['Unique19']= site19.map(str) + month19.map(str) + st19.map(str)
df20['Unique20']= site20.map(str) + month20.map(str) + st20.map(str)
df21['Unique21']= site21.map(str) + month21.map(str) + st21.map(str)

df19['Uniquex19']= site19.map(str) + month19.map(str)
df20['Uniquex20']= site20.map(str) + month20.map(str)
df21['Uniquex21']= site21.map(str) + month21.map(str)
pivot19 = pd.pivot_table(df19,index=['Unique19','Data Source','Sourcetype','Site','Month','Year','Uniquex19'],values=['Final Usage'], aggfunc=np.sum)
pivot20 = pd.pivot_table(df20,index=['Unique20','Data Source','Sourcetype','Site','Month','Year','Uniquex20'],values=['Final Usage'], aggfunc=np.sum)
pivot21 = pd.pivot_table(df21,index=['Unique21','Data Source','Sourcetype','Site','Month','Year','Uniquex21'],values=['Final Usage'], aggfunc=np.sum)
# print(pivot19, pivot20, pivot21)

print('n')
print('n')   

@jit
def check2019(): 
r=1
for index, values in pivot19.iterrows():

if (index[1]=='Actuals'):
count=0
for idx, val in pivot20.iterrows():
if(idx[1]=='Actuals'):
if (index[6]==idx[6]):
per= ((val-values)/values)*100
if (per > 50).bool():
if (per == math.inf).bool():
result= 'Usage was 0 for ', str(index[3]), ' in ', str(index[4]), str(index[5]), ' but not for ', str(idx[5])
count+=1
r+=1
res = ''.join(result)
print(res)
workSheet.write(r,1,str(res))
break
else:
result= str(index[3]) ,' for ', str(index[4]), str(index[5]), ' from ', str(index[2]), ' has increased by ', str(abs(round(float(per),2))), ' % in ', str(idx[5])
count+=1
r+=1
res = ''.join(result)
print(res)
workSheet.write(r,1,str(res))
break

elif (per < -50).bool():
result= str(index[3]) ,' for ', str(index[4]), str(index[5]), ' from ', str(index[2]), ' has dropped by ', str(abs(round(float(per),2))), ' % in ', str(idx[5])
count+=1
r+=1
res = ''.join(result)
print(res)
workSheet.write(r,1,str(res))
break

else:
# result= 'No spike or drop observed for ', str(index[3]), ' in ', index[4], str(index[5])
# count+=1
# r+=1
# res = ''.join(result)
# print(res)
# workSheet.write(r,1,str(res))
break

if count==0:
result= 'Missing ', str(index[3]), ' in ', str(index[4]), ' ', str(idx[5])
r+=1
res = ''.join(result)
print(res)
workSheet.write(r,1,str(res))


check2019()

在没有看到底层数据并了解你正在对它做什么的情况下,给你一个关于向量化或改进for循环的更精确的答案有点棘手。

然而,在我看来,你的嵌套for循环中已经有很多特定的东西在运行。

所以这种方法可能是一个更整洁的修复…它将数据帧分割成块,这样您就可以使用生成器表达式一次处理一个块,这将节省大量的系统内存。这将带来相当大的性能改进,并有望为您节省大量重写for循环逻辑的时间。

def split_df_into_chunks(df, n):
"""
Splits a dataframe into n chunks so each one can be processed at a time.
:param df: pandas dataframe.
:param n: int, number of rows to include in each chunk.
:return chunks: generator expression.
"""
chunks = (df.loc[x : x + n - 1] for x in list(range(len(df)))[::n])
return chunks
df_sections = split_df_into_chunks(your_df, 3)
try:
while True:
check2019(next(df_sections))
except StopIteration:
print('Finished processing dataframes')
finally:
del df_sections

这不是答案,而是要说明您需要考虑需要从代码中获得什么,并寻找现有的功能。很可能有人以前做过类似的事情,或者API中有标准方法可以快速处理您的输入。仅举一个例子…

如果你想做一个百分比变化的比较,你可以,例如,查看pct_change并尝试…

import pandas as pd
values = pd.date_range('2021-01-01', periods=3, freq='5W')
df = pd.DataFrame({'SiteA': [75.52,75.12,85.21],'SiteB': [70.21,69.21,72.21],'SiteC':[90.14,82.1,45.27]}, index=values)
print(round(df.pct_change(axis=0),2))

输出:

SiteA   SiteB   SiteC
2021-01-03  NaN     NaN     NaN
2021-02-07  -0.01   -0.01   -0.09
2021-03-14  0.13    0.04    -0.45

你应该注意到我没有写per= ((val-values)/values)*100,其中valvalues来自数据帧上的for循环;我也没说应该写在哪里。

现在,它的输入是一个像这样的数据帧…

SiteA   SiteB   SiteC
2021-01-03  75.52   70.21   90.14
2021-02-07  75.12   69.21   82.10
2021-03-14  85.21   72.21   45.27

你的第一步是尽可能地整理(清理、移动、洗牌、转移,无论如何)你的数据输入,并使其尽可能有组织。然后看看周围(搜索)"熊猫,我如何比较每年的百分比变化"……等等。如果你被卡住了(我们都有),你应该发布一个新的问题,并说"这是我的一部分代码……"它是这样的,但我希望它是这样的。我试过这个……

代码可能已经在那里了。

最新更新