高级切片与赋值:使用 `np.newaxis` 与 `Ellipsis`

高级切片与赋值:np.newaxisEllipsis 的奇幻漂流之旅 🚀

各位观众老爷,各位靓仔靓女,欢迎来到 NumPy 切片世界的冒险乐园!今天,我们要挑战的是 NumPy 丛林中最神秘、最令人神往的两个精灵:np.newaxisEllipsis。别怕,它们不是什么吃人的怪兽,而是能让你在多维数组里自由穿梭、优雅起舞的魔法棒!✨

准备好了吗?系好安全带,让我们一起踏上这段充满乐趣和惊喜的旅程吧!

1. 切片,切片,切糕也切给你看!🍰

在深入了解 np.newaxisEllipsis 之前,我们先来回顾一下 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.newaxisEllipsis 的奇妙世界了!

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.newaxisarr1 变成一个形状为 (1, 3) 的行向量,或者形状为 (3, 1) 的列向量。然后,NumPy 会自动进行广播,将 arr1_rowarr1_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)

在这个例子中,slice1slice2 的效果是一样的,但是 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.newaxisEllipsis 对图像进行一些简单的处理。

假设我们有一张彩色图片,它的形状是 (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.concatenatenp.newaxis 添加了一个透明度通道。虽然没有直接使用 np.newaxisEllipsis,但是这些操作都基于切片的基础知识,掌握了切片,才能更好地进行图像处理。

6. 总结:切片的艺术,永无止境 🎬

各位观众,今天的 NumPy 切片之旅就到这里告一段落了。我们一起探索了 np.newaxisEllipsis 的奇妙世界,学习了如何使用切片进行高级赋值。

NumPy 切片是一门艺术,需要不断地练习和实践才能掌握。希望通过今天的讲解,你能够对 NumPy 切片有更深入的理解,并在实际应用中灵活运用。

记住,切片不仅仅是一种技术,更是一种思维方式。它可以让你以更高效、更优雅的方式处理数据,让你的代码更加简洁和可读。

最后,祝大家在 NumPy 的世界里玩得开心,切得漂亮! 🎉

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注