本系列是《NumPy 速通》的配套章节。

索引和切片就是"从数组中取东西",是使用频率最高的操作。一维和 Python 列表几乎一样,关键是多维用逗号分隔各维度,还有最容易被误伤的花式索引和"视图"问题。

一维数组的索引与切片

import numpy as np

arr = np.array([10, 20, 30, 40, 50, 60, 70, 80])

# —— 单点索引 ——
arr[0]      # 10  第一个元素(从 0 开始)
arr[-1]     # 80  最后一个
arr[-2]     # 70  倒数第二个

# —— 切片:arr[start:stop:step] ——
arr[1:4]    # [20 30 40]  索引 1,2,3(不包含 4)
arr[:3]     # [10 20 30]  从开头到索引 2
arr[3:]     # [40 50 60 70 80]  从索引 3 到最后
arr[::2]    # [10 30 50 70]  每隔一个取一个
arr[::-1]   # [80 70 60 50 40 30 20 10]  倒序
arr[::-2]   # [80 60 40 20]  倒序且隔一个

规则:[start:stop:step],start 包含、stop 不包含。省略 start 默认 0,省略 stop 默认末尾,省略 step 默认 1。负 step 表示反向。

切片越界不报错

arr[0:100]   # 整个数组,超界也不报错(Python 列表同理)
arr[5:2]     # 空数组(start > stop 且 step 为正)

NumPy 切片很宽容,越界会被静默裁剪——这有时是 bug 的源头(你以为取到了,其实没有)。

二维数组的索引

关键:arr[行, 列],逗号左边是行,右边是列。

arr = np.array([[1, 2, 3],
                [4, 5, 6]])

arr[0, 1]      # 2         第 1 行第 2 列
arr[1, :]      # [4 5 6]   第 2 行(: 表示整行)
arr[:, 1]      # [2 5]     第 2 列(: 表示整列)
arr[0:2, 1:3]  # [[2 3] [5 6]]  子矩阵(第 1-2 行、第 2-3 列)
arr[0]         # [1 2 3]   只写一个下标 → 取第 1 行(降维成一维)

💡 只写一个下标 arr[0] 得到第 1 行——注意它降维了(二维→一维)。想要"保留行的二维结构"用 arr[0:1, :](切片)或 arr[[0], :](花式索引,见下)。

三维数组同理,arr[z, y, x] 三个下标,逗号分隔每个维度即可。

花式索引:用索引数组取多行/多列

不用连续切片,而是用数组指定要哪几个位置——取"不连续的行/列"全靠它:

arr = np.array([10, 20, 30, 40, 50])
idx = np.array([0, 2, 4])

arr[idx]       # [10 30 50]  一维花式索引

# 二维:取指定的几行
scores = np.array([[90, 80, 85],
                   [60, 70, 65],
                   [95, 88, 92],
                   [72, 65, 78],
                   [55, 60, 70]])
scores[[0, 2, 4], :]   # 第 1、3、5 位同学
# [[90 80 85]
#  [95 88 92]
#  [55 60 70]]

# 取指定的几列
scores[:, [0, 2]]      # 语文和英语两列

# 用布尔数组筛选(完整版见第 9 章)
mask = scores[:, 0] > 70   # 语文及格的
scores[mask]              # 只保留这些行

花式索引 vs 切片:一个返回副本,一个返回视图

这是新手最容易懵的地方,用一张表记住:

方式 返回 修改会影响原数组吗?
基本索引 arr[0] 降维 + 视图 ⚠️ 会
切片 arr[0:2] 视图 ⚠️ 会
花式索引 arr[[0,2]] 副本 ✅ 不会
布尔索引 arr[mask] 副本 ✅ 不会
arr.take([0,2]) 副本 ✅ 不会
b = arr[[0, 2]]      # 花式索引 → 副本
b[0] = 999
print(arr[0])        # 还是 10,原数组没被影响 ✅

⚠️ 灵魂警告:切片是视图,不是副本!

arr = np.array([10, 20, 30, 40, 50, 60, 70, 80])

b = arr[1:4]         # 视图
b[0] = 999
print(arr)           # [10 999 30 40 50 60 70 80]  原数组被改了!

切片返回的是原数组的视图(共享底层内存),改视图 = 改原数组。这是新手最大噩梦——你以为在操作副本,实际污染了源数据。

什么时候会踩坑:把切片当"独立数据"传进函数、在函数里修改、或先切片再逐项赋值时。

解法:要独立副本就 .copy():

c = arr[1:4].copy()   # 真正的独立副本
c[0] = 999            # 原数组无影响 ✅

💡 视图不全是坏事:视图零拷贝、零内存开销,对大数据切片再统计非常高效(比如 arr[::100].mean() 不复制数据)。“只读不改"时视图是优点;“要改"时记得 .copy()。

反向技巧:哪里是"最后一个”

arr[-1]      # 最后一个元素
arr[-1, :]   # 二维:最后一行
arr[:, -1]   # 二维:最后一列
arr[-3:, :]  # 最后三行

大数据处理里"取最后 N 条"极常用,负索引是标配。

小结

  1. 一维:arr[start:stop:step],和列表一致
  2. 多维:arr[行, 列, ...],逗号分隔
  3. 取不连续:花式索引 arr[[i, j]]、布尔索引 arr[mask]
  4. ⚠️ 基本索引/切片返回视图,改它会污染原数组;要副本用 .copy()
  5. 花式索引/布尔索引返回副本,天然安全

下一章:在取出来的数据上做运算。


上一篇:第 2 章 数组属性 | 下一篇:第 4 章 数组运算