高级切片与赋值:np.newaxis
与 Ellipsis
的奇幻漂流之旅 🚀
各位观众老爷,各位靓仔靓女,欢迎来到 NumPy 切片世界的冒险乐园!今天,我们要挑战的是 NumPy 丛林中最神秘、最令人神往的两个精灵:np.newaxis
和 Ellipsis
。别怕,它们不是什么吃人的怪兽,而是能让你在多维数组里自由穿梭、优雅起舞的魔法棒!✨
准备好了吗?系好安全带,让我们一起踏上这段充满乐趣和惊喜的旅程吧!
1. 切片,切片,切糕也切给你看!🍰
在深入了解 np.newaxis
和 Ellipsis
之前,我们先来回顾一下 NumPy 切片的基操。毕竟,不会基础,再高级的魔法也施展不出来啊!
NumPy 的切片就像一把锋利的手术刀,能从数组中精准地切割出你想要的片段。它的基本语法是:
array[start:stop:step]
start
: 切片的起始索引(包含)。如果省略,则默认为 0。stop
: 切片的结束索引(不包含)。如果省略,则默认为数组的长度。step
: 切片的步长。如果省略,则默认为 1。
举个栗子:
import numpy as np
arr = np.arange(10) # 创建一个包含 0 到 9 的数组
print(arr) # 输出: [0 1 2 3 4 5 6 7 8 9]
print(arr[2:5]) # 输出: [2 3 4] 从索引 2 开始,到索引 5 结束(不包含 5)
print(arr[:5]) # 输出: [0 1 2 3 4] 从头开始,到索引 5 结束(不包含 5)
print(arr[5:]) # 输出: [5 6 7 8 9] 从索引 5 开始,到末尾结束
print(arr[::2]) # 输出: [0 2 4 6 8] 从头开始,到末尾结束,步长为 2
是不是很简单?就像切蛋糕一样,想要哪一块,就切哪一块!🎂
对于多维数组,我们可以对每个维度进行切片,用逗号分隔:
arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr_2d)
# 输出:
# [[1 2 3]
# [4 5 6]
# [7 8 9]]
print(arr_2d[0, 1:3]) # 输出: [2 3] 第一行,从索引 1 到 3 的元素
print(arr_2d[:, 0]) # 输出: [1 4 7] 所有行,第一列的元素
print(arr_2d[1:, 1:]) # 输出: [[5 6] [8 9]] 从第二行开始,从第二列开始
掌握了这些基础,我们就可以开始探索 np.newaxis
和 Ellipsis
的奇妙世界了!
2. np.newaxis
: 维度变形金刚 🤖
np.newaxis
,顾名思义,就是“新轴”。它的作用就像一个维度变形金刚,可以给数组增加一个维度,让你的数据在空间中自由伸展!
想象一下,你有一个一维数组,像一条直线。你突然想把它变成一个二维数组,像一张纸。怎么办?用 np.newaxis
就可以啦!
arr = np.array([1, 2, 3])
print(arr.shape) # 输出: (3,) 一维数组
# 使用 np.newaxis 增加一个行维度
row_vector = arr[np.newaxis, :]
print(row_vector.shape) # 输出: (1, 3) 二维数组,形状为 (1, 3),相当于一个行向量
print(row_vector)
# 输出: [[1 2 3]]
# 使用 np.newaxis 增加一个列维度
col_vector = arr[:, np.newaxis]
print(col_vector.shape) # 输出: (3, 1) 二维数组,形状为 (3, 1),相当于一个列向量
print(col_vector)
# 输出:
# [[1]
# [2]
# [3]]
看到了吗?np.newaxis
就像一个魔法师,轻轻一点,就把一维数组变成了二维数组! 🧙
为什么要增加维度呢?
增加维度有很多用处,最常见的场景就是进行广播(broadcasting)运算。广播是一种强大的机制,可以让形状不同的数组进行算术运算,而无需显式地进行循环。
举个栗子:
arr1 = np.array([1, 2, 3])
arr2 = np.array([[4, 5, 6], [7, 8, 9]])
# 如果直接相加,会报错,因为形状不匹配
# print(arr1 + arr2) # ValueError: operands could not be broadcast together with shapes (3,) (2,3)
# 使用 np.newaxis 将 arr1 变成一个行向量,然后进行广播
arr1_row = arr1[np.newaxis, :]
print(arr1_row + arr2)
# 输出:
# [[ 5 7 9]
# [ 8 10 12]]
# 使用 np.newaxis 将 arr1 变成一个列向量,然后进行广播
arr1_col = arr1[:, np.newaxis]
# 创建一个 shape 为 (2,3) 的数组,用于演示列向量的广播
arr3 = np.array([[4, 5, 6],[7,8,9]])
print(arr1_col + arr3)
# 输出:
# [[ 5 6 7]
# [ 9 10 11]
# [11 12 13]]
在这个例子中,arr1
的形状是 (3,)
,arr2
的形状是 (2, 3)
。如果直接相加,NumPy 会报错,因为形状不匹配。但是,我们可以使用 np.newaxis
将 arr1
变成一个形状为 (1, 3)
的行向量,或者形状为 (3, 1)
的列向量。然后,NumPy 会自动进行广播,将 arr1_row
或 arr1_col
扩展到与 arr2
相同的形状,再进行相加。
是不是很神奇? ✨ np.newaxis
就像一个维度魔术师,让广播运算变得更加简单和高效!
总结一下 np.newaxis
的用法:
用法 | 效果 |
---|---|
arr[np.newaxis, :] |
在行方向上增加一个维度,将 (m,) 变成 (1, m) ,变成行向量 |
arr[:, np.newaxis] |
在列方向上增加一个维度,将 (m,) 变成 (m, 1) ,变成列向量 |
arr[..., np.newaxis] |
在最后一个维度后面增加一个维度,将 (m, n) 变成 (m, n, 1) ,以此类推 |
3. Ellipsis
: 省略号的艺术 🎨
Ellipsis
,也就是三个点 ...
,是 NumPy 切片中的一个省略号。它的作用是自动补全切片,让你在处理高维数组时更加轻松自如。
想象一下,你有一个五维数组,你只想对第一维和最后一维进行切片,中间的维度你不想管。如果不用 Ellipsis
,你需要写很多冒号 :
,非常繁琐。但是,有了 Ellipsis
,你就可以用三个点来代替中间的维度,简洁明了!
arr_5d = np.random.rand(2, 3, 4, 5, 6) # 创建一个五维数组
print(arr_5d.shape) # 输出: (2, 3, 4, 5, 6)
# 不使用 Ellipsis,需要写很多冒号
slice1 = arr_5d[0, :, :, :, 2]
print(slice1.shape) # 输出: (3, 4, 5)
# 使用 Ellipsis,简洁明了
slice2 = arr_5d[0, ..., 2]
print(slice2.shape) # 输出: (3, 4, 5)
# 还可以放在中间
slice3 = arr_5d[0, ..., 2, :]
print(slice3.shape) #输出 (3, 4, 6)
在这个例子中,slice1
和 slice2
的效果是一样的,但是 slice2
使用了 Ellipsis
,更加简洁易懂。Ellipsis
会自动补全中间的维度,让你的代码更加优雅。
Ellipsis
的用法总结:
arr[..., i]
:等价于arr[:, :, ..., i]
,表示对最后一个维度进行切片。arr[i, ...]
:等价于arr[i, :, :, ...]
,表示对第一个维度进行切片。arr[i, ..., j]
:表示对第一个维度和最后一个维度进行切片,中间的维度自动补全。
Ellipsis
的注意事项:
- 在一个切片中,只能使用一个
Ellipsis
。 Ellipsis
只能出现在切片中,不能单独使用。
Ellipsis
就像一个智能的省略号,能根据数组的维度自动补全切片,让你的代码更加简洁和可读。 🤓
4. 高级赋值:切片也能变魔术 🪄
切片不仅可以用来读取数组的元素,还可以用来修改数组的元素。通过切片赋值,你可以对数组的某个区域进行整体修改,而无需逐个元素地进行赋值。
arr = np.arange(10)
print(arr) # 输出: [0 1 2 3 4 5 6 7 8 9]
# 使用切片赋值
arr[2:5] = 100 # 将索引 2 到 5 的元素赋值为 100
print(arr) # 输出: [ 0 1 100 100 100 5 6 7 8 9]
# 使用切片赋值,并进行广播
arr[5:] = [200, 201, 202, 203, 204] # 将索引 5 到末尾的元素赋值为新的列表
print(arr) # 输出: [ 0 1 100 100 100 200 201 202 203 204]
对于多维数组,切片赋值的用法也很灵活:
arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr_2d)
# 输出:
# [[1 2 3]
# [4 5 6]
# [7 8 9]]
# 使用切片赋值
arr_2d[:2, 1:] = 0 # 将前两行,从第二列开始的元素赋值为 0
print(arr_2d)
# 输出:
# [[1 0 0]
# [4 0 0]
# [7 8 9]]
#使用newaxis 和 ellipsis 进行赋值
arr_2d[...,np.newaxis] = np.array([1,2,3]).reshape(3,1,1)
print(arr_2d)
# 输出:
# [[[1 1 1]
# [1 1 1]
# [1 1 1]]
# [[2 2 2]
# [2 2 2]
# [2 2 2]]
# [[3 3 3]
# [3 3 3]
# [3 3 3]]]
#使用ellipsis进行赋值
arr_5d = np.random.rand(2, 3, 4, 5, 6)
arr_5d[0,...,0] = 1 #将第一个维度的第一个切片的所有子切片的第一个元素赋值为1
print(arr_5d[0,0,0,0,0]) #输出: 1.0
切片赋值的注意事项:
- 赋值的形状必须与切片的形状兼容。如果形状不兼容,NumPy 会进行广播。
- 切片赋值会修改原始数组,而不是创建新的数组。
切片赋值就像一个魔法画笔,可以让你在数组上自由涂鸦,创造出各种各样的效果。 🎨
5. 案例分析:图像处理小试牛刀 🖼️
理论讲了一大堆,不如来点实际的。我们来用 np.newaxis
和 Ellipsis
对图像进行一些简单的处理。
假设我们有一张彩色图片,它的形状是 (height, width, 3)
,其中 3 表示 RGB 三个通道。
import matplotlib.pyplot as plt
# 模拟一张图片,实际使用时需要读取图片文件
image = np.random.randint(0, 256, size=(100, 150, 3), dtype=np.uint8)
# 显示原始图片
plt.imshow(image)
plt.title("Original Image")
plt.show()
1. 提取红色通道:
red_channel = image[:, :, 0] # 提取红色通道
plt.imshow(red_channel, cmap='gray') # 使用灰度图显示
plt.title("Red Channel")
plt.show()
2. 将红色通道增强:
image[:, :, 0] = image[:, :, 0] * 1.5 # 将红色通道的值乘以 1.5
image[:, :, 0] = np.clip(image[:, :, 0], 0, 255) # 将值限制在 0 到 255 之间
plt.imshow(image)
plt.title("Enhanced Red Channel")
plt.show()
3. 添加一个透明度通道:
alpha_channel = np.ones((image.shape[0], image.shape[1], 1)) * 255 # 创建一个透明度通道,所有像素都完全不透明
alpha_channel = alpha_channel.astype(np.uint8)
image_with_alpha = np.concatenate((image, alpha_channel), axis=2) # 将透明度通道添加到图像中
print(image_with_alpha.shape) # (100, 150, 4)
# 显示带有透明度通道的图像(由于显示限制,这里只是显示其形状)
# plt.imshow(image_with_alpha) # 这会报错,因为imshow需要float类型的数据才能展示透明度
# plt.title("Image with Alpha Channel")
# plt.show()
在这个案例中,我们使用切片提取了红色通道,并对其进行了增强。我们还使用 np.concatenate
和 np.newaxis
添加了一个透明度通道。虽然没有直接使用 np.newaxis
和 Ellipsis
,但是这些操作都基于切片的基础知识,掌握了切片,才能更好地进行图像处理。
6. 总结:切片的艺术,永无止境 🎬
各位观众,今天的 NumPy 切片之旅就到这里告一段落了。我们一起探索了 np.newaxis
和 Ellipsis
的奇妙世界,学习了如何使用切片进行高级赋值。
NumPy 切片是一门艺术,需要不断地练习和实践才能掌握。希望通过今天的讲解,你能够对 NumPy 切片有更深入的理解,并在实际应用中灵活运用。
记住,切片不仅仅是一种技术,更是一种思维方式。它可以让你以更高效、更优雅的方式处理数据,让你的代码更加简洁和可读。
最后,祝大家在 NumPy 的世界里玩得开心,切得漂亮! 🎉