如何高效过滤0值无效值并实现多数组对应位置求平均?
优化方案
你现有代码慢的核心原因是用了两层Python原生循环逐元素遍历数组,Python循环的执行开销极高,完全没有利用numpy的向量化运算能力——numpy的向量化操作会把运算下沉到C层面执行,同等逻辑速度比Python循环快2个数量级左右。
你的计算逻辑本身没问题,只需要把所有内层逐元素遍历的Python循环替换成numpy向量化操作即可,不需要改动整体流程:
- 对每个读入的LST数组,直接生成布尔型的有效位掩码(值非0的位置为True,0值位置为False)
- 求和数组直接累加「原数组 * 掩码」的结果,0值位置自然不会贡献累加值
- 计数数组直接累加掩码本身(True会被当作1,False当作0,刚好统计每个位置的有效值个数)
- 最终求平均的步骤也用numpy的带条件除法实现,自动跳过分母为0的位置
优化后的完整代码如下:
from pyhdf.SD import SD, SDC import numpy as np import os a, b = lst_template.shape sum_arr = np.zeros((a, b), dtype=np.float64) denominator_arr = np.zeros((a, b), dtype=np.int32) for filename in os.listdir(file_location): file_path = os.path.join(file_location, filename) try: file = SD(file_path) lst = file.select('LST').get() if lst.shape != (a, b): print(f'shape incorrect {filename}') continue # 向量化操作替代双层Python循环 valid_mask = lst != 0 sum_arr += lst * valid_mask denominator_arr += valid_mask print(filename) except Exception as e: print(e) print(filename) # 逐位置求平均,分母为0的位置默认输出0 result_arr = np.divide( sum_arr, denominator_arr, out=np.zeros((a, b), dtype=np.float32), where=denominator_arr != 0 )
可选进一步优化
如果你的单景LST数组尺寸不大,内存足够放下全部800景数组,可以用更简洁的np.nanmean实现,连手动维护求和、计数数组的步骤都可以省掉:
valid_lst_list = [] for filename in os.listdir(file_location): # 省略文件读入、形状校验、异常捕获逻辑,和前面一致 # 把形状符合要求的lst加入列表 if lst.shape == (a, b): valid_lst_list.append(lst) # 堆叠为三维数组,把0值替换为nan后沿新维度求均值,自动跳过nan all_lst = np.stack(valid_lst_list, axis=0).astype(np.float64) all_lst[all_lst == 0] = np.nan result_arr = np.nanmean(all_lst, axis=0)
这个写法代码更短,但是会额外占用存储全部数组的内存,如果单景数组尺寸很大(比如单景几GB)就不适合,用前面逐文件累加的方案内存占用固定,和文件总数无关,稳定性更好。
另外注意几个细节:
- 累加求和的数组建议用
float64类型,避免800组数据累加后出现数值溢出 - 路径拼接不要直接用字符串相加,用
os.path.join可以避免跨平台的路径分隔符问题
内容的提问来源于stack exchange,提问作者FarmerZee
相关产品推荐
相关产品推荐

