numpy 官网文章翻译

官网地址

什么是 numpy

numpy 是 Python 的一个科学计算包。提供了多种array对象、衍生对象(masked arrays 和 matrices)、及其对其日常快速操作,包括数学、逻辑、形状操作、分类、选择、I/O、离散傅里叶(discrete Fourier transforms)、基本线性代数、基本数据操作、随机墨迹等。
numpy 包的核心是ndarray,他分装了n-维 array 对象(每个维度的数据类型一致),这使得各种操作都在编译好的代码中执行,从而提高了性能。

NumPyarray对象 与 Python 标准序列区别:

  • NumPy 对象在创建时有一个固定的大小,而 Python 的 list 没有。改变 ndarray 的大小会删除原来对象,创建一个新的对象
  • NumPy所有数据类型是一致的,这样在内存中占用的大小才是一致的。例外:可以有对象数组(Python,包括 NumPy),允许不同大小元素的数组。
  • NumPy 对象更容易对于大量数据进行高级数学和其他类型操作。相较于 Python 内置序列,NumPy 操作执行效率更高,代码更少。
  • 越来越多的科学和数学 Python 包会使用 NumPy 包,尽管它们支持 Python 内置序列,但在操作之前都会转换成 NumPy 对象。换言之,要想有效的使用现在的科学\数学 Python 包,只了解 Python 内置序列是不够的,我们也要了解 NumPy 对象的使用。

科学计算的关键就是序列大小和速度。举个简单例子,在一个一维的序列中,每个元素要与另一个长度相同的序列相应的元素做乘积,我们可以迭代每个元素。

c = []
for i in range(len(a)):
    c.append(a[i]*b[i])

这个过程会输出正确答案,但如果列表 a 和 b 每个元素都是几百万,那我们就会为 Python 低效的循环付出代价。c 语言可以更快的完成相同任务(我们假设不去做变量声明、初始化和内存分配):

for(i = 0; i < rows; i++):{
    c[i] = a[i]*b[i]
}

这解决了解释Python代码和操作Python对象所涉及的所有开销,但牺牲了Python编码的好处。此外,随着数据维度增加,工作量也增加,如在 2 维数组中,c 语言编码为

for (i = 0; i < rows; i++): {
  for (j = 0; j < columns; j++): {
    c[i][j] = a[i][j]*b[i][j];
  }
}

而 NumPy 为这两张方案这个为我们提供了最优解:当涉及到 ndarray 时,逐个元素操作是默认模式,而实际上是由 提前变异的C语言快速执行。NumPy 代码运行速度与 C 语言接近,同时基于 Python 代码也很简洁(比 Python 还简介简洁!)。最后一个例子说明了 NumPy 的两个优势:矢量化(vectorization)和广播(broadcasting)。
c = a*b

矢量化

矢量化意味着代码中没有肉眼可见的循环和索引,而他们其实发生了,只不过优化后“秘密的进行着”——由预编译的 C 语言运行。
矢量化优点

  • 矢量化代码更简洁易读
  • 代码越少,bug 越少
  • 代码更类似于标准的数学符号(这样修改代码的数学结构会更容易)
  • 向量化会有更多“Pythonic”代码,没有矢量化,代码就会充斥着低效和难以读取的循环。

广播

广播是一个术语,用于描述操作的隐式逐个元素的行为。通常说来,在 NumPy中,包括算数、逻辑、按位、函数等所有操作,以这种隐式逐个元素的方式进行——即广播。此外,上述举例中,a 和 b 可以是形状相同的多维数组、标量、数组,甚至可以是不同形状的两个数组(前提是较小的数组扩大成较大数组的形状)。更多信息请见 broadcasting

NumPy 的 ndarray 全面支持面向对象。例如,ndarray是一个类,具有多种方法和对象。其中,许多方法都被NumPy最外层命名空间函数反映,允许程序员在他们喜欢的任何范式中编写代码。这种灵活性使得 NumPy 数组和 NumPyndarray 成为了Python 中多位数据交互的实际语言。

安装

安装 NumPy 唯一需要的条件就是 Python。如果你不懂 Python,只想用最简单的方式的话,我们推荐anaconda,它包括了 Python、NumPy 和许多其他常见科学计算和数据分析的Python 包。
Linux 和 macOS 安装 NumPy 可以使用pipconda,或者使用下载源。更多信息请参考https://numpy.org/install/#py...

  • donda
    你可以从defaults或者conda-forge 安装 numpy。
 Best practice, use an environment rather than install in the base env
conda create -n my-env
conda activate my-env
# If you want to install from conda-forge
conda config --env --add channels conda-forge
# The actual install command
conda install numpy
  • pip
    pip install numpy

NumPy 快速启动

使用 NumPy 前需要先了解Python;需要安装matplotlib

学习目标

  • 了解一维、二维和 n 维数组的区别
  • 了解在不使用 for 循环的情况下如何将一些线性代数应用到 n 维数组中
  • 了解 n 维数组的轴和形状特性

[基础知识]

NumPy 的主要对象为均匀的多维数组。数组是元素表(通产为数字),所有元素的类型一致,由一个非负整数的元组进行索引。在 NumPy 中,维度称为轴(axes)。
例如,在一个3 维空间中,一个点的坐标数组为[1, 2, 1],它只有一个轴,轴含有 3 个元素,因此我们可以说其长度为 3。下面的例子中的数组中有 2 个轴,第一个轴的长度为 2,第二个长度为 3.

[[1., 0., 0.],
 [0., 1., 2.]]

NumPy 的数组类称为ndarray,别名为array。记住:numpy.array与 Python 标准库的类array.array不同,后者只能解决以为数组,且功能较少。

ndarray最重要的参数:

  1. ndarray.ndim :数组维度的数量
  2. ndarray.shape: 数组的维度,返回一个标明每个维度的数组的大小的元组,如一个 n 行 m 列的矩阵,其 shape 为(n,m),元组的长度就是ndim值。
  3. ndarray.size:数组所有元素的个数,等于 shape 所有值的乘积
  4. ndarray.dtype:数组中元素的数据类型,我们可以创建或者指定为标准 Python 数据类型,也可以使用 NumPy 提供的类型,如numpy.int32、numpy.int16、numpy.float64等
  5. ndarray.itemsize:数组中每个元素的 字节大小,例如float64的大小为 8(64/8),float64为 4 字节。等同于ndarray.dtype.itemsize.
  6. ndarray.data:

创建数组

  • 将列表或者元组转换为数组
import numpy as np
a = np.array([2, 3, 4])
a #array([2, 3, 4])
a.dtype #dtype('int64')
b = np.array([1.2, 3.5, 5.1])
b.dtype #dtype('float64')

有一种常见的错误就是放入太多了参数,而要求是放入一个为序列的参数

a = np.array(1, 2, 3, 4)    # WRONG
Traceback (most recent call last):
  ...
TypeError: array() takes from 1 to 2 positional arguments but 4 were given
a = np.array([1, 2, 3, 4])  # RIGHT

array 方法 将含有一层序列的序列转换成二维数组,将含有两层序列的序列转换成三维数组,以此类推。

b = np.array([(1.5, 2, 3), (4, 5, 6)])
b
array([[1.5, 2. , 3. ],
       [4. , 5. , 6. ]])
b.shape #(2,3)

创建数组时可以指定数据类型

c = np.array([[1, 2], [3, 4]], dtype=complex)
c
array([[1.+0.j, 2.+0.j],
       [3.+0.j, 4.+0.j]])
  • 通过原始占位符创建数组
    通常来说,数组元素的类型未知,但其大小一直。因此,NumPy 也可以通过原始占位符的方法创建数组,这种方法减少了扩充数组这种高成本操作的的需要。
  • zero、ones、empty
    zero函数可以创建一个全是0 的数组,
    ones函数可以创建全是 1 的数组,
    empty函数可以根据缓存创建一个随机内容的数组。
    这三种方法数据类型默认为float64,但可以通过关键字参数dtype进行修改。
np.zeros((3, 4))
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])
np.ones((2, 3, 4), dtype=np.int16)
array([[[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]],

       [[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]]], dtype=int16)
np.empty((2, 3))
array([[3.73603959e-262, 6.02658058e-154, 6.55490914e-260],  # may vary
       [5.30498948e-313, 3.14673309e-307, 1.00000000e+000]])
  • 创建数字序列
    arange函数可以创建一个类似与range的序列,但返回数组。
np.arange(10, 30, 5) #头、尾、步长
array([10, 15, 20, 25])
np.arange(0, 2, 0.3)  # it accepts float arguments
array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8])
  • linspace
    当浮点数作为arange的参数时,由于精度问题,通常很难预测每个元素的值,因此最好使用linspace函数,这个函数可以接收元素个数作为一个参数。
from numpy import pi
np.linspace(0, 2, 9)  
array([0.  , 0.25, 0.5 , 0.75, 1.  , 1.25, 1.5 , 1.75, 2.  ])
x = np.linspace(0, 2 * pi, 100)
f = np.sin(x)
  • 其他

array|zeros|zeros_like|ones|ones_like|empty|empty_like
等等等等

打印数组

NumPy 数组结构形似嵌套列表,但具有一些特征

  • 最后的轴从左向右打印
  • 倒数第二从上到下打印
  • 其余从上到下打印,每个切片与下一个切片各一个空行
  • 如果矩阵太大的话,会自动跳过总监部分,只显示四周,若要全部显示,我们可以设置set_printoptions
c = np.arange(24).reshape(2, 3, 4)  # 3d array
print(c)
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]

np.set_printoptions(threshold=sys.maxsize)  # sys module should be imported

基本操作

  1. 算术操作应用于元素身上,结果返回一个新的答案数组。
a = np.array([20, 30, 40, 50])
b = np.arange(4)
b #array([0, 1, 2, 3])
c = a - b
c #array([20, 29, 38, 47])
b**2 #array([0, 1, 4, 9])
10 * np.sin(a) #array([ 9.12945251, -9.88031624,  7.4511316 , -2.62374854])
a < 35
array([ True,  True, False, False])
  1. *求乘积操作是针对于每个元素,如果要减小矩阵操作,使用@或者dot函数。
A = np.array([[1, 1],
              [0, 1]])
B = np.array([[2, 0],
              [3, 4]])
A * B     # elementwise product
array([[2, 0],
       [0, 4]])
A @ B     # matrix product
array([[5, 4],
       [3, 4]])
A.dot(B)  # another matrix product
array([[5, 4],
       [3, 4]])
  1. +=*=操作是对元数组的操作,不会返回一个新的数组。

    rg = np.random.default_rng(1)  # create instance of default random number generator
    a = np.ones((2, 3), dtype=int)
    b = rg.random((2, 3))
    a *= 3
    a
    array([[3, 3, 3],
        [3, 3, 3]])
    b += a
    b
    array([[3.51182162, 3.9504637 , 3.14415961],
        [3.94864945, 3.31183145, 3.42332645]])
    a += b  # b is not automatically converted to integer type
    Traceback (most recent call last):
     ...
    numpy.core._exceptions._UFuncOutputCastingError: Cannot cast ufunc 'add' output from dtype('float64') to dtype('int64') with casting rule 'same_kind'
  2. 当操作的数组们的数据类型不同,数组结果与更加综合精确的一致(upcasting)

    a = np.ones(3, dtype=np.int32)
    b = np.linspace(0, pi, 3)
    b.dtype.name
    'float64'
    c = a + b
    c
    array([1.        , 2.57079633, 4.14159265])
    c.dtype.name
    'float64'
    d = np.exp(c * 1j)
    d
    array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j,
        -0.54030231-0.84147098j])
    d.dtype.name
    'complex128'
  3. 许多一元操作,如求所有元素的和,在ndarray类的方法中。

    a = rg.random((2, 3))
    a
    array([[0.82770259, 0.40919914, 0.54959369],
        [0.02755911, 0.75351311, 0.53814331]])
    a.sum()
    3.1057109529998157
    a.min()
    0.027559113243068367
    a.max()
    0.8277025938204418
  4. 在默认情况下,对数组的操作可以不管其形状,看做列表即可。但在特殊情况下,可以指定axis参数,沿着这个轴进行操作。

    b = np.arange(12).reshape(3, 4)
    b
    array([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]])
    >>>
    b.sum(axis=0)     # sum of each column
    array([12, 15, 18, 21])
    >>>
    b.min(axis=1)     # min of each row
    array([0, 4, 8])
    >>>
    b.cumsum(axis=1)  # cumulative sum along each row
    array([[ 0,  1,  3,  6],
        [ 4,  9, 15, 22],
        [ 8, 17, 27, 38]])

你可能感兴趣的