如何让我的 python 代码 运行 更快
How can I make my python code run faster
我正在编写循环遍历多个 netcdf 文件(大~28G)的代码。 netcdf 文件在整个域中具有多个 4D 变量 [时间、东西、南北、高度]。目标是遍历这些文件并遍历域中所有这些变量的每个位置,并将某些变量提取到一个大数组中。当文件丢失或不完整时,我用 99.99 填充这些值。现在我只是通过循环超过 2 个每日 netcdf 文件来进行测试,但由于某种原因它会永远(~14 小时)。我不确定是否有优化此代码的方法。我不认为 python 应该花这么长时间来完成这项任务,但也许这是 python 或我的代码的问题。下面是我的代码,希望它是可读的,并且非常感谢任何关于如何使它更快的建议:
#Domain to loop over
k_space = np.arange(0,37)
j_space = np.arange(80,170)
i_space = np.arange(200,307)
predictors_wrf=[]
names_wrf=[]
counter = 0
cdate = start_date
while cdate <= end_date:
if cdate.month not in month_keep:
cdate+=inc
continue
yy = cdate.strftime('%Y')
mm = cdate.strftime('%m')
dd = cdate.strftime('%d')
filename = wrf_path+'\wrfoutRED_d01_'+yy+'-'+mm+'-'+dd+'_'+hour_str+'_00_00'
for i in i_space:
for j in j_space:
for k in k_space:
if os.path.isfile(filename):
f = nc.Dataset(filename,'r')
times = f.variables['Times'][1:]
num_lines = times.shape[0]
if num_lines == 144:
u = f.variables['U'][1:,k,j,i]
v = f.variables['V'][1:,k,j,i]
wspd = np.sqrt(u**2.+v**2.)
w = f.variables['W'][1:,k,j,i]
p = f.variables['P'][1:,k,j,i]
t = f.variables['T'][1:,k,j,i]
if num_lines < 144:
print "partial files for WRF: "+ filename
u = np.ones((144,))*99.99
v = np.ones((144,))*99.99
wspd = np.ones((144,))*99.99
w = np.ones((144,))*99.99
p = np.ones((144,))*99.99
t = np.ones((144,))*99.99
else:
u = np.ones((144,))*99.99
v = np.ones((144,))*99.99
wspd = np.ones((144,))*99.99
w = np.ones((144,))*99.99
p = np.ones((144,))*99.99
t = np.ones((144,))*99.99
counter=counter+1
predictors_wrf.append(u)
predictors_wrf.append(v)
predictors_wrf.append(wspd)
predictors_wrf.append(w)
predictors_wrf.append(p)
predictors_wrf.append(t)
u_names = 'u_'+str(k)+'_'+str(j)+'_'+str(i)
v_names = 'v_'+str(k)+'_'+str(j)+'_'+str(i)
wspd_names = 'wspd_'+str(k)+'_'+str(j)+'_'+str(i)
w_names = 'w_'+str(k)+'_'+str(j)+'_'+str(i)
p_names = 'p_'+str(k)+'_'+str(j)+'_'+str(i)
t_names = 't_'+str(k)+'_'+str(j)+'_'+str(i)
names_wrf.append(u_names)
names_wrf.append(v_names)
names_wrf.append(wspd_names)
names_wrf.append(w_names)
names_wrf.append(p_names)
names_wrf.append(t_names)
cdate+=inc
我没有太多建议,但有几点需要注意。
不要多次打开那个文件
首先,你定义了这个 filename
变量,然后在这个循环中(内部深处:三个 for 循环深处),你正在检查文件是否存在并可能在那里打开它(我不知道nc.Dataset
做什么,但我猜它必须打开文件并阅读它):
filename = wrf_path+'\wrfoutRED_d01_'+yy+'-'+mm+'-'+dd+'_'+hour_str+'_00_00'
for i in i_space:
for j in j_space:
for k in k_space:
if os.path.isfile(filename):
f = nc.Dataset(filename,'r')
这将是非常低效的。如果文件在所有循环之前没有更改,您当然可以打开它一次。
尝试使用更少的 for 循环
所有这些嵌套的 for 循环都增加了您需要执行的操作数量。一般建议:尝试改用numpy操作。
使用 CProfile
如果您想知道为什么您的程序需要很长时间,最好的找出方法之一就是分析它们。
这是收紧您的 forloop
的第一个蹩脚传球。由于每个文件只使用一次文件形状,因此可以将处理移到循环之外,这将减少中断处理中的数据加载量。我仍然不明白 counter
和 inc
做了什么,因为它们似乎没有在循环中更新。您肯定想研究重复的字符串连接性能,或者您附加到 predictors_wrf
和 names_wrf
的性能如何作为起点
k_space = np.arange(0,37)
j_space = np.arange(80,170)
i_space = np.arange(200,307)
predictors_wrf=[]
names_wrf=[]
counter = 0
cdate = start_date
while cdate <= end_date:
if cdate.month not in month_keep:
cdate+=inc
continue
yy = cdate.strftime('%Y')
mm = cdate.strftime('%m')
dd = cdate.strftime('%d')
filename = wrf_path+'\wrfoutRED_d01_'+yy+'-'+mm+'-'+dd+'_'+hour_str+'_00_00'
file_exists = os.path.isfile(filename)
if file_exists:
f = nc.Dataset(filename,'r')
times = f.variables['Times'][1:]
num_lines = times.shape[0]
for i in i_space:
for j in j_space:
for k in k_space:
if file_exists:
if num_lines == 144:
u = f.variables['U'][1:,k,j,i]
v = f.variables['V'][1:,k,j,i]
wspd = np.sqrt(u**2.+v**2.)
w = f.variables['W'][1:,k,j,i]
p = f.variables['P'][1:,k,j,i]
t = f.variables['T'][1:,k,j,i]
if num_lines < 144:
print "partial files for WRF: "+ filename
u = np.ones((144,))*99.99
v = np.ones((144,))*99.99
wspd = np.ones((144,))*99.99
w = np.ones((144,))*99.99
p = np.ones((144,))*99.99
t = np.ones((144,))*99.99
else:
u = np.ones((144,))*99.99
v = np.ones((144,))*99.99
wspd = np.ones((144,))*99.99
w = np.ones((144,))*99.99
p = np.ones((144,))*99.99
t = np.ones((144,))*99.99
counter=counter+1
predictors_wrf.append(u)
predictors_wrf.append(v)
predictors_wrf.append(wspd)
predictors_wrf.append(w)
predictors_wrf.append(p)
predictors_wrf.append(t)
u_names = 'u_'+str(k)+'_'+str(j)+'_'+str(i)
v_names = 'v_'+str(k)+'_'+str(j)+'_'+str(i)
wspd_names = 'wspd_'+str(k)+'_'+str(j)+'_'+str(i)
w_names = 'w_'+str(k)+'_'+str(j)+'_'+str(i)
p_names = 'p_'+str(k)+'_'+str(j)+'_'+str(i)
t_names = 't_'+str(k)+'_'+str(j)+'_'+str(i)
names_wrf.append(u_names)
names_wrf.append(v_names)
names_wrf.append(wspd_names)
names_wrf.append(w_names)
names_wrf.append(p_names)
names_wrf.append(t_names)
cdate+=inc
对于你的问题,我认为multiprocessing会有很大帮助。我仔细阅读了您的代码并在此处提供了一些建议。
不使用开始时间,而是使用文件名作为代码中的迭代器。
包装一个函数以根据时间找出所有文件名,return所有文件名的列表。
def fileNames(start_date, end_date):
# Find all filenames.
cdate = start_date
fileNameList = []
while cdate <= end_date:
if cdate.month not in month_keep:
cdate+=inc
continue
yy = cdate.strftime('%Y')
mm = cdate.strftime('%m')
dd = cdate.strftime('%d')
filename = wrf_path+'\wrfoutRED_d01_'+yy+'-'+mm+'-'+dd+'_'+hour_str+'_00_00'
fileNameList.append(filename)
cdate+=inc
return fileNameList
将提取数据的代码包装起来并填充 99.99,函数的输入是文件名。
def dataExtraction(filename):
file_exists = os.path.isfile(filename)
if file_exists:
f = nc.Dataset(filename,'r')
times = f.variables['Times'][1:]
num_lines = times.shape[0]
for i in i_space:
for j in j_space:
for k in k_space:
if file_exists:
if num_lines == 144:
u = f.variables['U'][1:,k,j,i]
v = f.variables['V'][1:,k,j,i]
wspd = np.sqrt(u**2.+v**2.)
w = f.variables['W'][1:,k,j,i]
p = f.variables['P'][1:,k,j,i]
t = f.variables['T'][1:,k,j,i]
if num_lines < 144:
print "partial files for WRF: "+ filename
u = np.ones((144,))*99.99
v = np.ones((144,))*99.99
wspd = np.ones((144,))*99.99
w = np.ones((144,))*99.99
p = np.ones((144,))*99.99
t = np.ones((144,))*99.99
else:
u = np.ones((144,))*99.99
v = np.ones((144,))*99.99
wspd = np.ones((144,))*99.99
w = np.ones((144,))*99.99
p = np.ones((144,))*99.99
t = np.ones((144,))*99.99
counter=counter+1
predictors_wrf.append(u)
predictors_wrf.append(v)
predictors_wrf.append(wspd)
predictors_wrf.append(w)
predictors_wrf.append(p)
predictors_wrf.append(t)
u_names = 'u_'+str(k)+'_'+str(j)+'_'+str(i)
v_names = 'v_'+str(k)+'_'+str(j)+'_'+str(i)
wspd_names = 'wspd_'+str(k)+'_'+str(j)+'_'+str(i)
w_names = 'w_'+str(k)+'_'+str(j)+'_'+str(i)
p_names = 'p_'+str(k)+'_'+str(j)+'_'+str(i)
t_names = 't_'+str(k)+'_'+str(j)+'_'+str(i)
names_wrf.append(u_names)
names_wrf.append(v_names)
names_wrf.append(wspd_names)
names_wrf.append(w_names)
names_wrf.append(p_names)
names_wrf.append(t_names)
return zip(predictors_wrf, names_wrf)
使用多处理来完成您的工作。通常,所有计算机都具有 1 CPU 个以上的内核。当有大量 CPU 计算时,多处理将有助于提高速度。根据我以前的经验,多处理将减少高达 2/3 的大型数据集消耗时间。
更新:在 2017 年 2 月 25 日再次测试我的代码和文件后,我发现对一个巨大的数据集使用 8 个内核为我节省了 90% 的崩溃时间。
if __name__ == '__main__':
from multiprocessing import Pool # This should be in the beginning statements.
start_date = '01-01-2017'
end_date = '01-15-2017'
fileNames = fileNames(start_date, end_date)
p = Pool(4) # the cores numbers you want to use.
results = p.map(dataExtraction, fileNames)
p.close()
p.join()
最后,请注意这里的数据结构,它非常复杂。希望这可以帮助。如果您还有其他问题,请发表评论。
我正在编写循环遍历多个 netcdf 文件(大~28G)的代码。 netcdf 文件在整个域中具有多个 4D 变量 [时间、东西、南北、高度]。目标是遍历这些文件并遍历域中所有这些变量的每个位置,并将某些变量提取到一个大数组中。当文件丢失或不完整时,我用 99.99 填充这些值。现在我只是通过循环超过 2 个每日 netcdf 文件来进行测试,但由于某种原因它会永远(~14 小时)。我不确定是否有优化此代码的方法。我不认为 python 应该花这么长时间来完成这项任务,但也许这是 python 或我的代码的问题。下面是我的代码,希望它是可读的,并且非常感谢任何关于如何使它更快的建议:
#Domain to loop over
k_space = np.arange(0,37)
j_space = np.arange(80,170)
i_space = np.arange(200,307)
predictors_wrf=[]
names_wrf=[]
counter = 0
cdate = start_date
while cdate <= end_date:
if cdate.month not in month_keep:
cdate+=inc
continue
yy = cdate.strftime('%Y')
mm = cdate.strftime('%m')
dd = cdate.strftime('%d')
filename = wrf_path+'\wrfoutRED_d01_'+yy+'-'+mm+'-'+dd+'_'+hour_str+'_00_00'
for i in i_space:
for j in j_space:
for k in k_space:
if os.path.isfile(filename):
f = nc.Dataset(filename,'r')
times = f.variables['Times'][1:]
num_lines = times.shape[0]
if num_lines == 144:
u = f.variables['U'][1:,k,j,i]
v = f.variables['V'][1:,k,j,i]
wspd = np.sqrt(u**2.+v**2.)
w = f.variables['W'][1:,k,j,i]
p = f.variables['P'][1:,k,j,i]
t = f.variables['T'][1:,k,j,i]
if num_lines < 144:
print "partial files for WRF: "+ filename
u = np.ones((144,))*99.99
v = np.ones((144,))*99.99
wspd = np.ones((144,))*99.99
w = np.ones((144,))*99.99
p = np.ones((144,))*99.99
t = np.ones((144,))*99.99
else:
u = np.ones((144,))*99.99
v = np.ones((144,))*99.99
wspd = np.ones((144,))*99.99
w = np.ones((144,))*99.99
p = np.ones((144,))*99.99
t = np.ones((144,))*99.99
counter=counter+1
predictors_wrf.append(u)
predictors_wrf.append(v)
predictors_wrf.append(wspd)
predictors_wrf.append(w)
predictors_wrf.append(p)
predictors_wrf.append(t)
u_names = 'u_'+str(k)+'_'+str(j)+'_'+str(i)
v_names = 'v_'+str(k)+'_'+str(j)+'_'+str(i)
wspd_names = 'wspd_'+str(k)+'_'+str(j)+'_'+str(i)
w_names = 'w_'+str(k)+'_'+str(j)+'_'+str(i)
p_names = 'p_'+str(k)+'_'+str(j)+'_'+str(i)
t_names = 't_'+str(k)+'_'+str(j)+'_'+str(i)
names_wrf.append(u_names)
names_wrf.append(v_names)
names_wrf.append(wspd_names)
names_wrf.append(w_names)
names_wrf.append(p_names)
names_wrf.append(t_names)
cdate+=inc
我没有太多建议,但有几点需要注意。
不要多次打开那个文件
首先,你定义了这个 filename
变量,然后在这个循环中(内部深处:三个 for 循环深处),你正在检查文件是否存在并可能在那里打开它(我不知道nc.Dataset
做什么,但我猜它必须打开文件并阅读它):
filename = wrf_path+'\wrfoutRED_d01_'+yy+'-'+mm+'-'+dd+'_'+hour_str+'_00_00'
for i in i_space:
for j in j_space:
for k in k_space:
if os.path.isfile(filename):
f = nc.Dataset(filename,'r')
这将是非常低效的。如果文件在所有循环之前没有更改,您当然可以打开它一次。
尝试使用更少的 for 循环
所有这些嵌套的 for 循环都增加了您需要执行的操作数量。一般建议:尝试改用numpy操作。
使用 CProfile
如果您想知道为什么您的程序需要很长时间,最好的找出方法之一就是分析它们。
这是收紧您的 forloop
的第一个蹩脚传球。由于每个文件只使用一次文件形状,因此可以将处理移到循环之外,这将减少中断处理中的数据加载量。我仍然不明白 counter
和 inc
做了什么,因为它们似乎没有在循环中更新。您肯定想研究重复的字符串连接性能,或者您附加到 predictors_wrf
和 names_wrf
的性能如何作为起点
k_space = np.arange(0,37)
j_space = np.arange(80,170)
i_space = np.arange(200,307)
predictors_wrf=[]
names_wrf=[]
counter = 0
cdate = start_date
while cdate <= end_date:
if cdate.month not in month_keep:
cdate+=inc
continue
yy = cdate.strftime('%Y')
mm = cdate.strftime('%m')
dd = cdate.strftime('%d')
filename = wrf_path+'\wrfoutRED_d01_'+yy+'-'+mm+'-'+dd+'_'+hour_str+'_00_00'
file_exists = os.path.isfile(filename)
if file_exists:
f = nc.Dataset(filename,'r')
times = f.variables['Times'][1:]
num_lines = times.shape[0]
for i in i_space:
for j in j_space:
for k in k_space:
if file_exists:
if num_lines == 144:
u = f.variables['U'][1:,k,j,i]
v = f.variables['V'][1:,k,j,i]
wspd = np.sqrt(u**2.+v**2.)
w = f.variables['W'][1:,k,j,i]
p = f.variables['P'][1:,k,j,i]
t = f.variables['T'][1:,k,j,i]
if num_lines < 144:
print "partial files for WRF: "+ filename
u = np.ones((144,))*99.99
v = np.ones((144,))*99.99
wspd = np.ones((144,))*99.99
w = np.ones((144,))*99.99
p = np.ones((144,))*99.99
t = np.ones((144,))*99.99
else:
u = np.ones((144,))*99.99
v = np.ones((144,))*99.99
wspd = np.ones((144,))*99.99
w = np.ones((144,))*99.99
p = np.ones((144,))*99.99
t = np.ones((144,))*99.99
counter=counter+1
predictors_wrf.append(u)
predictors_wrf.append(v)
predictors_wrf.append(wspd)
predictors_wrf.append(w)
predictors_wrf.append(p)
predictors_wrf.append(t)
u_names = 'u_'+str(k)+'_'+str(j)+'_'+str(i)
v_names = 'v_'+str(k)+'_'+str(j)+'_'+str(i)
wspd_names = 'wspd_'+str(k)+'_'+str(j)+'_'+str(i)
w_names = 'w_'+str(k)+'_'+str(j)+'_'+str(i)
p_names = 'p_'+str(k)+'_'+str(j)+'_'+str(i)
t_names = 't_'+str(k)+'_'+str(j)+'_'+str(i)
names_wrf.append(u_names)
names_wrf.append(v_names)
names_wrf.append(wspd_names)
names_wrf.append(w_names)
names_wrf.append(p_names)
names_wrf.append(t_names)
cdate+=inc
对于你的问题,我认为multiprocessing会有很大帮助。我仔细阅读了您的代码并在此处提供了一些建议。
不使用开始时间,而是使用文件名作为代码中的迭代器。
包装一个函数以根据时间找出所有文件名,return所有文件名的列表。
def fileNames(start_date, end_date): # Find all filenames. cdate = start_date fileNameList = [] while cdate <= end_date: if cdate.month not in month_keep: cdate+=inc continue yy = cdate.strftime('%Y') mm = cdate.strftime('%m') dd = cdate.strftime('%d') filename = wrf_path+'\wrfoutRED_d01_'+yy+'-'+mm+'-'+dd+'_'+hour_str+'_00_00' fileNameList.append(filename) cdate+=inc return fileNameList
将提取数据的代码包装起来并填充 99.99,函数的输入是文件名。
def dataExtraction(filename): file_exists = os.path.isfile(filename) if file_exists: f = nc.Dataset(filename,'r') times = f.variables['Times'][1:] num_lines = times.shape[0] for i in i_space: for j in j_space: for k in k_space: if file_exists: if num_lines == 144: u = f.variables['U'][1:,k,j,i] v = f.variables['V'][1:,k,j,i] wspd = np.sqrt(u**2.+v**2.) w = f.variables['W'][1:,k,j,i] p = f.variables['P'][1:,k,j,i] t = f.variables['T'][1:,k,j,i] if num_lines < 144: print "partial files for WRF: "+ filename u = np.ones((144,))*99.99 v = np.ones((144,))*99.99 wspd = np.ones((144,))*99.99 w = np.ones((144,))*99.99 p = np.ones((144,))*99.99 t = np.ones((144,))*99.99 else: u = np.ones((144,))*99.99 v = np.ones((144,))*99.99 wspd = np.ones((144,))*99.99 w = np.ones((144,))*99.99 p = np.ones((144,))*99.99 t = np.ones((144,))*99.99 counter=counter+1 predictors_wrf.append(u) predictors_wrf.append(v) predictors_wrf.append(wspd) predictors_wrf.append(w) predictors_wrf.append(p) predictors_wrf.append(t) u_names = 'u_'+str(k)+'_'+str(j)+'_'+str(i) v_names = 'v_'+str(k)+'_'+str(j)+'_'+str(i) wspd_names = 'wspd_'+str(k)+'_'+str(j)+'_'+str(i) w_names = 'w_'+str(k)+'_'+str(j)+'_'+str(i) p_names = 'p_'+str(k)+'_'+str(j)+'_'+str(i) t_names = 't_'+str(k)+'_'+str(j)+'_'+str(i) names_wrf.append(u_names) names_wrf.append(v_names) names_wrf.append(wspd_names) names_wrf.append(w_names) names_wrf.append(p_names) names_wrf.append(t_names) return zip(predictors_wrf, names_wrf)
使用多处理来完成您的工作。通常,所有计算机都具有 1 CPU 个以上的内核。当有大量 CPU 计算时,多处理将有助于提高速度。根据我以前的经验,多处理将减少高达 2/3 的大型数据集消耗时间。
更新:在 2017 年 2 月 25 日再次测试我的代码和文件后,我发现对一个巨大的数据集使用 8 个内核为我节省了 90% 的崩溃时间。
if __name__ == '__main__': from multiprocessing import Pool # This should be in the beginning statements. start_date = '01-01-2017' end_date = '01-15-2017' fileNames = fileNames(start_date, end_date) p = Pool(4) # the cores numbers you want to use. results = p.map(dataExtraction, fileNames) p.close() p.join()
最后,请注意这里的数据结构,它非常复杂。希望这可以帮助。如果您还有其他问题,请发表评论。