📋 本章概述
本章将深入学习NumPy数组的创建方法和基本操作。掌握多种数组创建技术是使用NumPy的基础,这些技能将为后续的数据处理和分析奠定坚实基础。
🎯 学习目标
- 掌握多种数组创建方法
- 理解ndarray对象的属性和特性
- 学会数组的基本操作和变换
- 熟悉数组的复制和视图概念
🚀 2.1 数组创建方法
从Python序列创建
基础创建方法
import numpy as np
# 从列表创建一维数组
list_1d = [1, 2, 3, 4, 5]
arr_1d = np.array(list_1d)
print(f"一维数组: {arr_1d}")
print(f"数据类型: {arr_1d.dtype}")
# 从嵌套列表创建二维数组
list_2d = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
arr_2d = np.array(list_2d)
print(f"二维数组:\n{arr_2d}")
print(f"形状: {arr_2d.shape}")
# 从元组创建数组
tuple_data = (10, 20, 30, 40)
arr_tuple = np.array(tuple_data)
print(f"从元组创建: {arr_tuple}")
指定数据类型
import numpy as np
# 创建时指定数据类型
arr_int = np.array([1, 2, 3, 4], dtype=np.int32)
arr_float = np.array([1, 2, 3, 4], dtype=np.float64)
arr_complex = np.array([1, 2, 3, 4], dtype=np.complex128)
print(f"整数数组: {arr_int}, 类型: {arr_int.dtype}")
print(f"浮点数组: {arr_float}, 类型: {arr_float.dtype}")
print(f"复数数组: {arr_complex}, 类型: {arr_complex.dtype}")
# 类型转换
arr_original = np.array([1.1, 2.9, 3.7, 4.2])
arr_int_converted = arr_original.astype(np.int32)
print(f"原数组: {arr_original}")
print(f"转换后: {arr_int_converted}")
🔧 内置函数创建数组
zeros、ones、empty
import numpy as np
# 创建全零数组
zeros_1d = np.zeros(5)
zeros_2d = np.zeros((3, 4))
zeros_3d = np.zeros((2, 3, 4))
print(f"一维全零: {zeros_1d}")
print(f"二维全零:\n{zeros_2d}")
print(f"三维全零形状: {zeros_3d.shape}")
# 创建全一数组
ones_2d = np.ones((3, 3))
ones_int = np.ones((2, 4), dtype=np.int32)
print(f"全一数组:\n{ones_2d}")
print(f"整数全一:\n{ones_int}")
# 创建未初始化数组(内容随机)
empty_arr = np.empty((2, 3))
print(f"未初始化数组:\n{empty_arr}")
arange和linspace
import numpy as np
# arange: 类似Python的range
arr_range = np.arange(10) # 0到9
arr_range_step = np.arange(1, 10, 2) # 1到9,步长2
arr_float_range = np.arange(0, 1, 0.1) # 浮点数范围
print(f"基础范围: {arr_range}")
print(f"带步长: {arr_range_step}")
print(f"浮点范围: {arr_float_range}")
# linspace: 线性等分
linear_space = np.linspace(0, 10, 11) # 0到10,11个点
linear_space_50 = np.linspace(0, 1, 50) # 0到1,50个点
print(f"线性等分: {linear_space}")
print(f"50个点: {linear_space_50[:10]}...") # 只显示前10个
特殊数组创建
import numpy as np
# 单位矩阵
identity_3x3 = np.eye(3)
identity_4x4 = np.identity(4)
print(f"3x3单位矩阵:\n{identity_3x3}")
print(f"4x4单位矩阵:\n{identity_4x4}")
# 对角矩阵
diag_arr = np.diag([1, 2, 3, 4])
print(f"对角矩阵:\n{diag_arr}")
# 从现有数组创建相同形状的数组
original = np.array([[1, 2], [3, 4]])
zeros_like = np.zeros_like(original)
ones_like = np.ones_like(original)
full_like = np.full_like(original, 7)
print(f"原数组:\n{original}")
print(f"相同形状全零:\n{zeros_like}")
print(f"相同形状全一:\n{ones_like}")
print(f"相同形状填充7:\n{full_like}")
🎲 随机数组创建
import numpy as np
# 设置随机种子(确保结果可重现)
np.random.seed(42)
# 均匀分布随机数
uniform_random = np.random.random((3, 3)) # 0-1之间
uniform_range = np.random.uniform(10, 20, (2, 4)) # 10-20之间
print(f"0-1均匀分布:\n{uniform_random}")
print(f"10-20均匀分布:\n{uniform_range}")
# 正态分布随机数
normal_standard = np.random.randn(3, 3) # 标准正态分布
normal_custom = np.random.normal(100, 15, (2, 5)) # 均值100,标准差15
print(f"标准正态分布:\n{normal_standard}")
print(f"自定义正态分布:\n{normal_custom}")
# 随机整数
random_ints = np.random.randint(1, 10, (3, 3)) # 1-9之间的整数
print(f"随机整数:\n{random_ints}")
# 随机选择
choices = np.random.choice([10, 20, 30, 40, 50], (2, 3))
print(f"随机选择:\n{choices}")
📊 2.2 数组属性详解
基本属性
import numpy as np
# 创建示例数组
arr = np.random.randint(1, 100, (3, 4, 5))
print("=== 数组基本属性 ===")
print(f"数组内容:\n{arr}")
print(f"形状 (shape): {arr.shape}")
print(f"维度数 (ndim): {arr.ndim}")
print(f"元素总数 (size): {arr.size}")
print(f"数据类型 (dtype): {arr.dtype}")
print(f"每个元素字节数 (itemsize): {arr.itemsize}")
print(f"总字节数 (nbytes): {arr.nbytes}")
print(f"数组标志 (flags):\n{arr.flags}")
内存布局
import numpy as np
# C风格(行优先)vs Fortran风格(列优先)
arr_c = np.array([[1, 2, 3], [4, 5, 6]], order='C')
arr_f = np.array([[1, 2, 3], [4, 5, 6]], order='F')
print(f"C风格数组:\n{arr_c}")
print(f"C风格标志: {arr_c.flags.c_contiguous}")
print(f"F风格标志: {arr_c.flags.f_contiguous}")
print(f"\nF风格数组:\n{arr_f}")
print(f"C风格标志: {arr_f.flags.c_contiguous}")
print(f"F风格标志: {arr_f.flags.f_contiguous}")
🔄 2.3 数组基本操作
数组形状操作
import numpy as np
# 创建示例数组
arr = np.arange(12)
print(f"原始数组: {arr}")
# reshape: 改变形状
reshaped_2d = arr.reshape(3, 4)
reshaped_3d = arr.reshape(2, 2, 3)
print(f"重塑为3x4:\n{reshaped_2d}")
print(f"重塑为2x2x3:\n{reshaped_3d}")
# flatten vs ravel
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
flattened = arr_2d.flatten() # 返回副本
raveled = arr_2d.ravel() # 返回视图(如果可能)
print(f"原2D数组:\n{arr_2d}")
print(f"flatten结果: {flattened}")
print(f"ravel结果: {raveled}")
# 修改测试
raveled[0] = 999
print(f"修改ravel后原数组:\n{arr_2d}") # 原数组也会改变
数组转置
import numpy as np
# 二维数组转置
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
print(f"原数组:\n{arr_2d}")
print(f"转置 (.T):\n{arr_2d.T}")
print(f"转置 (.transpose()):\n{arr_2d.transpose()}")
# 多维数组转置
arr_3d = np.arange(24).reshape(2, 3, 4)
print(f"3D数组形状: {arr_3d.shape}")
print(f"转置后形状: {arr_3d.T.shape}")
# 指定轴转置
transposed = arr_3d.transpose(2, 0, 1)
print(f"指定轴转置形状: {transposed.shape}")
数组拼接
import numpy as np
# 一维数组拼接
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
concatenated = np.concatenate([arr1, arr2])
print(f"一维拼接: {concatenated}")
# 二维数组拼接
arr2d_1 = np.array([[1, 2], [3, 4]])
arr2d_2 = np.array([[5, 6], [7, 8]])
# 垂直拼接(沿轴0)
vstack_result = np.vstack([arr2d_1, arr2d_2])
concatenate_axis0 = np.concatenate([arr2d_1, arr2d_2], axis=0)
print(f"垂直拼接:\n{vstack_result}")
print(f"axis=0拼接:\n{concatenate_axis0}")
# 水平拼接(沿轴1)
hstack_result = np.hstack([arr2d_1, arr2d_2])
concatenate_axis1 = np.concatenate([arr2d_1, arr2d_2], axis=1)
print(f"水平拼接:\n{hstack_result}")
print(f"axis=1拼接:\n{concatenate_axis1}")
数组分割
import numpy as np
# 一维数组分割
arr = np.arange(10)
split_result = np.split(arr, [3, 7]) # 在索引3和7处分割
print(f"原数组: {arr}")
print(f"分割结果: {split_result}")
# 二维数组分割
arr_2d = np.arange(16).reshape(4, 4)
print(f"原2D数组:\n{arr_2d}")
# 垂直分割
vsplit_result = np.vsplit(arr_2d, 2)
print(f"垂直分割:")
for i, sub_arr in enumerate(vsplit_result):
print(f" 部分{i+1}:\n{sub_arr}")
# 水平分割
hsplit_result = np.hsplit(arr_2d, 2)
print(f"水平分割:")
for i, sub_arr in enumerate(hsplit_result):
print(f" 部分{i+1}:\n{sub_arr}")
🔍 2.4 数组复制与视图
视图 vs 副本
import numpy as np
# 原始数组
original = np.arange(10)
print(f"原始数组: {original}")
# 视图(共享数据)
view = original.view()
view[0] = 999
print(f"修改视图后原数组: {original}")
# 副本(独立数据)
original = np.arange(10) # 重置
copy = original.copy()
copy[0] = 888
print(f"修改副本后原数组: {original}")
print(f"副本: {copy}")
# 检查是否共享内存
print(f"view与original共享内存: {np.shares_memory(original, view)}")
print(f"copy与original共享内存: {np.shares_memory(original, copy)}")
切片和索引的视图特性
import numpy as np
# 切片创建视图
arr = np.arange(20).reshape(4, 5)
print(f"原数组:\n{arr}")
# 切片是视图
slice_view = arr[1:3, 2:4]
print(f"切片:\n{slice_view}")
# 修改切片影响原数组
slice_view[0, 0] = 999
print(f"修改切片后原数组:\n{arr}")
# 花式索引创建副本
arr = np.arange(20).reshape(4, 5) # 重置
fancy_copy = arr[[1, 3], :]
fancy_copy[0, 0] = 888
print(f"花式索引修改后原数组:\n{arr}") # 原数组不变
print(f"花式索引结果:\n{fancy_copy}")
📈 2.5 实际应用示例
图像数据处理
import numpy as np
import matplotlib.pyplot as plt
# 创建模拟图像数据
height, width = 100, 100
image = np.random.randint(0, 256, (height, width, 3), dtype=np.uint8)
print(f"图像形状: {image.shape}")
print(f"数据类型: {image.dtype}")
print(f"像素值范围: {image.min()} - {image.max()}")
# 图像操作
# 转换为灰度图
gray_image = np.mean(image, axis=2).astype(np.uint8)
# 图像裁剪
cropped = image[25:75, 25:75, :]
# 图像翻转
flipped_horizontal = np.fliplr(image)
flipped_vertical = np.flipud(image)
print(f"灰度图形状: {gray_image.shape}")
print(f"裁剪后形状: {cropped.shape}")
# 可视化
fig, axes = plt.subplots(2, 3, figsize=(12, 8))
axes[0, 0].imshow(image)
axes[0, 0].set_title('原图像')
axes[0, 1].imshow(gray_image, cmap='gray')
axes[0, 1].set_title('灰度图')
axes[0, 2].imshow(cropped)
axes[0, 2].set_title('裁剪图')
axes[1, 0].imshow(flipped_horizontal)
axes[1, 0].set_title('水平翻转')
axes[1, 1].imshow(flipped_vertical)
axes[1, 1].set_title('垂直翻转')
axes[1, 2].axis('off')
for ax in axes.flat:
ax.axis('off')
plt.tight_layout()
plt.show()
数据表格处理
import numpy as np
# 创建模拟学生成绩数据
np.random.seed(42)
num_students = 50
num_subjects = 5
# 学生成绩矩阵 (学生 x 科目)
scores = np.random.normal(75, 15, (num_students, num_subjects))
scores = np.clip(scores, 0, 100) # 限制在0-100范围
# 科目名称
subjects = ['数学', '语文', '英语', '物理', '化学']
print("=== 学生成绩分析 ===")
print(f"数据形状: {scores.shape}")
print(f"总学生数: {num_students}")
print(f"科目数: {num_subjects}")
# 统计分析
print(f"\n=== 各科目统计 ===")
for i, subject in enumerate(subjects):
subject_scores = scores[:, i]
print(f"{subject}:")
print(f" 平均分: {np.mean(subject_scores):.2f}")
print(f" 标准差: {np.std(subject_scores):.2f}")
print(f" 最高分: {np.max(subject_scores):.2f}")
print(f" 最低分: {np.min(subject_scores):.2f}")
# 学生总分和排名
total_scores = np.sum(scores, axis=1)
rankings = np.argsort(total_scores)[::-1] # 降序排列
print(f"\n=== 学生排名 (前10名) ===")
for i in range(10):
student_id = rankings[i]
total = total_scores[student_id]
avg = np.mean(scores[student_id, :])
print(f"第{i+1}名: 学生{student_id+1:02d}, 总分: {total:.1f}, 平均分: {avg:.2f}")
🎯 2.6 性能优化技巧
预分配数组
import numpy as np
import time
# 错误方式:动态增长
def slow_array_building(n):
arr = np.array([])
for i in range(n):
arr = np.append(arr, i)
return arr
# 正确方式:预分配
def fast_array_building(n):
arr = np.zeros(n)
for i in range(n):
arr[i] = i
return arr
# 最佳方式:向量化
def vectorized_array_building(n):
return np.arange(n)
# 性能测试
n = 10000
# 测试慢速方法
start = time.time()
slow_result = slow_array_building(n)
slow_time = time.time() - start
# 测试快速方法
start = time.time()
fast_result = fast_array_building(n)
fast_time = time.time() - start
# 测试向量化方法
start = time.time()
vectorized_result = vectorized_array_building(n)
vectorized_time = time.time() - start
print(f"动态增长耗时: {slow_time:.4f}秒")
print(f"预分配耗时: {fast_time:.4f}秒")
print(f"向量化耗时: {vectorized_time:.6f}秒")
print(f"预分配比动态增长快: {slow_time/fast_time:.1f}倍")
print(f"向量化比预分配快: {fast_time/vectorized_time:.1f}倍")
📝 本章小结
🎯 核心知识点
- 数组创建: 从序列、内置函数、随机数等多种方式
- 数组属性: shape、dtype、size等重要属性
- 基本操作: 形状变换、转置、拼接、分割
- 视图与副本: 理解内存共享和数据独立性
- 性能优化: 预分配和向量化的重要性
💡 最佳实践
- 优先使用NumPy内置函数创建数组
- 预分配数组大小,避免动态增长
- 理解视图和副本的区别
- 使用向量化操作替代循环
- 选择合适的数据类型节省内存
🚀 下一步学习
- 掌握数组索引和切片技术
- 学习布尔索引和花式索引
- 理解数组的高级选择方法
🎯 练习题
- 基础练习: 使用5种不同方法创建相同的数组
- 形状操作: 练习reshape、transpose等操作
- 性能对比: 比较不同数组创建方法的性能
- 实际应用: 处理一个小型数据集,进行基本的统计分析
🎉 恭喜!您已经掌握了NumPy数组的创建和基本操作!
👉 下一章: 第3章:数组索引与切片
本章完成时间: 约45-60分钟
难度等级: ⭐⭐⭐☆☆