基础知识
NumPy的主要对象是同构多维数组。它是一个元素(通常是数字)表,所有元素(通常是数字)都具有相同的类型,由非负整数元组索引。在NumPy中,维度称为轴(axis)。
例如,用于 3D 空间中点的坐标的数组 具有一个轴。该轴中有3个元素,因此我们说它的长度为3。在下图所示的示例中,数组有 2 个轴。第一个轴的长度为2,第二个轴的长度为3。[1, 2, 1]
[[1., 0., 0.],
[0., 1., 2.]]
一、Numpy 的数组类
NumPy 的数组类称为 array。注意,这与标准Python库类不同,后者只处理一维数组,并且提供的功能较少。
1. Numpy数组对象的基本属性
-
ndarray.ndim:数组的轴数(尺寸)
-
ndarray.shape:数组的尺寸。这是一个整数元组,指示每个维度中数组的大小。对于具有 n 行和 m 列的矩阵,将为(n, m) 。因此,元组的长度是轴数。
-
ndarray.size:数组的元素总数。这等于shape元组元素的乘积。
-
ndarray.dtype:描述数组中元素类型的对象。可以使用标准Python类型创建或指定dtype。此外,NumPy还提供了自己的类型。
numpy.int32、numpy.int16 和 numpy.float64 就是一些例子。
-
ndarray.itemsize:数组中每个元素的大小(以字节为单位)。例如,float64类型的数组元素有 8个字节 (=64/8),而complex32类型的一个元素有 4 个字节(=32/8)。它等效于ndarray.dtype.items
- 例一:
import numpy as np # 一般在导入numpy包时,会给其起别名
>>> a = np.arange(15).reshape(3, 5)
>>> a
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> a.shape
(3, 5)
>>> a.ndim
2
>>> a.dtype.name
'int64'
>>> a.itemsize # 每个数组元素的字节大小
8
>>> a.size
15
>>> type(a)
<class 'numpy.ndarray'>
>>> b = np.array([6, 7, 8])
>>> b
array([6, 7, 8])
>>> type(b)
<class 'numpy.ndarray'>
2. 创建阵列
有很多方法可以创建一个阵列。首先,你可以使用Python中的最常见的两个数据类型:list、tuple来创建。生成的阵列类型从传入的数据类型中继承。若是,传入的数据类型并不统一,则将所有数据会强制转换为精度最高的数据类型。
import numpy as np
>>> a = np.array([2, 3, 4])
>>> a
array([2, 3, 4])
>>> a.dtype
dtype('int64') # 继承数据类型,我的CMD窗口是 int32
>>> a.itemsize
8
>>> b = np.array([1.2, 3.5, 5.1])
>>> b.dtype
dtype('float64') # 继承数据类型
>>> b.itemsize
8
## 进行强制转换
>>> b = np.array([(1.5, 2, 3), (4, 5, 6)])
>>> b
array([[1.5, 2. , 3. ],
[4. , 5. , 6. ]])
注意,使用numpy的array方法创建阵列时,只能传入一个用于初始化阵列的参数。
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
定义阵列时,可以传入数据类型。
>>> c = np.array([[1, 2], [3, 4]], dtype=complex)
>>> c
array([[1.+0.j, 2.+0.j],
[3.+0.j, 4.+0.j]])
-
创建特殊阵列
- 有些情况下,阵列的元素并不知晓,但是阵列的大小已固定,则我们可以先定义出带有一定数值的初始阵列。
- 0 阵列 —— zeros
- 1 阵列 —— ones
- 默认阵列 —— empty:初始化时不传入数值,以所占内存空间的原始值为准
- 均分阵列 —— arange:和range一致(起始点,终止后一个点,步长),但是可以接受float值
- 截断阵列 —— linspace:建立一个数轴,将对应区间内均分为 n - 1份,返回所以端点的值
>>> 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) # 千万注意初始化三维阵列的排列方式:Z(层数) - y(行数) - x(列数) 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], [5.30498948e-313, 3.14673309e-307, 1.00000000e+000]]) # empty方法可能会出错 # 均分阵列 >>> 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]) # 截断阵列 >>> from numpy import pi >>> np.linspace(0, 2, 9) # 9 numbers from 0 to 2 array([0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. ]) >>> x = np.linspace(0, 2 * pi, 100) # useful to evaluate function at lots of points >>> f = np.sin(x) # f是一个一维阵列 >>> f array([ 0.00000000e+00, 6.34239197e-02, 1.26592454e-01,......,-2.44929360e-16]) -
其他板块:
array,zeros,zeros_like,ones,ones_like,empty,empty_like,arange,linspace, numpy.random.Generator.rand, numpy.random.Generator.randn,fromfunction,fromfile
3. 打印阵列
与打印多维数组相似,但是阵列有如下特点:
- 最后一维是从左到右打印
- 倒数第二维是从上至下打印
- 剩余部分同样是从上到下打印,每个切片之间使用空行分隔
一维阵列以行为单位打印出来,二维阵列以矩阵,三维阵列以矩阵列表打印。
当然,如果一个阵列所含元素过多,则只会打印角落元素,会省略中间元素
>>> 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]
>>>
print(np.arange(10000).reshape(100, 100))
[[ 0 1 2 ... 97 98 99]
[ 100 101 102 ... 197 198 199]
[ 200 201 202 ... 297 298 299]
...
[9700 9701 9702 ... 9797 9798 9799]
[9800 9801 9802 ... 9897 9898 9899]
[9900 9901 9902 ... 9997 9998 9999]]
reshape的用法指南:below。
若是想要查看大阵列的所有元素,可以使用numpy模块的set_printoptions方法:
import sys
np.set_printoptions(threshold=sys.maxsize) # sys module should be imported
二、基础操作
阵列的算术运算会应用元素方式(和矩阵操作区分开,也就是对位元素之间的运算)。
>>> 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) # 注意np的三角函数是弧度制,6/pi = 30
array([ 9.12945251, -9.88031624, 7.4511316 , -2.62374854])
>>> a < 35 # 返回一个bool阵列
array([ True, True, False, False])
和大多数矩阵语言不同,* 在阵列运算中只是对位相乘。
若想要使用矩阵乘法:① 使用 narray对象的 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]])
另外一些操作,诸如+=、*=会对运算阵列进行修改(带有 = 嘛)。
>>> 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 # 会自动将 3 扩充至与 a 尺寸一致的阵列再进行运算
>>> 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'
不同类型的阵列进行运算时,会将结果类型转化为更常见或者更精确的类型(向上转换)。
>>> 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' # 转换为精度更高的float64了
>>> d = np.exp(c * 1j)
>>> d
array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j,
-0.54030231-0.84147098j])
>>> d.dtype.name
'complex128'
求阵列元素之和,最小值、最大值、均值等。
>>> 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
# 上面的这些操作都是没有考虑到“轴”的特性的,就是将所有元素都视为列表
# 若要考虑轴,则可以调用np模块的内置方法sum、mean、max、min时,传入axis参数(0 1 2)
>>> 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]])
>>> a = np.linspace(1, 4, 4).reshape((2, 2)) # 截断数轴
>>> a
array([[1., 2.],
[3., 4.]])
>>> print(np.sum(a, 0)) # 按照第一维(列)求和
[4. 6.]
>>> print(np.sum(a, 1)) # 按照第二维(行)求和
[3. 7.]
>>> print(np.cumsum(a, 0)) # 按照第一维(列)累计求和
[[1. 2.]
[4. 6.]]
>>> print(np.max(a, 0))
[3. 4.]
>>> print(np.min(a, 1))
[1. 3.]
>>>
三、常用函数
Numpy模块在内置了许多常用的数学函数,如sin、cos和exp[e ^ n]。这类函数被称为“universal functions” (ufunc)。使用这些函数时,会进行阵列运算,并返回一个阵列。
>>> B = np.arange(3)
>>> B
array([0, 1, 2])
>>> np.exp(B)
array([1. , 2.71828183, 7.3890561 ])
>>> np.sqrt(B)
array([0. , 1. , 1.41421356])
>>> C = np.array([2., -1., 4.])
>>> np.add(B, C) # 对位相加
array([2., 0., 6.])
-
其他版块:
all,any,apply_along_axis,argmax,argmin,argsort,average,bincount,ceil,clip,conj,corrcoef,cov,cross,cumprod,cumsum,diff,dot,floor,inner,invert,lexsort,max,maximum,mean,median,min,minimum,nonzero,outer,prod,re,round,sort,std,sum,trace,transpose,var,vdot,vectorize,where。
四、索引、切片和迭代
一维阵列可以进行与python的list、tuple相似的索引、切片和迭代操作。
1. 索引
>>> 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])
>>> # equivalent to a[0:6:2] = 1000;
>>> # from start to position 6, exclusive, set every 2nd element to 1000
>>> a[:6:2] = 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.)) ## 对所有阵列元素开立方
...
9.999999999999998
1.0
...
8.999999999999998
多维阵列的每个轴也有索引,这些索引以逗号分隔的元组形式给出。
>>> def f(x, y):
... return 10 * x + y
...
>>> b = np.fromfunction(f, (5, 4), dtype=int) # 与列表生成式类似
>>> b
array([[ 0, 1, 2, 3],
[10, 11, 12, 13],
[20, 21, 22, 23],
[30, 31, 32, 33],
[40, 41, 42, 43]])
>>> b[2, 3] # 不像list和tuple,可以直接利用一个[]来进行访问多维元素
23
# 始终牢记:,使用来分隔维度
>>> b[0:5, 1] # each row in the second column of b
array([ 1, 11, 21, 31, 41])
>>> b[:, 1] # equivalent to the previous example
array([ 1, 11, 21, 31, 41])
>>> b[1:3, :] # each column in the second and third row of b
array([[10, 11, 12, 13],
[20, 21, 22, 23]])
2. 切片
若是提供的索引维度小于实际维度,则会将缺少的高维自动取全。
>>> b[-1] # the last row. Equivalent to b[-1, :]
array([40, 41, 42, 43])
Numpy甚至允许使用...来代替省略其他维度,并且会将其取全。如下面的 x 为五轴:
x[1, 2, ...]is equivalent tox[1, 2, :, :, :],x[..., 3]tox[:, :, :, :, 3]andx[4, ..., 5, :]tox[4, :, :, 5, :].
# 最外层的[]表名传入参数是一个对象,内含了两个二维阵列
>>> c = np.array([[[ 0, 1, 2], # a 3D array (two stacked 2D arrays)
... [ 10, 12, 13]],
... [[100, 101, 102],
... [110, 112, 113]]])
>>> c.shape
(2, 2, 3) # 三维的排列顺序是 层、行、列
# 牢记所有的下标都是从 0 开始的
>>> c[1, ...] # same as c[1, :, :] or c[1]
array([[100, 101, 102],
[110, 112, 113]])
>>> c[..., 2] # same as c[:, :, 2]
array([[ 2, 13], # 每个层级的列按照阵列给出
[102, 113]])
3. 迭代
-
对多维阵列的迭代是通过第一维来进行的,所以是按行来遍历;
-
如果是想要按列遍历的话可以将阵列转置(np.transpose(A) Or A.T);
-
如果是想依次遍历所有元素的话,可以利用Numpy的flat(返回generator),或flatten(返回一维阵列)
>>> for row in b:
... print(row)
...
[0 1 2 3]
[10 11 12 13]
[20 21 22 23]
[30 31 32 33]
[40 41 42 43]
# 若要实现列遍历:
>>> for column in b.T:
... print(column)
# 若要实现逐个遍历(行优先)
for element in b.flat:
print(element)
0
1
...
43
-
其他版块:
Indexing on ndarrays, Indexing routines (reference),
newaxis,ndenumerate,indices。