跳到主要内容

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.asarraynp.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 创建用指定值填充的数组,比 zerosones 更通用:

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}")

性能提示emptyzeros 稍快,因为不需要初始化内存。当你确定会立即覆盖数组内容时,可以使用 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 中创建数组的各种方法:

  1. 从现有数据创建array, asarray, copy - 将 Python 序列转换为数组
  2. 从形状或值创建zeros, ones, full, empty - 创建占位数组
  3. 从数值范围创建arange, linspace, logspace, geomspace - 创建序列
  4. 特殊矩阵eye, identity, diag - 创建单位矩阵和对角矩阵
  5. 网格坐标meshgrid - 创建坐标网格
  6. 随机数组random 模块的各种分布
  7. 从函数创建fromfunction, fromiter - 函数式创建

选择合适的创建方法取决于你的具体需求:

  • 需要精确控制值时,使用 arraylinspace
  • 需要初始化占位时,使用 zerosones
  • 需要随机数据时,使用 random 模块
  • 需要网格坐标时,使用 meshgrid

练习

  1. 创建一个 10x10 的矩阵,对角线为 1,反对角线也为 1
  2. 使用 linspace 创建一个包含 100 个点的数组,范围从 -5 到 5,然后计算每个点的 sigmoid 函数值
  3. 创建一个三维数组表示 RGB 图像,初始化为纯白色(所有通道值为 255)
  4. 使用 meshgrid 创建一个坐标网格,计算每个点到原点的距离
  5. 创建一个长度为 1000 的随机游走序列(每步随机 ±1)