在真实数据集中,经常会出现缺失或无效的数据。NumPy的掩码数组(MaskedArray)通过一个独立的布尔掩码来标记哪些数据是无效的,从而在计算时自动忽略这些值。这使得处理缺失值变得简单而高效。
掩码数组是 numpy.ma 模块提供的 MaskedArray 类,它包含两个核心部分:数据数组和掩码数组。掩码是一个布尔数组,True 表示对应位置的数据无效(被掩码),False 表示有效。当对掩码数组执行运算时,被掩码的值会被自动忽略。
使用 np.ma.masked_array 可以直接从数据和掩码创建。
import numpy as np
import numpy.ma as ma
data = np.array([1, 2, 3, 4, 5])
mask = np.array([False, False, True, False, True]) # 标记索引2和4为无效
masked_arr = ma.masked_array(data, mask=mask)
print("掩码数组:", masked_arr)
输出:
掩码数组: [1 2 -- 4 --]
其中 -- 表示被掩码的值。
np.ma.masked_where(condition, arr) 会将满足条件的位置掩码。
arr = np.array([1, 2, 3, 4, 5])
masked = ma.masked_where(arr > 3, arr)
print("掩码大于3的值:", masked) # [1 2 3 -- --]
使用 np.ma.masked_invalid 可以自动掩码 NaN 和 inf。
arr_with_nan = np.array([1, 2, np.nan, 4, np.inf])
masked_invalid = ma.masked_invalid(arr_with_nan)
print("掩码无效值:", masked_invalid) # [1.0 2.0 -- 4.0 --]
masked_values
如果需要掩码特定的数值(如 -9999 表示缺失值),可以使用 masked_values。
arr = np.array([10, -9999, 30, -9999, 50])
masked_val = ma.masked_values(arr, -9999)
print("掩码特定值:", masked_val) # [10 -- 30 -- 50]
通过 .data 和 .mask 属性可以分别访问原始数据和掩码。
print("数据:", masked_arr.data)
print("掩码:", masked_arr.mask)
输出:
数据: [1 2 3 4 5]
掩码: [False False True False True]
a = ma.masked_where([False, True, False], [1, 2, 3])
b = ma.masked_where([False, False, True], [10, 20, 30])
print("a:", a) # [1 -- 3]
print("b:", b) # [10 20 --]
c = a + b
print("a + b:", c) # [11 -- --] (被掩码的位置结果也被掩码)
arr = ma.masked_where([False, True, False, False], [1, 2, 3, 4])
print("数组:", arr) # [1 -- 3 4]
print("总和:", arr.sum()) # 1+3+4 = 8
print("均值:", arr.mean()) # 8/3 ≈ 2.6667
filled()
使用 filled(fill_value) 可以将掩码值替换为指定值,返回普通数组。
filled_arr = arr.filled(0)
print("填充0:", filled_arr) # [1 0 3 4]
compressed()
compressed() 返回只包含有效值的一维数组。
compressed = arr.compressed()
print("压缩后:", compressed) # [1 3 4]
可以直接修改 .mask 属性,或者使用 masked_where 重新掩码。
arr.mask[2] = True # 将索引2也掩码
print("修改后:", arr) # [1 -- -- 4]
ma.masked_array 或 ma.masked_where 等。filled() 填充缺失值,或 compressed() 丢弃缺失值。np.array(masked_arr) 会丢失掩码信息,返回原始数据(包含无效值),通常不推荐。# 不推荐:直接转回普通数组会包含原始值(包括原本掩码的值)
plain = np.array(masked_arr)
print("直接转回:", plain) # [1 2 3 4 5]
numpy.ma 模块提供了与 numpy 对应的函数,它们自动处理掩码。例如:
ma.sum, ma.mean, ma.std, ma.varma.max, ma.minma.concatenateprint("最大值:", ma.max(arr))
print("最小值:", ma.min(arr)) # 忽略掩码
假设有一组温度数据,其中 -999 表示缺失值。
temps = np.array([25, 28, -999, 22, 23, -999, 30])
# 将 -999 掩码
masked_temps = ma.masked_values(temps, -999)
print("掩码后温度:", masked_temps)
print("有效温度均值:", masked_temps.mean())
print("有效温度标准差:", masked_temps.std())
输出:
掩码后温度: [25 28 -- 22 23 -- 30]
有效温度均值: 25.6
有效温度标准差: 3.114482
Series/DataFrame 可以相互转换,pandas 的 isnull 也提供了类似功能。np.nanmean 等函数,但掩码数组提供了更灵活的控制(例如标记任意值为无效)。
掩码数组是 NumPy 处理缺失值和无效数据的强大工具。通过 numpy.ma 模块,可以轻松标记无效数据,并在后续计算中自动忽略它们,从而避免手动过滤的麻烦。掌握掩码数组的创建、修改和应用,能让你更高效地进行数据清洗和分析。下一章我们将讨论 NumPy 的性能优化技巧。