Numpy(一)基础知识


概述

文章内容

主要讲解六个知识点:

  1. NumPy的作用
  2. Numpy与Python序列区别
  3. 数据大小与处理速度的重要性,并用一个例子讲解
  4. 向量化,两个例子讲解
  5. 向量化的优点
  6. 广播,连个例子讲解

1 NumPy基本信息

Numpy的作用

NumPy 是 Python 中用于科学计算的基础包。它是一个 Python 库,提供了「多维数组对象」、各种「派生对象」(如掩码数组和矩阵)以及用于「数组快速操作」的一系列常规方法,包括数学运算、逻辑运算、形状操作、排序、选择、输入/输出、离散傅立叶变换、基本线性代数、基本统计操作、随机模拟等。

NumPy 包的核心是 ndarray 对象。它封装了「同类型数据」的 「n 维数组」,并且许多操作都在「编译代码」中执行以提高性能。

Numpy与Python序列区别

  1. 固定大小: NumPy 数组在创建时具有固定大小,与 Python 列表(可以动态增长)不同。改变 ndarray 的大小会创建一个新的数组并删除原始数组。
  2. 相同类型: NumPy 数组中的元素必须全部是相同的数据类型,因此它们在内存中的大小也相同。例外情况是:可以有包含(Python,包括 NumPy)对象的数组,从而允许数组中有不同大小的元素。
  3. 高效的大数据操作: NumPy 数组便于对大量数据进行高级数学和其他类型的操作。通常,这样的操作比使用 Python 内置序列执行更为高效,且代码更少。
  4. 使用广泛: 越来越多的基于Python 包的科学和数学正在使用 NumPy 数组;尽管这些包通常支持 Python 序列输入,但它们在处理之前会将此类输入转换为 NumPy 数组,并且它们经常输出 NumPy 数组。换句话说,为了有效使用当今许多(甚至可能是大多数)基于 Python 的科学/数学软件,仅仅知道如何使用 Python 的内置序列类型是不够的——人们还需要知道如何使用 NumPy 数组。

大小与速度重要

在科学计算中,关于序列大小和速度的点特别重要。

序列乘积

大小与速度重要

将一个一维序列中的每个元素与另一个相同长度序列中的相应元素相乘。

  1. python列表效率低: 如果 a 和 b 每个都包含数百万数字,将为 Python 中循环的低效付出代价。
c = []
for i in range(len(a)):
    c.append(a[i]*b[i])
  1. c语言速度快
for (i = 0; i < rows; i++) {
  c[i] = a[i]*b[i];
}
  1. NumPy的两全其美: 当涉及 ndarray 时,「逐元素操作」是“默认模式”,但逐元素操作是由「预先编译」的 C 代码快速执行的。NumPy 以接近 C 语言的速度完成操作,但同时保持了 Python 的代码简洁性。 「向量化」和「广播」是NumPy 的两个特性,是其强大功能的基石。
c = a * b

2 NumPy快的原因

向量化(vectorization)

指使用「数组表达式」来替代「循环」来实现数组操作,从而提高代码的执行效率。代码中没有循环、索引等操作,在代码内部使用「预先编译的 C 代码」来提高效率。

「向量化」属性:

  1. 无需显式迭代:向量化操作不需要显式编写循环来迭代数组的每个元素。

  2. 元素级并行性:向量化操作在概念上是并行的,即数组的每个元素可以同时进行计算。

  3. 表达式操作:向量化通常通过表达式来实现,而不是通过编写具体的迭代逻辑。

  4. 数组间操作:向量化操作涉及数组与数组之间的运算,这些运算遵循元素级的操作规则。

  5. 广播兼容性:在向量化操作中,不同形状的数组可以通过广播机制进行兼容操作。

  6. 内存连续性:向量化操作通常在内存中连续存储数据,这有助于提高缓存效率和计算速度。

  7. 编译器优化:向量化代码可以被编译器优化,以利用现代CPU的向量指令集。

  8. 减少函数调用:向量化减少了函数调用的次数,因为操作是直接在数组上进行的,而不是在数组的每个元素上单独调用函数。

「向量化」是为了解决以下问题:

  • 提高性能:循环在 Python 中通常比较慢,因为 Python 是一种解释型语言,并且循环中的每次迭代都涉及到函数调用的开销。向量化操作可以显著减少这种开销。
  • 简化代码:向量化可以使代码更加简洁和易于理解,因为它减少了循环和索引的使用。
  • 利用硬件优势:现代计算机硬件对并行操作有良好的支持,向量化操作可以更好地利用这些优势。

如果没有「向量化」,可能会有以下影响:

  • 性能下降:代码执行速度会显著降低,因为需要使用低效的循环来实现相同的操作。
  • 代码复杂性增加:需要手动编写循环来处理数组的每个元素,这会增加代码的复杂性,降低可读性。
  • 可维护性降低:循环中的 bug 更难被发现和修复,而且循环代码的修改和维护也更加困难。

向量化的优点

  1. 向量化代码更加简洁,更易于阅读。

  2. 代码行数更少通常意味着更少的错误。

  3. 代码更接近标准数学符号(通常使正确编码数学结构更容易)。

  4. 向量化结果在更多的“Pythonic”代码中。如果没有向量化,我们的代码将充满低效且难。

批量烹饪

向量化(vectorization)

加入你是一位厨师,需要为餐厅准备晚餐。有两种选择来准备食物:

  1. 非向量化方法:你一个一个地烹饪菜品。例如,你先为第一位顾客烹饪一份意大利面,然后是第二位顾客的牛排,接着是第三位顾客的沙拉,以此类推。每道菜都需要你单独处理,这就像是在编程中使用循环来处理数组的每个元素。

  2. 向量化方法:你使用批量烹饪技术。你同时准备多份意大利面、牛排和沙拉,使用统一的烹饪流程和时间,同时为多位顾客服务。这就像是在编程中使用向量化操作,通过一次操作处理整个数组,而不是单独处理每个元素。

计算数组平方

向量化(vectorization)

一个包含多个数值的数组,计算这个数组中每个元素的平方。可以使用 Python 的 Numpy 库来实现这一操作:

  1. 非向量化方法(使用循环):
    在这个例子中,使用了一个 for 循环来遍历列表中的每个元素,并计算它们的平方,然后将结果存储在一个新的列表中。
numbers = [1, 2, 3, 4, 5] squared = [] for number in numbers:     squared.append(number ** 2)
  1. 向量化方法(使用 Numpy):
    在这个例子中,首先将列表转换为 Numpy 数组,然后直接使用指数运算符 ** 来计算数组中每个元素的平方。Numpy 会自动将这个操作应用到数组的每个元素上,而不需要编写显式的循环。

向量化方法不仅代码更简洁,而且由于 Numpy 的内部优化,执行速度也更快。这展示了向量化在编程中提高效率和简化代码的强大能力。

import numpy as np 
numbers = np.array([1, 2, 3, 4, 5]) 
squared = numbers ** 2

广播(Broadcasting)

广播(Broadcasting)允许 Numpy 用「不同大小的数组」进行算术运算。当无法直接对两个数组进行算术运算时,Numpy 会根据一定的规则「自动扩展(或“广播”)」较小的数组,使之与较大的数组具有「相同的形状」,然后进行元素级的运算。

主要属性:

  1. 维度扩展性:在广播中,较小的数组能够在缺少的维度上自动扩展,以匹配较大数组的维度。

  2. 形状兼容性:两个数组的形状在对应的维度上必须兼容。这意味着两个数组在对应维度上要么具有相同的大小,要么其中一个数组在该维度上的大小为1。

  3. 尾部对齐:广播机制从数组形状的尾部开始对齐,即从最后一个维度开始匹配形状。

  4. 自动填充:在不匹配的维度上,较小的数组会被自动填充为1,以实现与较大数组的兼容。

  5. 无需物理复制:广播不涉及物理上复制数据以创建新数组,而是在原有数据的基础上进行逻辑扩展。

「广播」是为了解决以下问题:

  • 简化数组运算:允许不同形状的数组进行运算,而不需要用户手动扩展数组。
  • 提高代码的灵活性:用户可以对不同大小的数组进行操作,而不必担心它们的维度是否匹配。

没有「广播」机制,可能会有以下影响:

  • 代码复杂性增加:用户需要手动编写代码来扩展数组,以确保它们的形状匹配。
  • 性能下降:缺少广播可能导致更多的内存使用和计算时间,因为需要复制数据来创建扩展的数组。
  • 可读性降低:手动处理数组形状的代码通常更难理解和维护。

蛋糕模具

广播(Broadcasting)

想象你在做蛋糕,你有一套「模具」,每个模具都是相同的形状和大小。你准备了一大碗「蛋糕糊」,现在需要将这个糊均匀地分配到每个模具中。在这个例子中,「蛋糕糊」就像是一个「数值」,「模具集合」就像是一个「数组」。你不需要改变蛋糕糊的量(数值)来适应每个模具,而是将相同的量“广播”到所有模具中。这样,每个模具都得到了相同量的蛋糕糊,而且过程非常高效。

年龄加一

广播(Broadcasting)

假设有一个数组表示了一组人的年龄,现在想要给每个人的年龄加上1,表示过了一年,每个人都老了一岁。

import numpy as np
ages = np.array([25, 30, 35, 40])
# 我们想要给每个人的年龄加1
new_ages = ages + 1

在这个例子中,1被“广播”到ages数组的每个元素上。这意味着NumPy自动将1扩展到与ages数组相同的形状,然后执行逐元素的加法。这里的1就像蛋糕糊,而ages数组就像模具集合。通过广播,1被有效地“复制”并加到了ages数组的每个元素上,从而实现了高效的数组操作。


文章作者: Hkini
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Hkini !
评论
  目录