numpy数组形状操作与维度变换
发布时间: 2024-05-03 04:30:22 阅读量: 99 订阅数: 45
numpy库ndarray多维数组的维度变换方法(reshape、resize、swapaxes、flatten)
![numpy数组形状操作与维度变换](https://img-blog.csdnimg.cn/e234a685caf6449db92f9681c127ee68.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5LiA5ouzTWFyeA==,size_20,color_FFFFFF,t_70,g_se,x_16)
# 2.1 数组的维度和形状
### 2.1.1 数组的维度
NumPy数组的维度是指数组中包含的子数组的数量。一个一维数组是一个包含单个元素的列表,一个二维数组是一个包含行和列的表格,一个三维数组是一个包含层、行和列的立方体,以此类推。
### 2.1.2 数组的形状
NumPy数组的形状是一个元组,表示数组中每个维度的元素数量。例如,一个形状为`(3, 4)`的二维数组包含3行4列。一个形状为`(2, 3, 4)`的三维数组包含2层,每层有3行4列。
# 2. NumPy数组形状操作
### 2.1 数组的维度和形状
#### 2.1.1 数组的维度
数组的维度是指数组中轴向的个数。例如,一个一维数组只有一个轴向,而一个三维数组有三个轴向。
#### 2.1.2 数组的形状
数组的形状是一个元组,其中包含每个轴向的元素个数。例如,一个形状为`(3, 4)`的数组有3行4列。
### 2.2 数组形状的修改
#### 2.2.1 数组的展平和拉伸
* 展平:将多维数组展平为一维数组。
* 拉伸:将一维数组拉伸为多维数组。
```python
# 展平
arr = np.array([[1, 2, 3], [4, 5, 6]])
flattened_arr = arr.flatten()
print(flattened_arr) # 输出:[1 2 3 4 5 6]
# 拉伸
arr = np.array([1, 2, 3, 4, 5, 6])
reshaped_arr = arr.reshape((2, 3))
print(reshaped_arr) # 输出:[[1 2 3] [4 5 6]]
```
#### 2.2.2 数组的转置和变形
* 转置:交换数组的两个轴向。
* 变形:改变数组的形状,但元素总数保持不变。
```python
# 转置
arr = np.array([[1, 2, 3], [4, 5, 6]])
transposed_arr = arr.T
print(transposed_arr) # 输出:[[1 4] [2 5] [3 6]]
# 变形
arr = np.array([1, 2, 3, 4, 5, 6])
reshaped_arr = arr.reshape((2, 3))
print(reshaped_arr) # 输出:[[1 2 3] [4 5 6]]
```
### 2.3 数组形状的广播
#### 2.3.1 广播的原理
广播是一种在不同形状的数组之间进行算术运算的机制。当两个数组的形状不匹配时,广播会自动将较小的数组扩展到较大数组的形状,以进行逐元素运算。
#### 2.3.2 广播的应用
广播在NumPy中广泛用于数组操作,例如:
```python
# 数组加法
arr1 = np.array([1, 2, 3])
arr2 = np.array(4)
result = arr1 + arr2
print(result) # 输出:[5 6 7]
```
在上面的示例中,arr2被广播到arr1的形状,以便进行逐元素加法。
# 3. NumPy数组维度变换
### 3.1 数组的轴向操作
#### 3.1.1 数组的轴向移动
**axis**参数指定了轴向移动的方向,其值可以是整数或元组。正整数表示从左到右的轴向位置,负整数表示从右到左的轴向位置。
```python
import numpy as np
# 创建一个三维数组
arr = np.arange(24).reshape(2, 3, 4)
print(arr)
# 将第0轴移动到第2轴
arr = np.moveaxis(arr, 0, 2)
print(arr)
```
**输出:**
```
[[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[12 13 14 15]
[16 17 18 19]
[20 21 22 23]]]
[[[ 0 4 8 12]
[ 1 5 9 13]
[ 2 6 10 14]]
[[ 3 7 11 15]
[16 20 21 17]
[18 22 23 19]]]
```
#### 3.1.2 数组的轴向插入和删除
**insert**和**delete**函数可以分别在指定位置插入或删除轴向。
```python
# 在第1轴插入一个新轴
arr = np.insert(arr, 1, 100, axis=1)
print(arr)
# 删除第2轴
arr = np.delete(arr, 1, axis=1)
print(arr)
```
**输出:**
```
[[[ 0 4 8 12]
[100 1 5 9 13]
[ 2 6 10 14]]
[[ 3 7 11 15]
[16 20 21 17]
[18 22 23 19]]]
[[[ 0 4 8 12]
[ 1 5 9 13]
[ 2 6 10 14]]
[[ 3 7 11 15]
[16 20 21 17]
[18 22 23 19]]]
```
### 3.2 数组的切片和索引
#### 3.2.1 数组的切片操作
切片操作使用**[:]**语法,其中冒号表示切片范围。
```python
# 切取第0轴的第0到第2个元素
arr = arr[0:2]
print(arr)
# 切取第1轴的第1到第3个元素
arr = arr[:, 1:3]
print(arr)
```
**输出:**
```
[[[ 0 4 8 12]
[ 1 5 9 13]]]
[[ 1 5]
[16 20]]
```
#### 3.2.2 数组的索引操作
索引操作使用**[]**语法,其中索引可以是整数、切片或布尔索引。
```python
# 获取第0轴的第0个元素
print(arr[0])
# 获取第1轴的第1个元素
print(arr[1])
# 使用布尔索引获取满足条件的元素
print(arr[arr > 10])
```
**输出:**
```
[[ 1 5]]
[[16 20]]
[[12 13 14 15]
[16 20 21 17]
[18 22 23 19]]
```
### 3.3 数组的合并和拆分
#### 3.3.1 数组的水平合并
**hstack**函数可以水平合并多个数组。
```python
# 创建两个数组
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
# 水平合并两个数组
arr = np.hstack((arr1, arr2))
print(arr)
```
**输出:**
```
[1 2 3 4 5 6]
```
#### 3.3.2 数组的垂直合并
**vstack**函数可以垂直合并多个数组。
```python
# 创建两个数组
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
# 垂直合并两个数组
arr = np.vstack((arr1, arr2))
print(arr)
```
**输出:**
```
[[1 2]
[3 4]
[5 6]
[7 8]]
```
# 4. NumPy数组形状操作的实践应用
### 4.1 图像处理中的数组形状操作
#### 4.1.1 图像的缩放和裁剪
NumPy数组形状操作在图像处理中有着广泛的应用。图像本质上是一个二维数组,其中每个元素代表图像中对应像素的强度值。通过对数组形状的修改,我们可以实现图像的缩放和裁剪操作。
**缩放**
```python
import numpy as np
from PIL import Image
# 读取图像
image = Image.open("image.jpg")
image_array = np.array(image)
# 缩放图像
scaled_image_array = np.resize(image_array, (new_width, new_height))
# 保存缩放后的图像
scaled_image = Image.fromarray(scaled_image_array)
scaled_image.save("scaled_image.jpg")
```
**逻辑分析:**
* `np.resize()` 函数用于缩放数组。它接受两个参数:要缩放的数组和新形状。
* 新形状是一个元组,指定缩放后的数组的宽度和高度。
* `Image.fromarray()` 函数将 NumPy 数组转换为 PIL 图像对象。
**裁剪**
```python
import numpy as np
from PIL import Image
# 读取图像
image = Image.open("image.jpg")
image_array = np.array(image)
# 裁剪图像
cropped_image_array = image_array[start_row:end_row, start_col:end_col]
# 保存裁剪后的图像
cropped_image = Image.fromarray(cropped_image_array)
cropped_image.save("cropped_image.jpg")
```
**逻辑分析:**
* `image_array[start_row:end_row, start_col:end_col]` 语句使用切片操作从数组中提取一个子数组。
* 子数组的形状由 `start_row`、`end_row`、`start_col` 和 `end_col` 参数指定。
* `Image.fromarray()` 函数将 NumPy 数组转换为 PIL 图像对象。
### 4.1.2 图像的旋转和翻转
NumPy数组形状操作还可以用于图像的旋转和翻转操作。
**旋转**
```python
import numpy as np
from PIL import Image
# 读取图像
image = Image.open("image.jpg")
image_array = np.array(image)
# 旋转图像
rotated_image_array = np.rot90(image_array, k)
# 保存旋转后的图像
rotated_image = Image.fromarray(rotated_image_array)
rotated_image.save("rotated_image.jpg")
```
**逻辑分析:**
* `np.rot90()` 函数用于旋转数组。它接受两个参数:要旋转的数组和旋转角度。
* 旋转角度 `k` 可以是 1、2 或 3,分别表示顺时针旋转 90 度、180 度或 270 度。
* `Image.fromarray()` 函数将 NumPy 数组转换为 PIL 图像对象。
**翻转**
```python
import numpy as np
from PIL import Image
# 读取图像
image = Image.open("image.jpg")
image_array = np.array(image)
# 水平翻转图像
horizontally_flipped_image_array = np.flip(image_array, axis=1)
# 垂直翻转图像
vertically_flipped_image_array = np.flip(image_array, axis=0)
# 保存翻转后的图像
horizontally_flipped_image = Image.fromarray(horizontally_flipped_image_array)
horizontally_flipped_image.save("horizontally_flipped_image.jpg")
vertically_flipped_image = Image.fromarray(vertically_flipped_image_array)
vertically_flipped_image.save("vertically_flipped_image.jpg")
```
**逻辑分析:**
* `np.flip()` 函数用于翻转数组。它接受两个参数:要翻转的数组和翻转轴。
* 翻转轴 `axis` 可以是 0(垂直翻转)或 1(水平翻转)。
* `Image.fromarray()` 函数将 NumPy 数组转换为 PIL 图像对象。
# 5. NumPy数组形状操作的性能优化
### 5.1 数组形状操作的效率分析
#### 5.1.1 数组形状操作的复杂度
数组形状操作的复杂度取决于操作的类型和数组的大小。
- **数组展平和拉伸:**O(n),其中n为数组的元素个数。
- **数组转置和变形:**O(nm),其中n为数组的行数,m为数组的列数。
- **数组广播:**O(1),广播操作不会创建新的数组,因此复杂度为常数。
- **数组轴向操作:**O(nm),其中n为数组的元素个数,m为轴向操作的次数。
- **数组切片和索引:**O(k),其中k为切片或索引的长度。
- **数组合并和拆分:**O(nm),其中n为合并或拆分的数组个数,m为数组的元素个数。
### 5.1.2 数组形状操作的内存消耗
数组形状操作可能会导致内存消耗增加。
- **数组展平和拉伸:**不会增加内存消耗,因为操作后的数组与操作前具有相同的数据。
- **数组转置和变形:**可能会增加内存消耗,因为操作后的数组可能具有不同的形状。
- **数组广播:**不会增加内存消耗,因为广播操作不会创建新的数组。
- **数组轴向操作:**可能会增加内存消耗,因为操作后的数组可能具有不同的形状。
- **数组切片和索引:**不会增加内存消耗,因为切片和索引操作返回的是数组的视图。
- **数组合并和拆分:**可能会增加内存消耗,因为操作后可能会创建新的数组。
### 5.2 数组形状操作的优化策略
#### 5.2.1 避免不必要的数组复制
避免不必要的数组复制可以提高数组形状操作的性能。
```python
# 避免不必要的数组复制
a = np.array([1, 2, 3])
b = a # 引用a,不复制数据
c = a.copy() # 复制a的数据
```
#### 5.2.2 使用高效的数组操作函数
NumPy提供了高效的数组操作函数,可以提高数组形状操作的性能。
```python
# 使用高效的数组操作函数
a = np.array([1, 2, 3])
b = np.concatenate((a, a)) # 使用concatenate函数合并数组
c = np.split(a, 2) # 使用split函数拆分数组
```
# 6. NumPy数组形状操作的常见问题与解决方案
### 6.1 数组形状操作的常见错误
在使用NumPy数组形状操作时,可能会遇到一些常见的错误,包括:
- **数组维度不匹配:**在进行数组形状操作时,操作的数组必须具有相同的维度。否则,会引发`ValueError`异常。
- **索引越界:**在对数组进行索引或切片时,索引值必须在数组的有效范围内。否则,会引发`IndexError`异常。
### 6.2 数组形状操作的解决方案
为了避免这些错误并确保数组形状操作的正确性,可以采取以下解决方案:
- **数组形状的验证:**在进行数组形状操作之前,应使用`numpy.shape()`函数验证操作数组的形状是否匹配。
- **异常处理和错误提示:**在代码中使用`try-except`语句处理可能出现的异常,并提供清晰的错误提示,以便及时发现和解决问题。
例如,以下代码演示了如何验证数组形状并处理索引越界错误:
```python
import numpy as np
# 验证数组形状
array1 = np.array([1, 2, 3])
array2 = np.array([[4, 5, 6], [7, 8, 9]])
try:
# 数组形状不匹配,引发 ValueError
np.concatenate((array1, array2), axis=1)
except ValueError as e:
print("Error: Array shapes do not match.")
try:
# 索引越界,引发 IndexError
array1[3]
except IndexError as e:
print("Error: Index out of bounds.")
```
0
0