目录
Converting Python array_like Objects to NumPy Arrays
基础
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,这里介绍更为常见的两种:
- 从 python 其他数据结构中转化而来,比如 list, tuple 等
- 固有的 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:
- numpy.zeros(shape, dtype=float, order=‘C’)
- numpy.ones(shape, dtype=None, order=‘C’)
- numpy.arange([start, ]stop, [step, ]dtype=None)
- numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
- 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
多维
多维数组每个轴可以有一个索引,这些索引以逗号分隔的元组给出:
>>> 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,其上的元素是 [100]\begin{bmatrix} 1 & 0 & 0 \end{bmatrix}[100]、 [010]\begin{bmatrix} 0 & 1 & 0 \end{bmatrix}[010]、 [001]\begin{bmatrix} 0 & 0 & 1 \end{bmatrix}[001] … [010]\begin{bmatrix} 0 & 1 & 0 \end{bmatrix}[010].
而 axis=1 轴的大小是 3,其上的元素是 ⎡⎣⎢⎢⎢⎢⎢⎢1001...⎤⎦⎥⎥⎥⎥⎥⎥\begin{bmatrix} 1 \\ 0 \\ 0 \\ 1 \\ ... \end{bmatrix}⎣⎢⎢⎢⎢⎡1001...⎦⎥⎥⎥⎥⎤ 、⎡⎣⎢⎢⎢⎢⎢⎢0100...⎤⎦⎥⎥⎥⎥⎥⎥\begin{bmatrix} 0 \\ 1 \\ 0 \\ 0 \\ ... \end{bmatrix}⎣⎢⎢⎢⎢⎡0100...⎦⎥⎥⎥⎥⎤ 和 ⎡⎣⎢⎢⎢⎢⎢⎢0010...⎤⎦⎥⎥⎥⎥⎥⎥\begin{bmatrix} 0 \\ 0 \\ 1 \\ 0 \\ ... \end{bmatrix}⎣⎢⎢⎢⎢⎡0010...⎦⎥⎥⎥⎥⎤。
对 axis=0 求和就是把其上的所有元素相加 [100]+[010]+[001]+...+[010]\begin{bmatrix} 1 & 0 & 0 \end{bmatrix}+\begin{bmatrix} 0 & 1 & 0 \end{bmatrix}+\begin{bmatrix} 0 & 0 & 1 \end{bmatrix}+ ... +\begin{bmatrix} 0 & 1 & 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)