提高python计算绘图代码运行速度


ds1 = xr.open_dataset('d:/history_weather/%s'% (time)+'/CDS%s'% (time)+'ts-s.nc')
ds = xr.open_dataset('d:/history_weather/%s'% (time)+'/CDS%s'% (time)+'ts-h.nc')
lon = ds1['longitude']#读取经度
lat = ds1['latitude']#读取纬度   
lon_range = lon[(lon>117) & (lon<124)]
lat_range = lat[(lat>24) & (lat<33)]
lons,lats=np.meshgrid(lon_range,lat_range)#网格化
lon_2d, lat_2d = np.meshgrid(lon, lat)
lon_2d[lon_2d > 180] = lon_2d[lon_2d > 180] - 360
for hour in hours:
    ds_time = '%s'%(year)+'-'+'%s'%(month)+'-'+'%s'%(day)+'T'+'%s'%(hour)
    wind_shear(ds, lon_range, lat_range, lons, lats, time, ds_time)
for hour in hours:
    ds_time = '%s'%(year)+'-'+'%s'%(month)+'-'+'%s'%(day)+'T'+'%s'%(hour)            
    draw_fet(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
for hour in hours:
    ds_time = '%s'%(year)+'-'+'%s'%(month)+'-'+'%s'%(day)+'T'+'%s'%(hour)            
    draw_vert(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
for hour in hours:
    ds_time = '%s'%(year)+'-'+'%s'%(month)+'-'+'%s'%(day)+'T'+'%s'%(hour)            
    draw_vf(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
for hour in hours:
    ds_time = '%s'%(year)+'-'+'%s'%(month)+'-'+'%s'%(day)+'T'+'%s'%(hour)            
    draw_vort(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
for hour in hours:
    ds_time = '%s'%(year)+'-'+'%s'%(month)+'-'+'%s'%(day)+'T'+'%s'%(hour)            
    draw_w(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
for hour in hours:
    ds_time = '%s'%(year)+'-'+'%s'%(month)+'-'+'%s'%(day)+'T'+'%s'%(hour)            
    draw_index(ds1, lon_range, lat_range, lons, lats, time, ds_time)

此段代码,是运用在main函数中的,每一个for对应的函数,运行时都需要读取一次数据,然后计算后再绘图。经过测试,单独的7个for循环,比一个for带上所有函数能节省一半的时间。
个人理解:以wind_shear为例,单独的for循环中,计算一次后再按不同hour绘图7次即可,而整体的for循环需要7次计算再7次绘图。
所以有没有什么方式使得更加简洁,同时提高运行速度。

可以考虑使用并行计算来加快代码运行速度。使用multiprocessing库可以在多个进程中同时运行这些函数

参考GPT和自己的思路,您可以尝试以下优化方式:

缓存重复读取的数据,避免重复读取。比如将 ds1 和 ds 存储在变量中,在需要时直接使用变量即可。

对于需要循环的数据,可以考虑使用 numpy 的数组计算,避免使用循环,提高代码运行速度。

使用并行计算库,如 multiprocessing、joblib 等,将代码运行分配到多个 CPU 核心上并行计算。

例如,对于您的代码,可以将数据读取和网格化部分提取出来,避免每次循环都读取和网格化。同时,对于循环部分,可以使用 numpy 的数组计算和 joblib 库的并行计算来提高速度,示例代码如下:

import numpy as np
from joblib import Parallel, delayed

# 读取数据
ds1 = xr.open_dataset('d:/history_weather/%s'% (time)+'/CDS%s'% (time)+'ts-s.nc')
ds = xr.open_dataset('d:/history_weather/%s'% (time)+'/CDS%s'% (time)+'ts-h.nc')
lon = ds1['longitude']
lat = ds1['latitude']

# 网格化
lon_range = lon[(lon>117) & (lon<124)]
lat_range = lat[(lat>24) & (lat<33)]
lons,lats=np.meshgrid(lon_range,lat_range)
lon_2d, lat_2d = np.meshgrid(lon, lat)
lon_2d[lon_2d > 180] = lon_2d[lon_2d > 180] - 360

# 并行计算绘图
def compute_draw(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time, func):
    data = func(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw(data)

Parallel(n_jobs=-1)(
    delayed(compute_draw)(
        ds, lon_range, lat_range, lon, lat, lons, lats, time, '%s'%(year)+'-'+'%s'%(month)+'-'+'%s'%(day)+'T'+'%s'%(hour), func)
    for func in [wind_shear, draw_fet, draw_vert, draw_vf, draw_vort, draw_w, draw_index]
    for hour in hours
)

在以上示例代码中,使用了并行计算库 joblib 来实现对每个函数的并行运行,通过设置 n_jobs=-1 可以让程序自动利用所有可用的 CPU 核心进行计算。同时,将循环部分移动到 Parallel 函数内部,在循环内部计算和绘图。通过这种方式,可以提高代码的运行效率,减少重复计算。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
首先,我们可以将读取数据的代码放在一个for循环外面,这样就可以只读取一次数据,而不是每个子函数都要读取一次:

ds1 = xr.open_dataset('d:/history_weather/%s'% (time)+'/CDS%s'% (time)+'ts-s.nc')
ds = xr.open_dataset('d:/history_weather/%s'% (time)+'/CDS%s'% (time)+'ts-h.nc')
lon = ds1['longitude']#读取经度
lat = ds1['latitude']#读取纬度
lon_range = lon[(lon>117) & (lon<124)]
lat_range = lat[(lat>24) & (lat<33)]
lons,lats=np.meshgrid(lon_range,lat_range)#网格化
lon_2d, lat_2d = np.meshgrid(lon, lat)
lon_2d[lon_2d > 180] = lon_2d[lon_2d > 180] - 360

for hour in hours:
    ds_time = '%s'%(year)+'-'+'%s'%(month)+'-'+'%s'%(day)+'T'+'%s'%(hour)  
    wind_shear(ds, lon_range, lat_range, lons, lats, time, ds_time)
    draw_fet(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw_vert(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw_vf(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw_vort(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw_w(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw_index(ds1, lon_range, lat_range, lons, lats, time, ds_time)

此外,我们可以使用numba库加速代码运行。

from numba import jit

@jit(nopython=True)
def wind_shear_numba(u10, v10, u500, v500, lon_range, lat_range, lons, lats):
    # 将wind_shear函数中的相关内容放到一个用numba装饰的函数中,
    # 并将函数中使用的循环全部换成numba支持的循环形式(例如prange等)
    # 具体要根据函数中的实现细节进行修改
    pass

for hour in hours:
    ds_time = '%s'%(year)+'-'+'%s'%(month)+'-'+'%s'%(day)+'T'+'%s'%(hour)
    u10 = ds.u10.sel(time=ds_time).sel(longitude=lon_range).sel(latitude=lat_range).values
    v10 = ds.v10.sel(time=ds_time).sel(longitude=lon_range).sel(latitude=lat_range).values
    u500 = ds['u'].sel(time=ds_time, level=500).sel(longitude=lon_range).sel(latitude=lat_range).values
    v500 = ds['v'].sel(time=ds_time, level=500).sel(longitude=lon_range).sel(latitude=lat_range).values

    # 直接调用使用numba装饰的函数
    wind_shear_numba(u10, v10, u500, v500, lon_range, lat_range, lons, lats)

需要注意的是,使用numba需要有一些限制,例如函数中的循环不能是动态(Python风格的)循环,需要使用静态循环,同时需要注意一些数据类型的使用等等。因此具体实现需要考虑具体的函数实现细节。
如果我的回答解决了您的问题,请采纳!

“Devil组”引证GPT后的撰写:
把for循环的函数的计算和绘图整合到一个for循环中

ds1 = xr.open_dataset('d:/history_weather/%s'% (time)+'/CDS%s'% (time)+'ts-s.nc')
ds = xr.open_dataset('d:/history_weather/%s'% (time)+'/CDS%s'% (time)+'ts-h.nc')
lon = ds1['longitude']#读取经度
lat = ds1['latitude']#读取纬度   
lon_range = lon[(lon>117) & (lon<124)]
lat_range = lat[(lat>24) & (lat<33)]
lons,lats=np.meshgrid(lon_range,lat_range)#网格化
lon_2d, lat_2d = np.meshgrid(lon, lat)
lon_2d[lon_2d > 180] = lon_2d[lon_2d > 180] - 360

for hour in hours:
    ds_time = '%s'%(year)+'-'+'%s'%(month)+'-'+'%s'%(day)+'T'+'%s'%(hour)
    wind_shear(ds, lon_range, lat_range, lons, lats, time, ds_time)
    draw_fet(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw_vert(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw_vf(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw_vort(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw_w(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw_index(ds1, lon_range, lat_range, lons, lats, time, ds_time)


该回答引用ChatGPT

有几个技巧可以帮助提高 Python 计算绘图代码的运行速度:

1、避免重复读取数据
在循环中多次读取同一数据集会浪费时间和内存。可以在循环外读取一次数据,然后将数据传递给每个函数。

2、合并多个循环
可以将多个循环合并成一个,这样可以避免重复读取数据并提高代码的效率。

3、并行化
使用并行计算可以将计算任务分配给多个处理器或核心,从而提高代码的运行速度。

下面是一个修改后的示例代码,其中采用了上述技巧:


ds1 = xr.open_dataset('d:/history_weather/%s' % (time) + '/CDS%s' % (time) + 'ts-s.nc')
ds = xr.open_dataset('d:/history_weather/%s' % (time) + '/CDS%s' % (time) + 'ts-h.nc')
lon = ds1['longitude']  # 读取经度
lat = ds1['latitude']  # 读取纬度
lon_range = lon[(lon > 117) & (lon < 124)]
lat_range = lat[(lat > 24) & (lat < 33)]
lons, lats = np.meshgrid(lon_range, lat_range)  # 网格化
lon_2d, lat_2d = np.meshgrid(lon, lat)
lon_2d[lon_2d > 180] = lon_2d[lon_2d > 180] - 360

def draw_all(ds, ds1, lon_range, lat_range, lon, lat, lons, lats, time, hours):
    from concurrent.futures import ThreadPoolExecutor
    
    def draw_hour(hour):
        ds_time = '%s' % (year) + '-' + '%s' % (month) + '-' + '%s' % (day) + 'T' + '%s' % (hour)
        wind_shear(ds, lon_range, lat_range, lons, lats, time, ds_time)
        draw_fet(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
        draw_vert(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
        draw_vf(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
        draw_vort(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
        draw_w(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
        draw_index(ds1, lon_range, lat_range, lons, lats, time, ds_time)
    
    with ThreadPoolExecutor() as executor:
        executor.map(draw_hour, hours)
        
draw_all(ds, ds1, lon_range, lat_range, lon, lat, lons, lats, time, hours)

参考chatGPT的内容和自己的思路,以减少重复计算,从而提高代码的运行速度。以下是优化建议:

1.将读取经纬度数据和网格化操作移至循环外部。这些操作只需要在程序开始时执行一次,而不是每个循环内部都执行一次。

2.将不需要修改的变量(如lon和lat)定义为常量,以避免每次循环都重新定义变量。

3.将多个循环合并为一个循环,从而减少循环的数量。

4.使用NumPy的向量化运算来代替循环,因为NumPy的向量化运算在处理大量数据时比循环更高效。

5.尝试使用并行计算(例如使用multiprocessing库)来加速程序。
下面是优化后的代码:

import multiprocessing as mp

# 读取经度和纬度数据
ds1 = xr.open_dataset('d:/history_weather/%s'% (time)+'/CDS%s'% (time)+'ts-s.nc')
ds = xr.open_dataset('d:/history_weather/%s'% (time)+'/CDS%s'% (time)+'ts-h.nc')
lon = ds1['longitude'].values # 读取经度
lat = ds1['latitude'].values # 读取纬度
lon[lon > 180] -= 360 # 将经度从0-360转换为-180-180

# 定义常量
lon_range = lon[(lon>117) & (lon<124)]
lat_range = lat[(lat>24) & (lat<33)]
lons,lats=np.meshgrid(lon_range,lat_range)#网格化
lon_2d, lat_2d = np.meshgrid(lon, lat)

def process_hour(hour):
    ds_time = '%s'%(year)+'-'+'%s'%(month)+'-'+'%s'%(day)+'T'+'%s'%(hour)
    # 调用各个函数
    wind_shear(ds, lon_range, lat_range, lons, lats, time, ds_time)
    draw_fet(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw_vert(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw_vf(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw_vort(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw_w(ds, lon_range, lat_range, lon, lat, lons, lats, time, ds_time)
    draw_index(ds1, lon_range, lat_range, lons, lats, time, ds_time)

if __name__ == '__main__':
    # 将循环合并为一个循环,并使用并行计算
    with mp.Pool() as pool:
        pool.map(process_hour, hours)


回答不易,还请采纳!!!