为什么学Numpy数组?

在Python数据分析和科学计算中,Numpy是基础中的基础。它提供了高效的多维数组对象,能让我们像处理简单数据一样操作复杂的数值集合,而且运算速度比纯Python列表快得多。要深入理解Numpy,必须掌握数组的结构(shape)、如何访问元素(索引)和如何截取子数组(切片),这也是本文的核心内容。

一、数组的创建:从基础开始

要操作数组,首先得创建它。Numpy提供了多种创建数组的方法,初学者可以从这几个最常用的开始:

1. np.array():从Python列表创建

import numpy as np

# 1维数组(向量)
arr1 = np.array([1, 2, 3, 4, 5])
print("1维数组:", arr1)

# 2维数组(矩阵)
arr2 = np.array([[1, 2, 3], 
                [4, 5, 6], 
                [7, 8, 9]])
print("2维数组:\n", arr2)

2. 快速创建全0/全1数组

# 3行4列的全0数组
zeros = np.zeros((3, 4))  # shape=(3,4)
print("全0数组:\n", zeros)

# 2行5列的全1数组
ones = np.ones((2, 5))
print("全1数组:\n", ones)

3. np.arange():类似Python的range

# 从0到9,步长为2(生成数组[0,2,4,6,8])
arange_arr = np.arange(0, 10, 2)
print("arange数组:", arange_arr)

二、shape:数组的“维度身份证”

shape是Numpy数组的核心属性,它告诉我们数组的维度信息(比如“几行几列”)。理解shape是处理数组的第一步。

1. 查看shape

.shape 属性查看数组的形状:

print("arr1的shape:", arr1.shape)  # 输出 (5,) → 1维数组,长度5
print("arr2的shape:", arr2.shape)  # 输出 (3, 3) → 3行3列的矩阵

2. 修改shape:reshape()

reshape() 可以调整数组的维度,但总元素数必须不变。例如:

# 将1维数组转为2行3列(原长度5?不行!必须保证总元素数不变)
# 修正:arange_arr是5个元素,reshape(2,3)会报错。换个例子:
arr3 = np.arange(6)  # [0,1,2,3,4,5],shape=(6,)
arr3_reshaped = arr3.reshape(2, 3)  # 转为2行3列
print("reshape后的数组:\n", arr3_reshaped)
print("新shape:", arr3_reshaped.shape)  # (2, 3)

关键点:

  • reshape() 只是调整维度,不会改变数据顺序(C-style行优先)。
  • 如果想转成1行多列,可以用 reshape(1, -1)-1表示自动计算列数)。

三、索引:访问数组元素

索引就像“钥匙”,让我们能精准找到数组中的某个元素。Numpy数组的索引规则和Python列表类似,但支持更简洁的多维度索引。

1. 1维数组索引

和Python列表一样,1维数组的索引从0开始,支持正负索引(负索引表示“倒数第几个”):

print("arr1[0] =", arr1[0])   # 第一个元素:1
print("arr1[-1] =", arr1[-1]) # 最后一个元素:5
print("arr1[2] =", arr1[2])   # 第三个元素:3

2. 2维数组索引

2维数组需要两个索引(行索引,列索引),用逗号分隔:

# arr2 = [[1,2,3], [4,5,6], [7,8,9]]
print("arr2[1, 2] =", arr2[1, 2])   # 第2行(索引1)第3列(索引2)→ 6
print("arr2[0, 0] =", arr2[0, 0])   # 第1行第1列 → 1
print("arr2[-1, -1] =", arr2[-1, -1]) # 最后一行最后一列 → 9

注意:

  • 不要写成 arr2[1][2](虽然Python列表嵌套也支持,但Numpy数组更推荐 arr[i,j],效率更高)。

四、切片:截取子数组

切片允许我们批量提取元素,形成一个“子数组”。语法是 [start:end:step],其中 start 是起始位置,end 是结束位置(不包含),step 是步长。

1. 1维数组切片

# arr1 = [1,2,3,4,5]
print("arr1[1:4] =", arr1[1:4])    # 从索引1到4(不含4)→ [2,3,4]
print("arr1[::2] =", arr1[::2])    # 步长2 → [1,3,5]
print("arr1[:3] =", arr1[:3])      # 省略start → 前3个元素 → [1,2,3]
print("arr1[::] =", arr1[::])      # 省略step和start/end → 整个数组

2. 2维数组切片

2维数组切片需要分别指定行切片和列切片,用逗号分隔:

# arr2 = [[1,2,3], [4,5,6], [7,8,9]]
print("arr2[1:3, 0:2] = \n", arr2[1:3, 0:2])  
# 解释:取第1-3行(不含3),第0-2列(不含2) → [[4,5], [7,8]]

print("arr2[:, 1] =", arr2[:, 1])  # 所有行,第1列 → [2,5,8]
print("arr2[0:2, :] = \n", arr2[0:2, :])  # 前2行,所有列 → [[1,2,3], [4,5,6]]

3. 切片的“视图”特性

Numpy切片默认返回的是原数组的“视图”(共享数据内存),修改子数组会影响原数组:

sub_arr = arr2[1:3, 0:2]  # 子数组
sub_arr[0, 0] = 100       # 修改子数组
print("修改后原数组:\n", arr2)  # 原数组的[1,0]位置也被修改为100

如何避免修改原数组?

.copy() 方法创建独立的拷贝:

sub_arr = arr2[1:3, 0:2].copy()  # 拷贝子数组
sub_arr[0, 0] = 200              # 修改拷贝,原数组不变
print("原数组未被修改:\n", arr2)

五、总结与练习建议

  • shape:理解数组的维度,用 reshape() 调整维度。
  • 索引:1维数组类似列表,2维数组需行+列双索引,支持正负索引。
  • 切片:通过 [start:end:step] 截取子数组,注意视图与拷贝的区别。

练习:尝试用Numpy创建一个5x5的随机数组,然后:
1. 查看它的shape;
2. 用索引访问第3行第4列的元素;
3. 用切片截取前3行前3列的子数组;
4. 对切片后的子数组修改,观察原数组是否变化。

通过动手实践,你会更快掌握Numpy数组的核心操作!

小夜