📋 本章概述

本章将深入学习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}倍")

📝 本章小结

🎯 核心知识点

  1. 数组创建: 从序列、内置函数、随机数等多种方式
  2. 数组属性: shape、dtype、size等重要属性
  3. 基本操作: 形状变换、转置、拼接、分割
  4. 视图与副本: 理解内存共享和数据独立性
  5. 性能优化: 预分配和向量化的重要性

💡 最佳实践

  • 优先使用NumPy内置函数创建数组
  • 预分配数组大小,避免动态增长
  • 理解视图和副本的区别
  • 使用向量化操作替代循环
  • 选择合适的数据类型节省内存

🚀 下一步学习

  • 掌握数组索引和切片技术
  • 学习布尔索引和花式索引
  • 理解数组的高级选择方法

🎯 练习题

  1. 基础练习: 使用5种不同方法创建相同的数组
  2. 形状操作: 练习reshape、transpose等操作
  3. 性能对比: 比较不同数组创建方法的性能
  4. 实际应用: 处理一个小型数据集,进行基本的统计分析

🎉 恭喜!您已经掌握了NumPy数组的创建和基本操作!

👉 下一章: 第3章:数组索引与切片


本章完成时间: 约45-60分钟
难度等级: ⭐⭐⭐☆☆