python之numpy快速上手(一)
一.学习前准备条件:
需要一点python基础。
为了可以做照着例子做,需要安装matplotlib
二、基础
NumPy的主要对象是同构多维数组,它是一个元素表(通常是数字),所有类型都相同,由非负整数元组索引。在numpy中纬度称为轴。
例如,三维空间中一个点的坐标【1,2,3】有一个轴,这个轴有三个元素,所以它的长度是3,在下面的图片的例子中,这个数组有2个轴,第一个轴长度为2,第二个轴的长度为3.
numpy的数组类叫ndarray,它也被别名数组所知。注意,numpy.array不同于标准python库的array.array类,标准python库的array.array类只能处理一维数组并且提供的方法也少。ndarray对象的重要属性有:
ndarray.ndim
数组的轴数(维数)
ndarray.shape
数组的维度,输出一个整数元组,表示数组在每个维度中的大小。对于一个n行m列的矩阵,它的shape为(n,m),因此shape元组的长度是轴的数量ndim
ndarray.size
数组元素的总数量,它等于shape元素的乘积。
ndarray.dtype
一个描述数组类型数量的对象。可以使用标准Python类型创建或指定dtype。例外numpy有它自己的类型,例如:numpy.int32, numpy.int16, and numpy.float64 。
ndarray.itemsize
数组中每个元素的大小(以字节为单位),例如:类型为float64的数组元素的itemsize为8(=64/8),而类型为complex32的元素itemsize为4(=32/8)。它相当于ndarray.dtype.itemsize
ndarray.data
包含数组中实际元素的缓冲区。通常不需要使用这个属性,因为我们会使用索引工具访问数组的元素
代码:
>>>import numpy as np >>> 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.创建数组
有几种方法创建数组,例如可以使用array方法从常规的python列表和元组中创建array,这样创建出来的array的类型由序列中元素的类型推断出。
代码:
>>>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')
一个常见的错误是用多个参数组成一个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
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提供了几个函数来创建带有初始占位符内容的数组,这减少了增长数组的必要性,而增长数组是一种昂贵的操作。
zeros函数创建的数组全是零,ones函数创建的数组全是一,而empty函数创建初始值为随机的和取决于内容状态的数组,但是默认创建的array的类型是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]])
为了创建一个数字序列,numpy提供了一个类似于python内置的range的函数arange,但是arange放回一个array
>>> 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])
当arange使用浮点数作为参数时,由于浮点精度有限,通常不可能预测得到的单元数。基于此,最好使用接收我们想要的元素数量作为参数的函数linspace,步骤为:
>>> 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)
3.打印array
一维数组被打印成行,二维数组被打印成矩阵,三维数组被打印成矩阵列表。
>>> 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]]]
如果数组太大而不能打印,NumPy会自动跳过数组的中心部分,只打印边角
>>> 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]]
禁用此行为并强制NumPy打印整个数组,你可以改变打印的参数,您可以使用set_printoptions更改打印选项。
>>> np.set_printoptions(threshold=sys.maxsize) # sys module should be imported
4.基本操作
数组上的算术运算符以元素方式应用。创建一个新数组并填充结果。
>>> 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])
与许多矩阵语言不同,乘积运算符*在NumPy数组中对元素进行操作,矩阵乘积可以使用@运算符或者点函数执行。
>>> 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 >>> 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' >>> d = np.exp(c * 1j) >>> d array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j, -0.54030231-0.84147098j]) >>> d.dtype.name 'complex128'
很多一元操作,例如计算数组所有元素的和,是作为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
默认情况下,这些操作应用于数组,就好像它是一个数字列表,而不管它的形状。但是,通过指定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]])
通用方法
Numpy提供例如sin、cos和exp等的相似数学方法,在Numpy中,它们称为通用方法(ufunc),在NumPy中,这些方法以元素方式对数组进行操作,生成一个数组作为输出。
>>> 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.])
5.索引、切片和迭代
一维数组可以被索引、切片和迭代,就像列表和其他Python序列一样。
>>> 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 9.999999999999998 3.0 9.999999999999998 4.999999999999999 5.999999999999999 6.999999999999999 7.999999999999999 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] 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]])
当提供的指标少于轴的数量时,缺失的指标被认为是完整的切片:
>>> b[-1] # the last row. Equivalent to b[-1, :] array([40, 41, 42, 43])
在b[i]中括号内的表达式被当作i,后面跟着尽可能多的实例:来表示剩余的轴。NumPy还允许你使用点作为b[i,…]来写这个。这些点(…)表示生成一个完整索引元组所需的冒号数量。例如,如果x是一个有5个轴的数组,那么
x[1, 2, ...]等价于x[1, 2, :, :, ;]
x[..., 3]等价于x[:, :, :, :, 3]
x[4, ..., 5, :]等价于x[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) >>> 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]])
对多维数组的迭代是根据第一个轴完成的:
>>> 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]
但是,如果你想对数组中的每个元素执行操作,你可以使用flat属性,它是一个遍历数组所有元素的迭代器:
>>> for element in b.flat: ... print(element) ... 0 1 2 3 10 11 12 13 20 21 22 23 30 31 32 33 40 41 42 43
7.形状操作
改变数组的形状
数组的形状由每个轴上的元素数量决定:
>>> a = np.floor(10 * rg.random((3, 4)))#floor类似取整,np.floor(-2.5)=3而np.floor(2.5)=2 >>> a array([[3., 7., 3., 4.], [1., 4., 2., 2.], [7., 2., 4., 9.]]) >>> a.shape (3, 4)
数组的形状可以通过不同的命令改变。注意以下三个命令都返回一个修改后的数组,而不是改变原来的数组:
>>> a.ravel() # returns the array, flattened array([3., 7., 3., 4., 1., 4., 2., 2., 7., 2., 4., 9.]) >>> a.reshape(6, 2) # returns the array with a modified shape array([[3., 7.], [3., 4.], [1., 4.], [2., 2.], [7., 2.], [4., 9.]]) >>> a.T # returns the array, transposed array([[3., 1., 7.], [7., 4., 2.], [3., 2., 4.], [4., 2., 9.]]) >>> a.T.shape (4, 3) >>> a.shape (3, 4)
由ravel产生的数组中元素的顺序通常是“c风格的”,即最右边的下标“变化最快”,所以a[0, 0]的下一个元素是a[0, 1].如果数组被重塑为其他形状,数组同样被视为“c风格”。NumPy通常按照这个顺序创建数组,所以ravel通常不需要复制它的参数,但如果数组是通过获取另一个数组的切片或使用不同寻常的选项创建的,则可能需要复制它。ravel和reshape方法也可以使用可选参数指定而得到一个FORTRAN-style的数组,FORTRAN-style的数组最左边的索引变化最快。
reshape方法返回一个修改过的形状的参数,而ndarray.Resize方法修改数组本身:
>>> a array([[3., 7., 3., 4.], [1., 4., 2., 2.], [7., 2., 4., 9.]]) >>> a.resize((2, 6)) >>> a array([[3., 7., 3., 4., 1., 4.], [2., 2., 7., 2., 4., 9.]])
如果在重塑操作中一个维度被指定为-1,其他维度将自动计算:
>>> a.reshape(3, -1) array([[3., 7., 3., 4.], [1., 4., 2., 2.], [7., 2., 4., 9.]])
将不同的数组合并在一起
>>> a = np.floor(10 * rg.random((2, 2))) >>> a array([[9., 7.], [5., 2.]]) >>> b = np.floor(10 * rg.random((2, 2))) >>> b array([[1., 9.], [5., 1.]]) >>> np.vstack((a, b)) array([[9., 7.], [5., 2.], [1., 9.], [5., 1.]]) >>> np.hstack((a, b)) array([[9., 7., 1., 9.], [5., 2., 5., 1.]])
函数column_stack将一维数组作为列堆叠到2D数组中。它只等价于2D数组的hstack:
>>> from numpy import newaxis >>> np.column_stack((a, b)) # with 2D arrays array([[9., 7., 1., 9.], [5., 2., 5., 1.]]) >>> a = np.array([4., 2.]) >>> b = np.array([3., 8.]) >>> np.column_stack((a, b)) # returns a 2D array array([[4., 3.], [2., 8.]]) >>> np.hstack((a, b)) # the result is different array([4., 2., 3., 8.]) >>> a[:, newaxis] # view `a` as a 2D column vector array([[4.], [2.]]) >>> np.column_stack((a[:, newaxis], b[:, newaxis])) array([[4., 3.], [2., 8.]]) >>> np.hstack((a[:, newaxis], b[:, newaxis])) # the result is the same array([[4., 3.], [2., 8.]])
另一方面,对于任何输入数组,函数row_stack等同于vstack。事实上,row_stack是vstack的别名:
>>> np.column_stack is np.hstack False >>> np.row_stack is np.vstack True
通常,对于二维以上的数组,hstack堆栈沿其第二个轴,vstack堆栈沿其第一个轴,以及concatenate允许一个可选参数,给出应该发生连接的轴的数目。
注意:在复杂的情况下,r_和c_对于沿着一个轴堆叠数字来创建数组很有用。它们允许使用范围字面量:。
>>> np.r_[1:4, 0, 4] array([1, 2, 3, 0, 4])
当与数组一起使用时,r_和c_在默认行为上类似于vstack和hstack,但是允许一个可选的参数来指定要连接的轴的编号。
将一个数组分解成几个较小的数组
使用hsplit,可以沿水平轴拆分数组,可以指定要返回的形状相同的数组的数量,也可以指定拆分后的列:
>>> a = np.floor(10 * rg.random((2, 12))) >>> a array([[6., 7., 6., 9., 0., 5., 4., 0., 6., 8., 5., 2.], [8., 5., 5., 7., 1., 8., 6., 7., 1., 8., 1., 0.]]) >>> # Split `a` into 3 >>> np.hsplit(a, 3) [array([[6., 7., 6., 9.], [8., 5., 5., 7.]]), array([[0., 5., 4., 0.], [1., 8., 6., 7.]]), array([[6., 8., 5., 2.], [1., 8., 1., 0.]])] >>> # Split `a` after the third and the fourth column(在a的第三列和四列的位置分解数组) >>> np.hsplit(a, (3, 4)) [array([[6., 7., 6.], [8., 5., 5.]]), array([[9.], [7.]]), array([[0., 5., 4., 0., 6., 8., 5., 2.], [1., 8., 6., 7., 1., 8., 1., 0.]])]
参考:
https://numpy.org/devdocs/user/quickstart.html