Python NumPy ndarray 入门指南

因为这几天做模糊数学和用 Python OpenCV2 都涉及到 NumPy ndarray,搜到的东西都没有写一些自己想要的。于是干脆自己写一篇,方便以后查阅。

numpy.ndarray 的参数说明在这里:https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html
使用指南:https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.ndarray.html
本文参考的 quick start 在这里:https://docs.scipy.org/doc/numpy/user/quickstart.html

基础

NumPy 的主要对象是齐次多维数组。它是一个元素表(通常是元素是数字),其中所有元素类型都相同,元素以正整数元组索引。在 NumPy 维度(dimension)被称为轴(axis)。
ps. 有几个轴就是几维数组,符合平时生活中有 x, y 两个坐标轴就是二维空间,再加上 z 轴就是三维空间的概念

例如三维空间空一个点的坐标 [1, 2, 1] 有一个轴。这个轴有 3 个元素,即该轴的长度是 3。下面代码区中的数组有两个轴。第一个轴长度是 2,第二个长度是 3.

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

Numpy 的数组类称做 ndarry,别名是 array。注意 numpy.array 和 Python 标准库的类 array.array 不同,标准库的类只处理一维数组(one-dimensional arrays)。

重要属性

  • ndarray.ndim
    the number of axes (dimensions) of the array.
  • ndarray.shape
    数组的维度(the dimensions of the array)。 以一个整型元组的方式表示数组中每个维度的大小。比如对一个有 n 行 m 列的矩阵来说,其 shape 属性为 (n, m)。The length of the shape tuple is therefore the number of axes, ndim.
  • ndarray.size
    数组元素总数。相当于 shape 中每个元素的乘积。
  • ndarray.dtype
    一个用来描述数组中元素类型的对象。我们可以使用 Python 标准类型来创建指定该对象,NumPy 也提供了自己的类型,如 numpy.int32, numpy.int16, and numpy.float641
  • ndarray.itemsize
    数组中每个元素的字节大小。 For example, an array of elements of type float64 has itemsize 8 (=64/8), while one of type complex32 has itemsize 4 (=32/8). It is equivalent to ndarray.dtype.itemsize.

创建

对于创建 numpy.ndarray,官网上给出了五种创建方式2,这里介绍更为常见的两种:

  1. 从 python 其他数据结构中转化而来,比如 list, tuple 等
  2. 固有的 NumPy ndarray 创建方式,比如 np.arange(), np.ones(), np.zeros() 等

这里还会补充一种从文件中读入的方式。

Converting Python array_like Objects to NumPy Arrays

整体来说,我们可以使用 numpy.array() 函数将 Python 中任何以类似数组方式组织的数值数据转化成 numpy.ndarray。最显而易见的例子是 list 和 tuple3

有一些对象支持 array-protocol,因此我们也可以使用 numpy.array() 函数将这些对象转换成 numpy.array。最简单判断对象是否支持这种转换方式的方法是自己动手转换试试。

>>> import numpy as np
>>> x = np.array([2,3,1,0]) # 列表方式
>>> x
array([2, 3, 1, 0])
>>> type(x)
<class 'numpy.ndarray'>
>>> x.dtype
dtype('int32')

>>> x = np.array((1, 2, 3)) # 元组方式
>>> x
array([1, 2, 3])

>>> x = np.array([[ 1.+0.j, 2.+0.j], [ 0.+0.j, 0.+0.j], [ 1.+1.j, 3.+0.j]])  # 不同类型的数据
>>> x
array([[1.+0.j, 2.+0.j],
       [0.+0.j, 0.+0.j],
       [1.+1.j, 3.+0.j]])
>>> x.dtype
dtype('complex128')

>>> x = np.array([[1,2.0],[0,0],(1+1j,3.)]) # note mix of tuple and lists, and types
>>> x
array([[1.+0.j, 2.+0.j],
       [0.+0.j, 0.+0.j],
       [1.+1.j, 3.+0.j]])
>>> x.dtype
dtype('complex128')

Intrinsic NumPy Array Creation

一般来说 array 的元素本身是不知道的,但是如果我们知道 array 的大小(size),我们就可以使用 NumPy 提供的一些方法来创建具有初始值的 array。

下面我列举了一些用于创建 numpy.ndarray 的内建函数,更多可以参考 Array creation routines

  1. numpy.zeros(shape, dtype=float, order=‘C’)
  2. numpy.ones(shape, dtype=None, order=‘C’)
  3. numpy.arange([start, ]stop, [step, ]dtype=None)
  4. numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
  5. numpy.indices(dimensions, dtype=<class ‘int’>)

其中的 shape 参数指定你想创建数组的维度以及每维的大小。比如 (2, 3, 4) 就是创建 234 的三维数组。

其中函数 zero() 创建一个全为 0 的 array,函数 ones() 创建一个全为 1 的 array,函数 empty() 创建一个根据内存状态随机初始化值的 array。

numpy.zeros(shape, dtype=float, order=‘C’)

从函数本身我们就可以知道这个是创建一个全为 0 的 ndarray。其中 shape 指定创建 ndarray 的形状,如是 2行3列的,还是 4行5列的。

>>> np.zeros((2, 3))
array([[ 0., 0., 0.], [ 0., 0., 0.]])
numpy.arange([start, ]stop, [step, ]dtype=None)

arange() 会创建有规律递增的数值的 ndarray,个人感觉类似 range() 函数

>>> np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

>>> np.arange(2, 10, dtype=float)
array([ 2., 3., 4., 5., 6., 7., 8., 9.])

>>> np.arange(2, 3, 0.1)
array([ 2. , 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9])
numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)

当我们给 arange() 函数传入附点类型参数的时候,我们通常不能预测我们创建 array 里元素的个数,这时候我们可以使用 linspace() 函数。

linspace() 会根据 num 参数创建指定数量等差数据,数据的范围在 start 和 stop 之间,默认包含 stop。

>>> np.linspace(1., 4., 6)
array([ 1. ,  1.6,  2.2,  2.8,  3.4,  4. ])

从文件中读入

使用 numpy.genfromtxt,这里以读入 CSV 文件为例

import numpy as np
myFile = np.genfromtxt('filepath', delimiter=',')

如果想读入以 TAB 为分隔符的文件,将 delimiter 换成 \t

多维数组

array() 函数将序列的序列转化成二维数组,将序列的序列的序列转化成三维数组,这样依次下去。

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

在创建的时候我们也可以指明元素的类型

>>> c = np.array( [ [1,2], [3,4] ], dtype=complex )
>>> c
array([[ 1.+0.j,  2.+0.j],
       [ 3.+0.j,  4.+0.j]])

注意事项

NumPy 的 ndarray 基本和 C++/Java 创建时要注意的差不多,创建的时候我们最好对我们需求的大小有个估计,然后再创建一个略多一点的就可以了。 ndarry 并没有 Python 的 list 那么灵活,可以随时更改数组大小(不过你要想增加一行或一列的话,也有一些方法),要更改大小的话一般得重新创建数组,效率不是很高4

增加行

https://stackoverflow.com/questions/3881453/numpy-add-row-to-array
使用函数 vstack

>>> a = np.zeros((3, 4))
>>> a
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])

>>> row = np.ones((1, a.shape[1]))
>>> row

array([[1., 1., 1., 1.]])
>>> np.vstack((a, row))
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [1., 1., 1., 1.]])
增加列

https://stackoverflow.com/questions/15815854/how-to-add-column-to-numpy-array
用 hstack

>>> col = np.ones((a.shape[0], 2))
>>> col
array([[1., 1.],
       [1., 1.],
       [1., 1.]])
>>> np.hstack((a, col))
array([[0., 0., 0., 0., 1., 1.],
       [0., 0., 0., 0., 1., 1.],
       [0., 0., 0., 0., 1., 1.]])

索引,切片,迭代(Indexing, Slicing and Iterating)

一维

一维数组像 Python list 一样被索引、切片和迭代。

>>> a = np.arange(10)**3  # 创建数组
>>> a
array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729])
>>> a[2]  # 索引
8
>>> a[2:5]  # 切片
array([ 8, 27, 64])
>>> a[:6:2] = -1000    # equivalent to a[0:6:2] = -1000; from start to position 6, exclusive, set every 2nd element to -1000
>>> a
array([-1000,     1, -1000,    27, -1000,   125,   216,   343,   512,   729])
>>> a[ : :-1]   # reversed a
array([  729,   512,   343,   216,   125, -1000,    27, -1000,     1, -1000])
>>> for i in a:  # 迭代
...     print(i**(1/3.))
...
nan
1.0
nan
3.0
nan
5.0
6.0
7.0
8.0
9.0

多维

多维数组每个轴可以有一个索引,这些索引以逗号分隔的元组给出:

索引

参考 arrays indexing

索引单个元素
>>> b = np.arange(12).reshape(4,3)  # 将一维数组改成 4*3 的二维数组
>>> b
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])
>>> b[0, 2]  # 注意这里不能越界了。其实和 C++/Java 二维数组访问差不多。
2
索引行

索引第 1 行

>>> b[1, :]
array([3, 4, 5])

索引 0 3 行

>>> b[[0,3], :]
array([[ 0,  1,  2],
       [ 9, 10, 11]])
索引列

索引第 0 列

>>> b[:, 0]
array([0, 3, 6, 9])
切片
>>> b
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])
>>> b[0:4, 1]              # each row in the second column of b
array([ 1,  4,  7, 10])
>>> b[0:5, 1]              # 这里可以越界
array([ 1,  4,  7, 10])
>>> b[ : ,1]               # equivalent to the previous example
array([ 1,  4,  7, 10])
>>> b[1:3, : ]             # each column in the second and third row of b
array([[3, 4, 5],
       [6, 7, 8]])

当提供的索引少于轴的数量时,缺失的索引被认为是完整的切片

>>> b[-1]       # the last row. Equivalent to b[-1,:]
array([ 9, 10, 11])
迭代

对多维数组进行迭代是针对第一个轴完成的

>>> b
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])
>>> for row in b:
	print(row)

[0 1 2]
[3 4 5]
[6 7 8]
[ 9 10 11]

如果你想对数组中的每个元素进行操作的话,你可以使用 flat 属性完成对每个元素的迭代。

>>> for element in b.flat:
	print(element)

0
... # 为了节省地方,我这里认为删去了很多行。把 0~11 竖起来就是结果
11

基本运算

当涉及到算术运算的时候,数组中的每个元素都会参与,运算最后会创建一个新数组并填充结果。

>>> 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])
>>> a + 10  # 加
array([30, 40, 50, 60])
>>> b**2  # 幂运算
array([0, 1, 4, 9])
>>> a<35 # 比较运算
array([ True, True, False, False])

在 NumPy 数组中 * 是按对应元素进行计算的。想要进行 矩阵运算5 我们需要使用操作符 @ 或者 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]])

一些运算如,*= 和 += 并不会新创建数组,而是在原有数组上进行修改。

通用数学函数

NumPy 提供一系列数学函数,例如sin,cos和exp。在 NumPy 中,这些被称为“通用函数”(ufunc)。在 NumPy 中,这些函数都产生一个数组作为输出
由于比较多,我把它放到了脚注6
因为其中的很多函数都涉及到 axis 这个参数,这里就来简单介绍一下这个参数怎么指定,我们不妨以 np.sum() 来探究。

numpy.sum(a, axis=None, dtype=None, out=None, keepdims=, initial=)
给指定轴上的元素求和:
参数说明(这里只介绍这里要谈的参数):

  • a: array_like
  • axis: None or int or tuple of ints, 可选
    指定要求和的轴。默认 axis=None 会对输入数组的所有元素求和,指定负数的话是从最后一个轴开始往前统计(其实和数组负数索引是一个道理)。

对一个轴上的元素求和是一个减少操作,指定的轴会在运算完后消失。比如对一个一维数组上的元素求和,最后我们会得到是一个数。7

想要理解这个东西,我们首先要对每个轴上有哪些元素有个清楚的认识。
比如下图: axis=0 轴的大小是 8,其上的元素是 [ 1 0 0 ] \begin{bmatrix} 1 &amp; 0 &amp; 0 \end{bmatrix} [100] [ 0 1 0 ] \begin{bmatrix} 0 &amp; 1 &amp; 0 \end{bmatrix} [010] [ 0 0 1 ] \begin{bmatrix} 0 &amp; 0 &amp; 1 \end{bmatrix} [001] [ 0 1 0 ] \begin{bmatrix} 0 &amp; 1 &amp; 0 \end{bmatrix} [010].

而 axis=1 轴的大小是 3,其上的元素是 [ 1 0 0 1 . . . ] \begin{bmatrix} 1 \\ 0 \\ 0 \\ 1 \\ ... \end{bmatrix} 1001... [ 0 1 0 0 . . . ] \begin{bmatrix} 0 \\ 1 \\ 0 \\ 0 \\ ... \end{bmatrix} 0100... [ 0 0 1 0 . . . ] \begin{bmatrix} 0 \\ 0 \\ 1 \\ 0 \\ ... \end{bmatrix} 0010...

对 axis=0 求和就是把其上的所有元素相加 [ 1 0 0 ] + [ 0 1 0 ] + [ 0 0 1 ] + . . . + [ 0 1 0 ] \begin{bmatrix} 1 &amp; 0 &amp; 0 \end{bmatrix}+\begin{bmatrix} 0 &amp; 1 &amp; 0 \end{bmatrix}+\begin{bmatrix} 0 &amp; 0 &amp; 1 \end{bmatrix}+ ... +\begin{bmatrix} 0 &amp; 1 &amp; 0 \end{bmatrix} [100]+[010]+[001]+...+[010],因此这个这从图上我们可以直观的看出来 axis=0 这个轴被压缩了,也就是消失了。
在这里插入图片描述
注:上图中的 sum of each row 这些文字我都是参考 universal-function 上面一小节程序里的注释的,因此应该不会出错。

要是没搞懂的话,可以再来看看下面这个三维的例子。

>>> a = np.ones((2,3,4))  # 创建一个三维的数组
>>> a
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.]]])
>>> a.shape # 第一个轴大小为2,第二个轴大小为3,第三个轴大小为4
(2, 3, 4)
>>> a.ndim  # 一共有三个轴
3
>>> a.sum() # 计算所有元素的总和
24

>>> a.sum(axis=0)
array([[2., 2., 2., 2.],
       [2., 2., 2., 2.],
       [2., 2., 2., 2.]])
>>> a.sum(axis=0).shape
(3, 4)

>>> a.sum(axis=1)
array([[3., 3., 3., 3.],
       [3., 3., 3., 3.]])
>>> a.sum(axis=1).shape
(2, 4)

>>> a.sum(axis=2)
array([[4., 4., 4.],
       [4., 4., 4.]])
>>> a.sum(axis=2).shape
(2, 3)

参数指定为负数

>>> a.sum(axis=-1)  # 等价于 a.sum(axis=2)
array([[4., 4., 4.],
       [4., 4., 4.]])
>>> a.sum(axis=-2)  # 等价于 a.sum(axis=1)
array([[3., 3., 3., 3.],
       [3., 3., 3., 3.]])
>>> a.sum(axis=-3)  # 等价于 a.sum(axis=0)
array([[2., 2., 2., 2.],
       [2., 2., 2., 2.],
       [2., 2., 2., 2.]])

参数指定为元组

>>> a.shape
(2, 3, 4)
>>> a.sum(axis=(0,1))
array([6., 6., 6., 6.])
>>> a.sum(axis=(0,1)).shape
(4,)

输出

当我们输出数组时,NumPy 以与嵌套列表类似的方式显示它,但具有以下布局:

  • the last axis is printed from left to right,
  • the second-to-last is printed from top to bottom,
  • the rest are also printed from top to bottom, with each slice separated from the next by an empty line.

然后将一维数组打印为行,将二维数组打印为矩阵,将三维数组打印为矩阵列表。

>>> a = np.arange(6)                         # 1d array
>>> print(a)
[0 1 2 3 4 5]
>>>
>>> b = np.arange(12).reshape(4,3)           # 2d array
>>> print(b)
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
>>>
>>> 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]]]

当数组太大的时候,会省略显示一部分内容

>>> print(np.arange(10000))
[   0    1    2 ..., 9997 9998 9999]

我们可以通过以下方式强制显示全:

>>> np.set_printoptions(threshold=np.nan)

  1. Data types ↩︎

  2. Array creation routines ↩︎

  3. 列表和元组的区别是,元组不能修改。创建空元组和空列表方式类似:empty_tuple = () ↩︎

  4. https://stackoverflow.com/questions/568962/how-do-i-create-an-empty-array-matrix-in-numpy ↩︎

  5. [ a 1 b 1 c 1 d 1 ] ∗ [ a 2 b 2 c 2 d 2 ] = [ ( a 1 ∗ a 2 + b 1 ∗ c 2 ) ( a 1 ∗ b 2 + b 1 ∗ d 2 ) ( c 1 ∗ a 2 + d 1 ∗ c 2 ) ( c 1 ∗ b 2 + d 1 ∗ d 2 ) ] \begin{bmatrix} a1 &amp; b1 \\ c1 &amp; d1 \end{bmatrix} * \begin{bmatrix} a2 &amp; b2 \\ c2 &amp; d2 \end{bmatrix} = \begin{bmatrix} (a1*a2+b1*c2) &amp; (a1*b2+b1*d2) \\ (c1*a2+d1*c2) &amp; (c1*b2+d1*d2) \end{bmatrix} [a1c1b1d1][a2c2b2d2]=[(a1a2+b1c2)(c1a2+d1c2)(a1b2+b1d2)(c1b2+d1d2)] ↩︎

  6. all,
    any,
    apply_along_axis,
    argmax,
    argmin,
    argsort,
    average,
    bincount,
    ceil,
    clip,
    conj,
    corrcoef,
    cov,
    cross,
    cumprod,
    cumsum,
    diff,
    dot 矩阵乘法
    floor,
    inner,
    inv,
    lexsort,
    max, 最大值
    maximum,
    mean, 平均值
    median,
    min, 最小值
    minimum,
    nonzero,
    outer,
    prod,
    re,
    round,
    sort, 排序
    std, 样本标准差
    sum, 求和
    trace,
    transpose,
    var,
    vdot,
    vectorize,
    where ↩︎

  7. https://stackoverflow.com/questions/22320534/how-does-the-axis-parameter-from-numpy-work ↩︎

  • 14
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值