NumPy 数组创建
数组创建是使用 NumPy 的第一步。掌握各种创建数组的方法,能够让你在不同场景下选择最合适的方式,提高代码效率和可读性。本章将详细介绍 NumPy 中创建数组的各种方法,包括从现有数据创建、使用内置函数创建、以及从数值范围创建等。
创建方法概览
NumPy 提供了丰富的数组创建函数,根据数据来源可以分为几类:
| 类别 | 典型函数 | 适用场景 |
|---|---|---|
| 从现有数据 | array, asarray, copy | 转换 Python 列表、元组等 |
| 从形状或值 | zeros, ones, full, empty | 创建特定形状的占位数组 |
| 数值范围 | arange, linspace, logspace | 创建等差或等比序列 |
| 单位和对角 | eye, identity, diag | 创建矩阵相关数组 |
| 随机数组 | random.rand, random.randn | 模拟数据、初始化参数 |
| 网格坐标 | meshgrid | 创建坐标网格用于绘图 |
从现有数据创建
array 函数详解
np.array 是最基础的创建函数,它将 Python 序列(列表、元组等)转换为 NumPy 数组。理解它的工作原理是掌握 NumPy 的第一步。
import numpy as np
# 从列表创建一维数组
arr1 = np.array([1, 2, 3, 4, 5])
print(f"一维数组: {arr1}")
print(f"数据类型: {arr1.dtype}") # 默认根据数据推断
# 从嵌套列表创建二维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(f"\n二维数组形状: {arr2.shape}")
print(f"维度数: {arr2.ndim}")
关键参数详解:
import numpy as np
# dtype: 指定数据类型
arr_float = np.array([1, 2, 3], dtype=np.float32)
print(f"float32 数组: {arr_float}, dtype: {arr_float.dtype}")
# ndmin: 强制最小维度
arr_2d = np.array([1, 2, 3], ndmin=2)
print(f"\n强制二维数组形状: {arr_2d.shape}") # (1, 3)
# copy: 是否复制数据(默认 True)
original = [1, 2, 3]
arr_copy = np.array(original, copy=True)
arr_no_copy = np.array(original, copy=False)
original[0] = 100
print(f"\n修改原列表后:")
print(f" copy=True 的数组: {arr_copy}") # 不变
print(f" copy=False 的数组: {arr_no_copy}") # 可能变化(取决于内存布局)
实际应用:将 Python 列表数据转换为 NumPy 数组以便进行向量化计算。
import numpy as np
# 实际场景:将收集的数据转换为数组进行分析
temperatures = [23.5, 24.1, 22.8, 25.0, 24.5, 23.9, 25.2]
temp_array = np.array(temperatures)
print(f"平均温度: {temp_array.mean():.1f}°C")
print(f"温度范围: {temp_array.min():.1f}°C ~ {temp_array.max():.1f}°C")
print(f"标准差: {temp_array.std():.2f}°C")
asarray 与 array 的区别
np.asarray 与 np.array 功能相似,但在处理已经是 NumPy 数组的输入时有关键区别:
import numpy as np
original = np.array([1, 2, 3])
# np.array 默认创建副本
copy_arr = np.array(original)
print(f"array 创建的是副本: {copy_arr is not original}") # True
# np.asarray 不会创建副本(如果输入已是数组)
no_copy_arr = np.asarray(original)
print(f"asarray 不创建副本: {no_copy_arr is original}") # True
# 修改原数组
original[0] = 100
print(f"\n修改原数组后:")
print(f" array 创建的副本: {copy_arr}") # [1, 2, 3] 不变
print(f" asarray 的结果: {no_copy_arr}") # [100, 2, 3] 变了
使用建议:
- 当你不确定输入是否已经是 NumPy 数组时,使用
asarray更高效 - 当你需要确保获得独立副本时,使用
array或显式调用copy()
copy 函数
np.copy 创建数组的深拷贝,完全独立于原数组:
import numpy as np
original = np.array([[1, 2, 3], [4, 5, 6]])
copied = np.copy(original)
# 修改副本不影响原数组
copied[0, 0] = 100
print(f"原数组:\n{original}")
print(f"\n副本:\n{copied}")
从形状或值创建
zeros - 创建全零数组
np.zeros 创建指定形状的全零数组,常用于初始化存储空间:
import numpy as np
# 一维全零数组
zeros_1d = np.zeros(5)
print(f"一维全零: {zeros_1d}")
# 二维全零数组(矩阵)
zeros_2d = np.zeros((3, 4))
print(f"\n3x4 全零矩阵:\n{zeros_2d}")
# 指定数据类型
zeros_int = np.zeros((2, 2), dtype=np.int32)
print(f"\n整数型全零矩阵:\n{zeros_int}")
实际应用:初始化累加器或结果数组。
import numpy as np
# 实际场景:累加多组实验数据
n_experiments = 5
n_measurements = 100
results = np.zeros(n_measurements) # 初始化累加器
for i in range(n_experiments):
# 模拟每次实验的数据
data = np.random.rand(n_measurements)
results += data # 累加
average = results / n_experiments # 计算平均值
print(f"平均值的范围: [{average.min():.3f}, {average.max():.3f}]")
ones - 创建全一数组
np.ones 创建指定形状的全一数组,常用于创建掩码或归一化因子:
import numpy as np
# 基本用法
ones_arr = np.ones((3, 3))
print(f"3x3 全一矩阵:\n{ones_arr}")
# 指定类型
ones_float = np.ones(5, dtype=np.float32)
print(f"\nfloat32 全一数组: {ones_float}")
实际应用:创建归一化权重。
import numpy as np
# 实际场景:创建等权重进行加权平均
n_samples = 100
data = np.random.rand(n_samples, 3) # 3 列数据
# 等权重平均
weights = np.ones(n_samples) / n_samples
weighted_avg = np.dot(weights, data)
print(f"等权重平均: {weighted_avg}")
full - 创建填充指定值的数组
np.full 创建用指定值填充的数组,比 zeros 和 ones 更通用:
import numpy as np
# 用特定数值填充
full_num = np.full((3, 3), 7)
print(f"填充 7 的矩阵:\n{full_num}")
# 用布尔值填充
full_bool = np.full(5, True)
print(f"\n布尔数组: {full_bool}")
# 用复数填充
full_complex = np.full(3, 1+2j)
print(f"\n复数数组: {full_complex}")
empty - 创建未初始化数组
np.empty 创建未初始化的数组,内容不确定(可能是内存中的随机值):
import numpy as np
# 未初始化数组
empty_arr = np.empty((3, 3))
print(f"未初始化数组:\n{empty_arr}")
print(f"注意:数组内容是随机的内存值,不保证是零")
# 通常紧接着赋值操作
empty_arr[:] = 0 # 显式赋值
print(f"\n赋值后:\n{empty_arr}")
性能提示:empty 比 zeros 稍快,因为不需要初始化内存。当你确定会立即覆盖数组内容时,可以使用 empty。
*_like 函数系列
NumPy 提供了一组 *_like 函数,根据现有数组的形状和类型创建新数组:
import numpy as np
# 参考数组
reference = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32)
print(f"参考数组形状: {reference.shape}, 类型: {reference.dtype}")
# 创建相同形状的各种数组
zeros_like = np.zeros_like(reference)
ones_like = np.ones_like(reference)
full_like = np.full_like(reference, 99)
empty_like = np.empty_like(reference)
print(f"\nzeros_like 形状: {zeros_like.shape}, 类型: {zeros_like.dtype}")
print(f"ones_like 形状: {ones_like.shape}, 类型: {ones_like.dtype}")
实际应用:处理不确定形状的数据时非常有用。
import numpy as np
def normalize(data):
"""将数据归一化到 [0, 1] 范围"""
# 使用 *_like 确保输出形状和类型与输入一致
result = np.empty_like(data, dtype=np.float64)
min_val = data.min()
max_val = data.max()
result[:] = (data - min_val) / (max_val - min_val)
return result
# 测试
data = np.random.rand(100, 50) * 100
normalized = normalize(data)
print(f"归一化后范围: [{normalized.min():.4f}, {normalized.max():.4f}]")
从数值范围创建
arange - 等差数列
np.arange 类似 Python 的 range,但返回 NumPy 数组:
import numpy as np
# 基本用法
arr1 = np.arange(5)
print(f"arange(5): {arr1}") # [0, 1, 2, 3, 4]
# 指定起止
arr2 = np.arange(2, 8)
print(f"arange(2, 8): {arr2}") # [2, 3, 4, 5, 6, 7]
# 指定步长
arr3 = np.arange(0, 10, 2)
print(f"arange(0, 10, 2): {arr3}") # [0, 2, 4, 6, 8]
# 负步长
arr4 = np.arange(10, 0, -2)
print(f"arange(10, 0, -2): {arr4}") # [10, 8, 6, 4, 2]
# 浮点步长
arr5 = np.arange(0, 1, 0.2)
print(f"arange(0, 1, 0.2): {arr5}")
重要警告:使用浮点步长时,arange 可能因为浮点精度问题导致结果不符合预期:
import numpy as np
# 浮点精度问题示例
arr = np.arange(0, 1, 0.1)
print(f"arange(0, 1, 0.1) 的长度: {len(arr)}")
print(f"结果: {arr}")
# 注意:可能包含或不包含接近 1 的值
# 解决方案:使用 linspace
arr_linspace = np.linspace(0, 1, 11)
print(f"\nlinspace(0, 1, 11) 的长度: {len(arr_linspace)}")
print(f"结果: {arr_linspace}")
# 精确包含 0 和 1,共 11 个点
linspace - 线性等分
np.linspace 在指定区间内创建均匀分布的点,是 arange 的精确替代方案:
import numpy as np
# 基本用法:在 [0, 1] 区间创建 5 个点
arr = np.linspace(0, 1, 5)
print(f"linspace(0, 1, 5): {arr}")
# 结果: [0. , 0.25, 0.5 , 0.75, 1. ]
# endpoint=False: 不包含终点
arr_no_end = np.linspace(0, 1, 5, endpoint=False)
print(f"\nendpoint=False: {arr_no_end}")
# 结果: [0. , 0.2 , 0.4 , 0.6 , 0.8]
# retstep=True: 返回 (数组, 步长)
arr, step = np.linspace(0, 10, 5, retstep=True)
print(f"\n数组: {arr}")
print(f"步长: {step}")
实际应用:创建函数采样点。
import numpy as np
# 在 [-π, π] 区间采样 100 个点
x = np.linspace(-np.pi, np.pi, 100)
y = np.sin(x)
# 计算一些统计量
print(f"sin(x) 在 [-π, π] 上的范围: [{y.min():.4f}, {y.max():.4f}]")
print(f"采样点数量: {len(x)}")
print(f"采样间距: {x[1] - x[0]:.4f} 弧度")
logspace 和 geomspace - 对数尺度
当需要在对数尺度上创建均匀分布的点时,使用这些函数:
import numpy as np
# logspace: 在 10^start 到 10^stop 之间创建点
arr_log = np.logspace(0, 3, 5) # 10^0 到 10^3
print(f"logspace(0, 3, 5): {arr_log}")
# [1, 10^0.75, 10^1.5, 10^2.25, 1000]
# 指定底数
arr_log2 = np.logspace(0, 3, 5, base=2) # 2^0 到 2^3
print(f"\nlogspace(0, 3, 5, base=2): {arr_log2}")
# geomspace: 几何等比序列(直接指定起止值)
arr_geom = np.geomspace(1, 1000, 4)
print(f"\ngeomspace(1, 1000, 4): {arr_geom}")
# [1, 10, 100, 1000]
实际应用:创建对数坐标轴或指数增长序列。
import numpy as np
# 实际场景:频率响应分析
frequencies = np.logspace(0, 6, 100) # 1 Hz 到 1 MHz
print(f"频率范围: {frequencies[0]:.0f} Hz 到 {frequencies[-1]/1e6:.0f} MHz")
print(f"采样点数: {len(frequencies)}")
单位矩阵和对角矩阵
eye 和 identity - 单位矩阵
import numpy as np
# eye: 创建单位矩阵(可非方阵)
eye_3x3 = np.eye(3)
print(f"3x3 单位矩阵:\n{eye_3x3}")
# 非方阵
eye_3x5 = np.eye(3, 5)
print(f"\n3x5 单位矩阵:\n{eye_3x5}")
# k 参数: 对角线偏移
eye_k1 = np.eye(3, k=1) # 主对角线向上偏移 1
print(f"\n偏移对角线 (k=1):\n{eye_k1}")
# identity: 只能创建方阵
identity_4 = np.identity(4)
print(f"\n4x4 单位矩阵:\n{identity_4}")
diag - 对角矩阵
np.diag 有两种用途:从一维数组创建对角矩阵,或从二维数组提取对角线:
import numpy as np
# 从一维数组创建对角矩阵
diag_matrix = np.diag([1, 2, 3, 4])
print(f"对角矩阵:\n{diag_matrix}")
# 指定偏移
diag_k1 = np.diag([1, 2, 3], k=1)
print(f"\n上对角线 (k=1):\n{diag_k1}")
# 从矩阵提取对角线
matrix = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
diagonal = np.diag(matrix)
print(f"\n提取主对角线: {diagonal}")
# 提取偏移对角线
diag_upper = np.diag(matrix, k=1)
print(f"提取上对角线: {diag_upper}")
网格坐标
meshgrid - 坐标网格
np.meshgrid 从坐标向量创建坐标矩阵,常用于绘制三维图形和函数可视化:
import numpy as np
# 创建一维坐标
x = np.array([0, 1, 2])
y = np.array([0, 1])
# 生成网格
X, Y = np.meshgrid(x, y)
print(f"X (x 坐标矩阵):\n{X}")
print(f"\nY (y 坐标矩阵):\n{Y}")
# 每个网格点的坐标
print(f"\n网格点坐标:")
for i in range(len(y)):
for j in range(len(x)):
print(f" ({X[i,j]}, {Y[i,j]})")
实际应用:计算二维函数值。
import numpy as np
# 创建网格
x = np.linspace(-2, 2, 5)
y = np.linspace(-2, 2, 5)
X, Y = np.meshgrid(x, y)
# 计算函数值 z = x^2 + y^2
Z = X**2 + Y**2
print("函数 z = x² + y² 的网格值:")
for i in range(len(y)):
row = [f"{Z[i,j]:.1f}" for j in range(len(x))]
print(f" y={y[i]:.1f}: {row}")
索引顺序 indexing 参数
import numpy as np
x = np.array([0, 1, 2])
y = np.array([0, 1])
# 'xy' 模式(默认):笛卡尔坐标,输出形状为 (len(y), len(x))
X_xy, Y_xy = np.meshgrid(x, y, indexing='xy')
print(f"'xy' 模式形状: X={X_xy.shape}, Y={Y_xy.shape}")
# 'ij' 模式:矩阵索引,输出形状为 (len(x), len(y))
X_ij, Y_ij = np.meshgrid(x, y, indexing='ij')
print(f"'ij' 模式形状: X={X_ij.shape}, Y={Y_ij.shape}")
随机数组
NumPy 的随机模块提供了丰富的随机数生成功能。
均匀分布
import numpy as np
# 推荐使用新的 Generator API
rng = np.random.default_rng(seed=42)
# [0, 1) 均匀分布
uniform = rng.random(5)
print(f"[0, 1) 均匀分布: {uniform}")
# 指定范围的均匀分布
uniform_range = rng.uniform(0, 10, 5)
print(f"[0, 10) 均匀分布: {uniform_range}")
# 随机整数
integers = rng.integers(0, 10, size=5)
print(f"[0, 10) 随机整数: {integers}")
# 包含端点的随机整数
integers_endpoint = rng.integers(0, 5, size=10, endpoint=True)
print(f"[0, 5] 包含端点的随机整数: {integers_endpoint}")
正态分布
import numpy as np
rng = np.random.default_rng(seed=42)
# 标准正态分布(均值0,标准差1)
standard_normal = rng.standard_normal(5)
print(f"标准正态分布: {standard_normal}")
# 一般正态分布
normal = rng.normal(loc=50, scale=10, size=5) # 均值50,标准差10
print(f"N(50, 10) 分布: {normal}")
# 验证大样本统计特性
large_sample = rng.normal(loc=100, scale=15, size=10000)
print(f"\n大样本验证 (N=10000):")
print(f" 样本均值: {large_sample.mean():.2f} (理论值: 100)")
print(f" 样本标准差: {large_sample.std():.2f} (理论值: 15)")
从数组中采样
import numpy as np
rng = np.random.default_rng(seed=42)
population = np.array(['A', 'B', 'C', 'D', 'E'])
# 随机选择(可重复)
choice = rng.choice(population, size=5)
print(f"随机选择(可重复): {choice}")
# 随机选择(不重复)
choice_unique = rng.choice(population, size=3, replace=False)
print(f"随机选择(不重复): {choice_unique}")
# 带权重的选择
weights = [0.4, 0.3, 0.15, 0.1, 0.05]
choice_weighted = rng.choice(population, size=10, p=weights)
print(f"带权重选择: {choice_weighted}")
从函数创建
fromfunction - 函数式创建
np.fromfunction 通过对每个坐标执行函数来创建数组:
import numpy as np
# 创建乘法表
def multiply(x, y):
return x * y
mult_table = np.fromfunction(multiply, (5, 5), dtype=int)
print(f"5x5 乘法表:\n{mult_table}")
# 创建距离矩阵
def distance(x, y):
return np.sqrt(x**2 + y**2)
coords = np.linspace(-2, 2, 5)
X, Y = np.meshgrid(coords, coords)
dist_matrix = np.fromfunction(lambda i, j: np.sqrt((i-2)**2 + (j-2)**2), (5, 5))
print(f"\n距离矩阵(从中心):\n{dist_matrix}")
fromiter - 从迭代器创建
import numpy as np
# 从生成器创建数组
def fibonacci(n):
"""生成前 n 个斐波那契数"""
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
fib_array = np.fromiter(fibonacci(10), dtype=int)
print(f"斐波那契数列前10项: {fib_array}")
# 从迭代器创建
squares = (x**2 for x in range(10))
squares_array = np.fromiter(squares, dtype=int)
print(f"平方数: {squares_array}")
结构化数组初始化
当需要创建包含多种数据类型的数组时:
import numpy as np
# 定义结构化数据类型
dt = np.dtype([
('name', 'U20'), # Unicode 字符串,最大长度 20
('age', 'i4'), # 32 位整数
('score', 'f8') # 64 位浮点数
])
# 创建空结构化数组
students = np.zeros(3, dtype=dt)
students['name'] = ['Alice', 'Bob', 'Charlie']
students['age'] = [20, 22, 21]
students['score'] = [89.5, 92.0, 85.5]
print("结构化数组:")
print(students)
# 从列表创建
students2 = np.array([
('Alice', 20, 89.5),
('Bob', 22, 92.0)
], dtype=dt)
print(f"\n从列表创建:\n{students2}")
创建函数对比总结
| 函数 | 输出内容 | 是否初始化 | 典型用途 |
|---|---|---|---|
zeros | 全零 | 是 | 初始化累加器 |
ones | 全一 | 是 | 创建掩码、权重 |
full | 指定值 | 是 | 初始化特定值 |
empty | 未定义 | 否 | 性能优化,立即覆盖 |
eye | 单位矩阵 | 是 | 线性代数运算 |
arange | 等差序列 | 是 | 整数序列 |
linspace | 精确等分 | 是 | 浮点数序列 |
logspace | 对数序列 | 是 | 对数尺度分析 |
实战案例
案例1:创建实验数据矩阵
import numpy as np
rng = np.random.default_rng(seed=42)
# 实验参数
n_subjects = 50
n_conditions = 4
n_measurements = 100
# 创建三维数据数组:被试 × 条件 × 测量
data = np.zeros((n_subjects, n_conditions, n_measurements))
# 填充模拟数据
for subject in range(n_subjects):
baseline = rng.normal(100, 10) # 每个被试有不同的基线
for condition in range(n_conditions):
# 不同条件有不同效果
effect = (condition + 1) * 5
noise = rng.normal(0, 2, n_measurements)
data[subject, condition, :] = baseline + effect + noise
# 分析
print(f"数据形状: {data.shape}")
print(f"条件均值: {data.mean(axis=(0, 2))}")
案例2:创建图像处理用的网格
import numpy as np
# 图像参数
height, width = 100, 150
# 创建坐标网格
x = np.arange(width)
y = np.arange(height)
X, Y = np.meshgrid(x, y)
# 创建以图像中心为原点的坐标
cx, cy = width // 2, height // 2
X_centered = X - cx
Y_centered = Y - cy
# 创建圆形掩码
radius = 30
circular_mask = X_centered**2 + Y_centered**2 <= radius**2
print(f"圆形掩码覆盖的像素数: {circular_mask.sum()}")
print(f"理论面积: {np.pi * radius**2:.1f}")
案例3:创建时间序列索引
import numpy as np
# 创建日期序列(使用 datetime64)
start_date = np.datetime64('2024-01-01')
end_date = np.datetime64('2024-12-31')
# 每日数据
days = np.arange(start_date, end_date + 1, dtype='datetime64[D]')
print(f"2024年天数: {len(days)}")
# 每周数据
weeks = np.arange(start_date, end_date + 1, dtype='datetime64[W]')
print(f"周数: {len(weeks)}")
# 每月数据
months = np.arange('2024-01', '2025-01', dtype='datetime64[M]')
print(f"月份数据:\n{months}")
小结
本章详细介绍了 NumPy 中创建数组的各种方法:
- 从现有数据创建:
array,asarray,copy- 将 Python 序列转换为数组 - 从形状或值创建:
zeros,ones,full,empty- 创建占位数组 - 从数值范围创建:
arange,linspace,logspace,geomspace- 创建序列 - 特殊矩阵:
eye,identity,diag- 创建单位矩阵和对角矩阵 - 网格坐标:
meshgrid- 创建坐标网格 - 随机数组:
random模块的各种分布 - 从函数创建:
fromfunction,fromiter- 函数式创建
选择合适的创建方法取决于你的具体需求:
- 需要精确控制值时,使用
array或linspace - 需要初始化占位时,使用
zeros或ones - 需要随机数据时,使用
random模块 - 需要网格坐标时,使用
meshgrid
练习
- 创建一个 10x10 的矩阵,对角线为 1,反对角线也为 1
- 使用
linspace创建一个包含 100 个点的数组,范围从 -5 到 5,然后计算每个点的 sigmoid 函数值 - 创建一个三维数组表示 RGB 图像,初始化为纯白色(所有通道值为 255)
- 使用
meshgrid创建一个坐标网格,计算每个点到原点的距离 - 创建一个长度为 1000 的随机游走序列(每步随机 ±1)