HOWTO · NumPy

如何从 NumPy 数组中获取列

从二维 NumPy 数组中选择一列或多列、保留所需形状、筛选行,并根据需要选择视图或副本。

对于二维 NumPy 数组,可使用 array[:, column_index] 选择一列。冒号选择所有行,整数选择从零开始编号的列。例如,data[:, 1] 会将第二列作为一维数组返回。如果后续代码要求二维的 (rows, 1) 结果,请改用 data[:, 1:2]

选择一列并控制其形状

NumPy 使用 array[row_selection, column_selection] 的形式为二维数组建立索引。省略逗号会改变操作:data[:][1] 会先创建完整切片,然后选择行 1,因此它并不会选择列 1。

下面的可复现程序涵盖了常见的列选择情况:

import numpy as np

data = np.array([[10, 11, 12], [20, 21, 22], [30, 31, 32]])

second = data[:, 1]
second_2d = data[:, 1:2]
adjacent = data[:, :2]
reordered = data[:, [2, 0]]
taken = np.take(data, [0, 2], axis=1)
filtered = data[data[:, 0] >= 20, 2]

print("second:", second, second.shape)
print("second_2d:\n", second_2d, second_2d.shape)
print("adjacent:\n", adjacent)
print("reordered:\n", reordered)
print("taken:\n", taken)
print("filtered:", filtered)

view = data[:, 1]
copy = data[:, 1].copy()
view[0] = 99
print("data after view edit:\n", data)
print("copy:", copy)

输出:

second: [11 21 31] (3,)
second_2d:
 [[11]
 [21]
 [31]] (3, 1)
adjacent:
 [[10 11]
 [20 21]
 [30 31]]
reordered:
 [[12 10]
 [22 20]
 [32 30]]
taken:
 [[10 12]
 [20 22]
 [30 32]]
filtered: [22 32]
data after view edit:
 [[10 99 12]
 [20 21 22]
 [30 31 32]]
copy: [11 21 31]

整数列索引会移除该轴,这就是 second 的形状为 (3,) 的原因。只有一个元素的切片会保留该轴,因此 second_2d 的形状为 (3, 1)。当 API 要求类似矩阵的输入而非扁平向量时,这一区别非常重要。

选择多列

对于相邻列,请使用 data[:, :2] 这样的切片。切片表示连续范围,通常会生成视图。对于不相邻的列或自定义顺序,请提供索引列表:data[:, [2, 0]]。这属于高级索引,会生成副本而非视图。

索引列表始终会保留列轴。因此,data[:, [1]] 的形状是 (3, 1),而 data[:, 1] 的形状是 (3,)。列表还可以重复列或改变其位置,因此,如果动态提供的索引中出现重复项或顺序变化会令人意外,请进行验证。当列构成简单范围时使用切片,当选择本身是序列时使用索引列表。

np.take 是一种显式指定轴的替代方案。np.take(data, [0, 2], axis=1) 会选择列 0 和列 2;由于列位于第二个轴上,axis=1 必不可少。当轴或索引存储在变量中时,此方法尤其有用。

读取列之前筛选行

布尔条件通常标识行,而不是列。先根据某一列创建行掩码,然后在逗号后指定所需的输出列。在示例中,data[:, 0] >= 20 会保留最后两行,而 data[data[:, 0] >= 20, 2] 会返回列 2 中的 [22 32]。掩码长度必须与行数相同。

确定需要视图还是副本

根据 NumPy 的索引规则,基本切片会尽可能返回视图,而高级索引则返回副本。因此,通过 view = data[:, 1] 赋值可能会更改 data,输出中的 99 就说明了这一点。如果提取的列必须独立存在,请调用 .copy()。视图更改源数组后,复制的值仍为 [11 21 31]

这些视图还会使其基础数组的内存保持可访问状态。如果只保留一个小列却导致不再需要的大型源数组一直占用内存,请复制该列。

检查维度和列边界

这些表达式假定数组是二维的。一维数组没有列轴,因此 array[:, 0] 会引发索引错误。当输入形状不确定时,请检查 array.ndim == 2。对于二维数组,有效的非负列索引范围是 0array.shape[1] - 1;超出此范围的索引会引发 IndexError。负索引在边界内也有效,因此 array[:, -1] 会选择最后一列。