当前位置: 首页 > news >正文

Python 数据分析与机器学习入门 (二):NumPy 核心教程,玩转多维数组

引言:为什么需要 NumPy?

在开始学习数据分析时,一个自然的问题是:“Python 已经有了列表(list),为什么我们还需要一个新的数据结构,比如 NumPy 数组?” 答案在于性能效率。Python 列表非常灵活,可以存储不同类型的元素(如整数、字符串、甚至其他对象),但这种灵活性是以牺牲数值计算性能为代价的。

当处理大规模数值数据,尤其是进行数学运算时,Python 列表的性能瓶颈会变得非常明显。NumPy(Numerical Python 的简称)的出现正是为了解决这个问题。它提供了核心的数据结构——ndarray(N-dimensional array),一个为高性能数值计算而优化的多维数组对象。

基石地位:几乎所有 Python 数据科学生态系统中的高级库,包括 PandasScikit-learn,都是构建在 NumPy 之上的。因此,深刻理解 NumPy 是您数据科学之旅的基石。

性能优势的深层原因:不只是“快”

简单地说 NumPy 比 Python 列表快是远远不够的。理解其背后的原因,能帮助您建立一个关于高性能计算的坚实心智模型。NumPy 的速度优势主要源于以下三个方面:

1. 内存布局 (Contiguous Memory Layout)

  • Python 列表:元素在内存中是分散存储的。列表本身只存储指向各个 Python 对象的指针,而这些对象可以散布在内存的任何地方。当您遍历列表时,CPU 需要在内存中“跳来跳去”地访问数据,这会导致大量的“缓存未命中”(cache misses),严重影响效率。
  • NumPy 数组ndarray 是一个同质(所有元素类型相同)且连续的内存块。数据紧凑地存储在一起,使得 CPU 可以利用其高速缓存(cache locality)一次性加载一大块数据,极大地提升了数据访问速度。

2. 向量化操作 (Vectorization)

  • Python 列表:对列表进行元素级运算(例如,将两个列表的对应元素相加)需要显式的 for 循环。Python 的解释器在执行循环时,每一次迭代都会产生开销。
  • NumPy 数组:通过向量化彻底改变了这一点。当您写下 array_a + array_b 这样的代码时,NumPy 并没有在 Python 层面进行循环。相反,它将这个操作委托给底层用 C 或 Fortran 语言编写的高度优化的、预编译的代码库(如 BLAS 和 LAPACK)来执行。这些底层代码可以直接操作连续的内存块,并利用现代 CPU 的 SIMD(Single Instruction, Multiple Data,单指令多数据流)指令集,实现对整个数组的并行计算。

3. 更少的内存占用

  • 由于 Python 列表中的每个元素都是一个完整的 Python 对象,它包含了类型信息、引用计数等额外的元数据。而 NumPy 数组直接存储原始数据类型(如 32位整数或 64位浮点数),没有这些额外的开销,因此在存储大规模数据时更为节省内存

理解了这三点,您就会明白为什么整个科学计算栈都建立在 NumPy 之上。它不仅仅是“方便”,更是性能的保证。

创建 NumPy 数组

首先,确保导入 NumPy 库,业界通用惯例是将其简写为 np

import numpy as np

从 Python 列表创建

最直接的创建方式是从 Python 列表或元组转换而来。

# 从列表创建一维数组
my_list = [1, 2, 3, 4, 5]
my_array = np.array(my_list)
print(my_array)
# 输出: [1 2 3 4 5]# 从嵌套列表创建二维数组
nested_list = [[1, 2, 3], [4, 5, 6]]
two_d_array = np.array(nested_list)
print(two_d_array)
# 输出:
# [[1 2 3]
#  [4 5 6]]

使用内置函数创建

NumPy 提供了多种便捷的函数来创建特定类型的数组,这在初始化时非常有用。

  • np.arange(): 类似于 Python 的 range() 函数,但返回的是一个 NumPy 数组。

    # 创建一个从 0 到 9 的数组
    arr_arange = np.arange(10)
    print(arr_arange)
    # 输出: [0 1 2 3 4 5 6 7 8 9]
    
  • np.zeros()np.ones(): 创建指定形状且全为 0 或全为 1 的数组。这对于创建占位符数组非常方便。

    # 创建一个 3x4 的全零矩阵
    zeros_arr = np.zeros((3, 4))
    print(zeros_arr)
    # 输出:
    # [[0. 0. 0. 0.]
    #  [0. 0. 0. 0.]
    #  [0. 0. 0. 0.]]# 创建一个 2x3 的全一矩阵
    ones_arr = np.ones((2, 3))
    print(ones_arr)
    # 输出:
    # [[1. 1. 1.]
    #  [1. 1. 1.]]
    
  • np.linspace(): 在指定的间隔内返回均匀间隔的数字。

    # 创建一个从 0 到 10 之间,包含 5 个均匀间隔点的数组
    linspace_arr = np.linspace(0, 10, 5)
    print(linspace_arr)
    # 输出: [ 0.   2.5  5.   7.5 10. ]
    

检查您的数组

了解数组的属性对于后续操作至关重要。NumPy 数组有几个非常有用的属性:

  • .shape: 返回一个元组,表示数组的维度(行数、列数等)。
  • .ndim: 返回数组的维数。
  • .size: 返回数组中元素的总数。
  • .dtype: 返回数组中元素的数据类型。
arr = np.array([[1, 2, 3], [4, 5, 6]])print(f"Shape: {arr.shape}")     # 输出: Shape: (2, 3)
print(f"Dimensions: {arr.ndim}") # 输出: Dimensions: 2
print(f"Size: {arr.size}")       # 输出: Size: 6
print(f"Data type: {arr.dtype}") # 输出: Data type: int64

基础数组运算

元素级算术运算

这是 NumPy 最强大的功能之一。所有标准的算术运算符(+, -, *, /)都可以在数组上进行元素级的操作,无需编写循环

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])# 元素级加法
print(f"a + b = {a + b}") # 输出: a + b = [5 7 9]# 标量乘法 (Broadcasting)
print(f"a * 2 = {a * 2}") # 输出: a * 2 = [2 4 6]# 元素级乘法
print(f"a * b = {a * b}") # 输出: a * b = [ 4 10 18]

通用函数 (ufuncs)

NumPy 的通用函数(ufunc)是对 ndarray 中的数据执行元素级操作的函数。它们是实现向量化操作的核心。常见的 ufuncs 包括数学运算(如 np.sqrt(), np.exp(), np.sin())和聚合函数。

arr = np.array([1, 4, 9])# 计算每个元素的平方根
print(np.sqrt(arr)) # 输出: [1. 2. 3.]

基本索引与切片

NumPy 数组的索引和切片语法与 Python 列表非常相似,这使得它易于上手。

arr = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]# 获取单个元素
print(arr[5]) # 输出: 5# 切片获取子数组
print(arr[2:5]) # 输出: [2 3 4]# 从头开始切片
print(arr[:5]) # 输出: [0 1 2 3 4]# 修改数组中的值
arr[0:3] = 99
print(arr) # 输出: [99 99 99  3  4  5  6  7  8  9]

重要区别: NumPy 数组的切片是原始数组的视图(view),而不是副本(copy)。这意味着,如果您修改了切片,原始数组也会被修改。这是一种为了性能和内存效率而做的设计。如果需要副本,必须显式使用 .copy() 方法。

arr = np.arange(10)
slice_of_arr = arr[0:5]
slice_of_arr[:] = 99 # 修改切片的所有元素print(f"Slice: {slice_of_arr}")        # 输出: Slice: [99 99 99 99 99]
print(f"Original array: {arr}")      # 输出: Original array: [99 99 99 99 99  5  6  7  8  9]

总结与展望

在本篇中,我们不仅学习了 NumPy 的基本操作,更重要的是理解了它作为科学计算基石的深层原因——基于 C 的性能、连续内存布局和向量化操作。这些概念是理解后续所有数据科学工具的关键。

掌握了如何用 NumPy 高效地处理同质化的数值数据后,我们自然会遇到更复杂的需求:如何处理带有不同数据类型、带有标签(列名)的表格数据?这正是 Pandas 的用武之地。在下一篇文章中,我们将学习 Pandas,看看它是如何建立在 NumPy 之上,为我们提供处理真实世界结构化数据的强大武器。

http://www.dtcms.com/a/263502.html

相关文章:

  • 【C语言】知识总结·内存函数
  • CSDN博客大搬家(本地下载markdown合适和图片本地化)
  • I/O I/O基本概念与基本I/O函数 6.30
  • Swift 实现二叉树垂直遍历:LeetCode 314 完整解析与实战示例
  • HTML之常用基础标签
  • Stable Diffusion 项目实战落地:从0到1 掌握ControlNet 第四篇 风格化字体大揭秘:从线稿到涂鸦,ControlNet让文字焕发新生
  • C#索引和范围:简化集合访问的现代特性详解
  • 湖北理元理律师事务所债务解法:从法律技术到生活重建
  • 使用nomachine远程连接ARM设备桌面
  • 【SpringAI】3.结构化输出,初级版
  • 大语言模型 API 进阶指南:DeepSeek 与 Qwen 的深度应用与封装实践
  • C# Winfrom教程(二)----label
  • Unity性能优化-渲染模块(1)-CPU侧(2)-DrawCall优化(2)GPUInstancing
  • StackGAN(堆叠生成对抗网络)
  • Qt Hello World 程序
  • js代码02
  • NVCC编译以及Triton编译介绍
  • 攻防世界-MISC-red_green
  • 【Python使用】嘿马python运维开发全体系教程第2篇:日志管理,Linux概述【附代码文档】
  • 查看CPU支持的指令集和特性
  • 计算机网络中那些常见的路径搜索算法(一)——DFS、BFS、Dijkstra
  • leetcode:693. 交替位二进制数(数学相关算法题,python3解法)
  • 集群【运维】麒麟V10挂载本地yum源
  • 一套非常完整的复古传奇源码测试
  • LLaMA-Factory框架之参数详解
  • 【机器学习】决策树(Decision Tree)
  • 字节跳动 C++ QT PC客户端面试
  • 设计模式-访问者模式
  • Prompt:提示词工程
  • postgresql增量备份系列二 pg_probackup