如何使用Cython优化数组循环以提升处理效率?
用Cython优化栅格作物面积分配的数组迭代
先分析原代码的性能瓶颈
你的代码慢主要有两个核心问题:
- 三重嵌套循环+冗余赋值:内层循环每次给所有栅格单元的
Maize列赋同一个值,最终只会保留最后一次循环的结果,逻辑上存在大量冗余计算。 - Pandas
loc的高开销:循环内频繁调用df_area.loc[n,"Maize"],这是带标签查找的高层操作,每次调用都有Python对象的额外开销,循环次数多了会严重拖慢速度。
优先尝试NumPy向量化优化(比Cython更易实现)
如果你的数据结构是每个栅格对应12个月的记录,可直接用NumPy的向量化操作替代循环,性能提升会非常明显:
import numpy as np import pandas as pd # 提取玉米生长季的起止月份 k = df_dist.Planting_month[5] l = df_dist.Maturity_month[5] start_month = min(k, l) end_month = max(k, l) # 假设arr_5是对应每个栅格的玉米面积数组(形状为(N,),N是栅格数量) # 生成生长季月份的掩码(匹配df_area中属于生长季的行) month_mask = df_area['month'].isin(range(start_month, end_month + 1)) # 为生长季的行赋值:每个栅格对应end_month - start_month + 1个月份,重复arr_5的值 df_area.loc[month_mask, 'Maize'] = np.repeat(arr_5, end_month - start_month + 1)
用Cython进一步优化(适合超大规模数据)
如果NumPy的速度还不够,可通过Cython将循环编译为纯C代码,彻底消除Python解释器的开销:
步骤1:编写Cython代码文件(crop_area.pyx)
import numpy as np cimport numpy as np cimport cython # 关闭边界检查和负索引检查,提升运行速度 @cython.boundscheck(False) @cython.wraparound(False) def assign_maize_area( np.ndarray[np.float64_t, ndim=1] grid_area, # 每个栅格的玉米面积 np.ndarray[np.int32_t, ndim=1] row_months, # df_area每行对应的月份 int start_month, int end_month, np.ndarray[np.float64_t, ndim=1] maize_column # df_area['Maize']的NumPy视图 ): cdef int total_rows = row_months.shape[0] cdef int grid_count = grid_area.shape[0] cdef int row_idx, grid_idx # 遍历所有行,根据月份判断是否属于生长季,直接赋值 for row_idx in range(total_rows): # 每个栅格对应连续12个月,计算当前行所属的栅格索引 grid_idx = row_idx // 12 if start_month <= row_months[row_idx] <= end_month: maize_column[row_idx] = grid_area[grid_idx]
步骤2:编写编译脚本(setup.py)
from setuptools import setup from Cython.Build import cythonize import numpy as np setup( ext_modules=cythonize("crop_area.pyx"), include_dirs=[np.get_include()] )
步骤3:编译并调用
运行以下命令编译Cython代码:
python setup.py build_ext --inplace
在主代码中调用编译后的函数:
import numpy as np import pandas as pd from crop_area import assign_maize_area # 准备输入数据 grid_area = maz_st_1 # 每个栅格的玉米面积数组(形状为(N,)) row_months = df_area['month'].values # df_area每行的月份(NumPy数组) start_month = min(df_dist.Planting_month[5], df_dist.Maturity_month[5]) end_month = max(df_dist.Planting_month[5], df_dist.Maturity_month[5]) maize_col = df_area['Maize'].values # 直接操作NumPy数组视图,避免Pandas开销 # 执行Cython优化的赋值操作 assign_maize_area(grid_area, row_months, start_month, end_month, maize_col) # 可选:将结果写回DataFrame df_area['Maize'] = maize_col
Cython优化的核心点
- 静态类型声明:用
cdef声明变量和数组类型,让Cython生成纯C代码,避免Python对象的运行时开销。 - 关闭安全检查:
boundscheck(False)和wraparound(False)关闭不必要的边界和负索引检查,进一步提升速度。 - 直接操作内存:通过NumPy数组的底层内存进行赋值,完全绕过Pandas的高层API开销。
内容的提问来源于stack exchange,提问作者Endalkachew Kebede
相关产品推荐
相关产品推荐

