NumPy 数组基础
在这一章中,我们将深入了解 NumPy 数组(ndarray)的基础知识,包括数组的创建、属性、数据类型等核心概念。
什么是 NumPy 数组
NumPy 数组(ndarray)是 NumPy 库的核心数据结构,它是一个多维数组对象,具有以下特点:
- 同质性:数组中的所有元素必须是相同的数据类型
- 固定大小:数组创建后大小固定,不能动态改变
- 高效性:使用连续的内存存储,操作速度快
- 多维性:支持任意维度的数组
数组的创建方法
1. 从 Python 列表创建
python
import numpy as np
# 一维数组
array_1d = np.array([1, 2, 3, 4, 5])
print("一维数组:", array_1d)
print("数据类型:", array_1d.dtype)
print("形状:", array_1d.shape)
print()
# 二维数组
array_2d = np.array([[1, 2, 3], [4, 5, 6]])
print("二维数组:")
print(array_2d)
print("数据类型:", array_2d.dtype)
print("形状:", array_2d.shape)
print()
# 三维数组
array_3d = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
print("三维数组:")
print(array_3d)
print("数据类型:", array_3d.dtype)
print("形状:", array_3d.shape)输出结果:
一维数组: [1 2 3 4 5]
数据类型: int32
形状: (5,)
二维数组:
[[1 2 3]
[4 5 6]]
数据类型: int32
形状: (2, 3)
三维数组:
[[[1 2]
[3 4]]
[[5 6]
[7 8]]]
数据类型: int32
形状: (2, 2, 2)2. 使用内置函数创建
创建特殊数组
python
import numpy as np
# 全零数组
zeros_1d = np.zeros(5)
zeros_2d = np.zeros((3, 4))
print("一维全零数组:", zeros_1d)
print("二维全零数组:")
print(zeros_2d)
print()
# 全一数组
ones_1d = np.ones(4)
ones_2d = np.ones((2, 3))
print("一维全一数组:", ones_1d)
print("二维全一数组:")
print(ones_2d)
print()
# 单位矩阵
identity = np.eye(3)
print("3x3单位矩阵:")
print(identity)
print()
# 填充特定值的数组
full_array = np.full((2, 3), 7)
print("填充数字7的数组:")
print(full_array)输出结果:
一维全零数组: [0. 0. 0. 0. 0.]
二维全零数组:
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
一维全一数组: [1. 1. 1. 1.]
二维全一数组:
[[1. 1. 1.]
[1. 1. 1.]]
3x3单位矩阵:
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
填充数字7的数组:
[[7 7 7]
[7 7 7]]创建数值序列
python
import numpy as np
# 等差数列
arange_array = np.arange(10) # 0到9
print("0到9:", arange_array)
arange_step = np.arange(2, 10, 2) # 从2开始,到10结束,步长为2
print("2到10,步长2:", arange_step)
arange_float = np.arange(0, 1, 0.1) # 浮点数步长
print("0到1,步长0.1:", arange_float)
print()
# 等间距数列
linspace_array = np.linspace(0, 10, 5) # 0到10之间等分5个点
print("0到10等分5点:", linspace_array)
linspace_exclude = np.linspace(0, 10, 5, endpoint=False) # 不包含终点
print("0到10等分5点(不含终点):", linspace_exclude)
print()
# 对数空间
logspace_array = np.logspace(0, 2, 5) # 10^0 到 10^2 之间等分5个点
print("对数空间:", logspace_array)输出结果:
0到9: [0 1 2 3 4 5 6 7 8 9]
2到10,步长2: [2 4 6 8]
0到1,步长0.1: [0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]
0到10等分5点: [ 0. 2.5 5. 7.5 10. ]
0到10等分5点(不含终点): [0. 2. 4. 6. 8.]
对数空间: [ 1. 3.16227766 10. 31.6227766 100. ]创建随机数组
python
import numpy as np
# 设置随机种子以获得可重复的结果
np.random.seed(42)
# 0到1之间的随机数
random_uniform = np.random.random(5)
print("0-1随机数:", random_uniform)
# 指定范围的随机整数
random_int = np.random.randint(1, 10, 5)
print("1-10随机整数:", random_int)
# 正态分布随机数
random_normal = np.random.normal(0, 1, 5) # 均值0,标准差1
print("正态分布随机数:", random_normal)
# 多维随机数组
random_2d = np.random.random((3, 3))
print("3x3随机数组:")
print(random_2d)输出结果:
0-1随机数: [0.37454012 0.95071431 0.73199394 0.59865848 0.15601864]
1-10随机整数: [6 4 8 8 3]
正态分布随机数: [ 1.76405235 0.40015721 0.97873798 2.2408932 1.86755799]
3x3随机数组:
[[0.95008842 0.4879643 0.22479665]
[0.19806286 0.76053071 0.16911084]
[0.08833981 0.68535982 0.95339335]]数组的基本属性
python
import numpy as np
# 创建一个示例数组
array = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
print("示例数组:")
print(array)
print()
# 基本属性
print(f"数组维度数量 (ndim): {array.ndim}")
print(f"数组形状 (shape): {array.shape}")
print(f"数组元素总数 (size): {array.size}")
print(f"数据类型 (dtype): {array.dtype}")
print(f"每个元素字节数 (itemsize): {array.itemsize}")
print(f"数组总字节数 (nbytes): {array.nbytes}")
print(f"数组内存布局 (flags): ")
print(array.flags)输出结果:
示例数组:
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
数组维度数量 (ndim): 2
数组形状 (shape): (3, 4)
数组元素总数 (size): 12
数据类型 (dtype): int32
每个元素字节数 (itemsize): 4
数组总字节数 (nbytes): 48
数组内存布局 (flags):
C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : True
WRITEABLE : True
ALIGNED : True
WRITEBACKIFCOPY : False
UPDATEIFCOPY : False数据类型详解
NumPy 支持的数据类型
python
import numpy as np
# 整数类型
int8_array = np.array([1, 2, 3], dtype=np.int8) # 8位整数
int16_array = np.array([1, 2, 3], dtype=np.int16) # 16位整数
int32_array = np.array([1, 2, 3], dtype=np.int32) # 32位整数
int64_array = np.array([1, 2, 3], dtype=np.int64) # 64位整数
print("int8:", int8_array.dtype, "字节数:", int8_array.itemsize)
print("int16:", int16_array.dtype, "字节数:", int16_array.itemsize)
print("int32:", int32_array.dtype, "字节数:", int32_array.itemsize)
print("int64:", int64_array.dtype, "字节数:", int64_array.itemsize)
print()
# 浮点数类型
float16_array = np.array([1.0, 2.0, 3.0], dtype=np.float16) # 半精度
float32_array = np.array([1.0, 2.0, 3.0], dtype=np.float32) # 单精度
float64_array = np.array([1.0, 2.0, 3.0], dtype=np.float64) # 双精度
print("float16:", float16_array.dtype, "字节数:", float16_array.itemsize)
print("float32:", float32_array.dtype, "字节数:", float32_array.itemsize)
print("float64:", float64_array.dtype, "字节数:", float64_array.itemsize)
print()
# 布尔类型
bool_array = np.array([True, False, True], dtype=np.bool_)
print("bool:", bool_array.dtype, "字节数:", bool_array.itemsize)
print("布尔数组:", bool_array)
print()
# 复数类型
complex_array = np.array([1+2j, 3+4j], dtype=np.complex128)
print("complex128:", complex_array.dtype, "字节数:", complex_array.itemsize)
print("复数数组:", complex_array)输出结果:
int8: int8 字节数: 1
int16: int16 字节数: 2
int32: int32 字节数: 4
int64: int64 字节数: 8
float16: float16 字节数: 2
float32: float32 字节数: 4
float64: float64 字节数: 8
bool: bool 字节数: 1
布尔数组: [ True False True]
complex128: complex128 字节数: 16
复数数组: [1.+2.j 3.+4.j]数据类型转换
python
import numpy as np
# 创建整数数组
int_array = np.array([1, 2, 3, 4, 5])
print("原始整数数组:", int_array, "类型:", int_array.dtype)
# 转换为浮点数
float_array = int_array.astype(np.float64)
print("转换为浮点数:", float_array, "类型:", float_array.dtype)
# 转换为字符串
string_array = int_array.astype(np.str_)
print("转换为字符串:", string_array, "类型:", string_array.dtype)
# 浮点数转换为整数(会截断小数部分)
float_data = np.array([1.7, 2.3, 3.9])
int_from_float = float_data.astype(np.int32)
print("浮点数:", float_data)
print("转换为整数:", int_from_float)
# 字符串转换为数字
string_numbers = np.array(['1', '2', '3'])
numbers = string_numbers.astype(np.int32)
print("字符串数组:", string_numbers)
print("转换为数字:", numbers)输出结果:
原始整数数组: [1 2 3 4 5] 类型: int32
转换为浮点数: [1. 2. 3. 4. 5.] 类型: float64
转换为字符串: ['1' '2' '3' '4' '5'] 类型: <U11
浮点数: [1.7 2.3 3.9]
转换为整数: [1 2 3]
字符串数组: ['1' '2' '3']
转换为数字: [1 2 3]数组的形状操作
改变数组形状
python
import numpy as np
# 创建一维数组
array_1d = np.arange(12)
print("原始一维数组:", array_1d)
print("形状:", array_1d.shape)
print()
# 重塑为二维数组
array_2d = array_1d.reshape(3, 4)
print("重塑为3x4:")
print(array_2d)
print("形状:", array_2d.shape)
print()
# 重塑为三维数组
array_3d = array_1d.reshape(2, 2, 3)
print("重塑为2x2x3:")
print(array_3d)
print("形状:", array_3d.shape)
print()
# 自动计算维度(使用-1)
auto_reshape = array_1d.reshape(4, -1) # 自动计算列数
print("自动计算维度(4, -1):")
print(auto_reshape)
print("形状:", auto_reshape.shape)输出结果:
原始一维数组: [ 0 1 2 3 4 5 6 7 8 9 10 11]
形状: (12,)
重塑为3x4:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
形状: (3, 4)
重塑为2x2x3:
[[[ 0 1 2]
[ 3 4 5]]
[[ 6 7 8]
[ 9 10 11]]]
形状: (2, 2, 3)
自动计算维度(4, -1):
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
形状: (4, 3)展平数组
python
import numpy as np
# 创建二维数组
array_2d = np.array([[1, 2, 3], [4, 5, 6]])
print("原始二维数组:")
print(array_2d)
print()
# 展平为一维数组
flattened = array_2d.flatten()
print("flatten():", flattened)
print("原数组是否改变:", array_2d)
print()
# 展平为一维数组(返回视图)
raveled = array_2d.ravel()
print("ravel():", raveled)
print("修改ravel结果:")
raveled[0] = 999
print("ravel结果:", raveled)
print("原数组:", array_2d) # 原数组也会改变输出结果:
原始二维数组:
[[1 2 3]
[4 5 6]]
flatten(): [1 2 3 4 5 6]
原数组是否改变: [[1 2 3]
[4 5 6]]
ravel(): [1 2 3 4 5 6]
修改ravel结果:
ravel结果: [999 2 3 4 5 6]
原数组: [[999 2 3]
[ 4 5 6]]数组的拼接和分割
数组拼接
python
import numpy as np
# 创建两个数组
array1 = np.array([1, 2, 3])
array2 = np.array([4, 5, 6])
print("数组1:", array1)
print("数组2:", array2)
print()
# 水平拼接(沿着列方向)
horizontal = np.hstack([array1, array2])
print("水平拼接:", horizontal)
# 垂直拼接(沿着行方向)
vertical = np.vstack([array1, array2])
print("垂直拼接:")
print(vertical)
print()
# 二维数组拼接
array2d_1 = np.array([[1, 2], [3, 4]])
array2d_2 = np.array([[5, 6], [7, 8]])
print("二维数组1:")
print(array2d_1)
print("二维数组2:")
print(array2d_2)
print()
# 沿轴0拼接(行方向)
concat_axis0 = np.concatenate([array2d_1, array2d_2], axis=0)
print("沿轴0拼接:")
print(concat_axis0)
# 沿轴1拼接(列方向)
concat_axis1 = np.concatenate([array2d_1, array2d_2], axis=1)
print("沿轴1拼接:")
print(concat_axis1)输出结果:
数组1: [1 2 3]
数组2: [4 5 6]
水平拼接: [1 2 3 4 5 6]
垂直拼接:
[[1 2 3]
[4 5 6]]
二维数组1:
[[1 2]
[3 4]]
二维数组2:
[[5 6]
[7 8]]
沿轴0拼接:
[[1 2]
[3 4]
[5 6]
[7 8]]
沿轴1拼接:
[[1 2 5 6]
[3 4 7 8]]数组分割
python
import numpy as np
# 创建数组
array = np.arange(12)
print("原始数组:", array)
# 等分分割
split_equal = np.split(array, 3) # 分成3个相等的部分
print("等分为3部分:")
for i, part in enumerate(split_equal):
print(f" 部分{i+1}: {part}")
print()
# 指定分割点
split_points = np.split(array, [3, 7]) # 在索引3和7处分割
print("在索引3和7处分割:")
for i, part in enumerate(split_points):
print(f" 部分{i+1}: {part}")
print()
# 二维数组分割
array_2d = np.arange(12).reshape(3, 4)
print("二维数组:")
print(array_2d)
print()
# 水平分割
hsplit_result = np.hsplit(array_2d, 2) # 分成2列
print("水平分割:")
for i, part in enumerate(hsplit_result):
print(f" 部分{i+1}:")
print(part)
print()
# 垂直分割
vsplit_result = np.vsplit(array_2d, 3) # 分成3行
print("垂直分割:")
for i, part in enumerate(vsplit_result):
print(f" 部分{i+1}: {part}")输出结果:
原始数组: [ 0 1 2 3 4 5 6 7 8 9 10 11]
等分为3部分:
部分1: [0 1 2 3]
部分2: [4 5 6 7]
部分3: [ 8 9 10 11]
在索引3和7处分割:
部分1: [0 1 2]
部分2: [3 4 5 6]
部分3: [ 7 8 9 10 11]
二维数组:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
水平分割:
部分1:
[[0 1]
[4 5]
[8 9]]
部分2:
[[ 2 3]
[ 6 7]
[10 11]]
垂直分割:
部分1: [[0 1 2 3]]
部分2: [[4 5 6 7]]
部分3: [[ 8 9 10 11]]数组的复制
视图 vs 副本
python
import numpy as np
# 创建原始数组
original = np.array([1, 2, 3, 4, 5])
print("原始数组:", original)
print()
# 创建视图(共享内存)
view = original.view()
print("视图:", view)
print("是否共享内存:", np.shares_memory(original, view))
# 修改视图
view[0] = 999
print("修改视图后:")
print("原始数组:", original)
print("视图:", view)
print()
# 创建副本(独立内存)
copy = original.copy()
print("副本:", copy)
print("是否共享内存:", np.shares_memory(original, copy))
# 修改副本
copy[1] = 888
print("修改副本后:")
print("原始数组:", original)
print("副本:", copy)输出结果:
原始数组: [1 2 3 4 5]
视图: [1 2 3 4 5]
是否共享内存: True
修改视图后:
原始数组: [999 2 3 4 5]
视图: [999 2 3 4 5]
副本: [999 2 3 4 5]
是否共享内存: False
修改副本后:
原始数组: [999 2 3 4 5]
副本: [999 888 3 4 5]实际应用示例
示例1:图像数据处理
python
import numpy as np
# 模拟一个简单的灰度图像(8x8像素)
image = np.random.randint(0, 256, (8, 8), dtype=np.uint8)
print("原始图像数据:")
print(image)
print(f"图像形状: {image.shape}")
print(f"数据类型: {image.dtype}")
print(f"像素值范围: {image.min()} - {image.max()}")
print()
# 图像处理操作
# 1. 增加亮度
brighter = np.clip(image + 50, 0, 255).astype(np.uint8)
print("增加亮度后的像素值范围:", f"{brighter.min()} - {brighter.max()}")
# 2. 图像二值化
threshold = 128
binary = (image > threshold).astype(np.uint8) * 255
print(f"二值化(阈值{threshold})后的唯一值:", np.unique(binary))
# 3. 计算图像统计信息
print(f"平均像素值: {np.mean(image):.2f}")
print(f"标准差: {np.std(image):.2f}")
print(f"中位数: {np.median(image)}")示例2:科学计算
python
import numpy as np
# 创建实验数据
time = np.linspace(0, 10, 100) # 时间从0到10秒,100个数据点
frequency = 2 # 频率2Hz
amplitude = 5 # 振幅5
noise_level = 0.5 # 噪声水平
# 生成带噪声的正弦波信号
signal = amplitude * np.sin(2 * np.pi * frequency * time)
noise = np.random.normal(0, noise_level, len(time))
noisy_signal = signal + noise
print(f"时间范围: {time[0]:.2f} - {time[-1]:.2f} 秒")
print(f"数据点数: {len(time)}")
print(f"信号统计:")
print(f" 平均值: {np.mean(noisy_signal):.3f}")
print(f" 标准差: {np.std(noisy_signal):.3f}")
print(f" 最大值: {np.max(noisy_signal):.3f}")
print(f" 最小值: {np.min(noisy_signal):.3f}")
# 简单的信号处理
# 计算移动平均来平滑信号
window_size = 5
smoothed_signal = np.convolve(noisy_signal, np.ones(window_size)/window_size, mode='valid')
print(f"\n平滑后的信号长度: {len(smoothed_signal)}")
print(f"平滑后的标准差: {np.std(smoothed_signal):.3f}")性能比较
python
import numpy as np
import time
# 比较 NumPy 数组和 Python 列表的性能
size = 1000000
# Python 列表操作
start_time = time.time()
python_list = list(range(size))
python_result = [x * 2 for x in python_list]
list_time = time.time() - start_time
# NumPy 数组操作
start_time = time.time()
numpy_array = np.arange(size)
numpy_result = numpy_array * 2
numpy_time = time.time() - start_time
print(f"数据大小: {size:,} 个元素")
print(f"Python 列表时间: {list_time:.4f} 秒")
print(f"NumPy 数组时间: {numpy_time:.4f} 秒")
print(f"NumPy 速度提升: {list_time/numpy_time:.1f}x")
# 内存使用比较
import sys
list_memory = sys.getsizeof(python_list) + sum(sys.getsizeof(x) for x in python_list[:100]) # 估算
numpy_memory = numpy_array.nbytes
print(f"\nPython 列表内存使用(估算): {list_memory:,} 字节")
print(f"NumPy 数组内存使用: {numpy_memory:,} 字节")
print(f"内存效率提升: {list_memory/numpy_memory:.1f}x")本章小结
在这一章中,我们深入学习了:
- NumPy 数组的基本概念和特点
- 多种数组创建方法
- 数组的基本属性和信息获取
- 数据类型系统和类型转换
- 数组形状操作(重塑、展平)
- 数组的拼接和分割
- 视图和副本的区别
- 实际应用示例
- 性能优势分析
下一步
在下一章中,我们将学习 NumPy 数组的索引和切片操作,这是数据操作的基础技能。
练习题
- 创建一个 5x5 的数组,填充从 1 到 25 的数字,然后将其重塑为 25x1 的数组
- 创建两个 3x3 的随机数组,将它们水平和垂直拼接
- 创建一个包含 100 个随机数的数组,将其分割为 10 个相等的部分
- 比较不同数据类型(int8, int32, float32, float64)的内存使用情况
- 创建一个模拟温度数据的数组,实现数据的标准化(均值为0,标准差为1)