本系列是《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 条"极常用,负索引是标配。
小结
- 一维:
arr[start:stop:step],和列表一致 - 多维:
arr[行, 列, ...],逗号分隔 - 取不连续:花式索引
arr[[i, j]]、布尔索引arr[mask] - ⚠️ 基本索引/切片返回视图,改它会污染原数组;要副本用
.copy() - 花式索引/布尔索引返回副本,天然安全
下一章:在取出来的数据上做运算。
上一篇:第 2 章 数组属性 | 下一篇:第 4 章 数组运算