跳到主要内容

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 中创建数组的各种方法:

  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 - 函数式创建

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

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

练习​

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