数组
貌似也称为tensor?
>>> import numpy as np>>> a = np.array([1, 2, 3])>>> print(type(a))<class 'numpy.ndarray'>>>> print(a.shape)(3,)>>> print(a[0], a[1], a[2])1 2 3>>> a[0] = 5>>> print(a)[5 2 3]>>> b = np.array([[1,2,3], [4,5,6]])>>> print(b.shape)(2, 3)>>> print(b[0,0], b[0,1], b[1,0])1 2 4
从第18行可以看出,array的维数是从外往内数的,看最左边,有2个方括号,那肯定是2维的,然后从外面一层层开始数,有2个方括号([1,2,3]和[4,5,6]),所以第一个维度是2,再数里面的方括号,有3个数字(例如1,2,3),所以,第二个维度是3。这个从第21行,也可以看出array是如何排序的。
创建array
>>> import numpy as np>>> a = np.zeros((2,2))>>> print(a)[[0. 0.][0. 0.]]>>> b = np.ones((1,2))>>> print(b)[[1. 1.]]>>> print(b.shape)(1, 2)>>> c = np.full((2,2), 7)>>> print(c)[[7 7][7 7]]>>> d = np.eye(2)>>> print(d)[[1. 0.][0. 1.]]>>> e = np.random.random((2,2))>>> print(e)[[0.7265488 0.51714818][0.42346625 0.88364074]]
查询
切片
不产生新数据
# 创建一个(3,4)的2维数组>>> a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])>>> print(a.shape)(3, 4)>>> print(a[0, 1])2>>> print(a[0])[1 2 3 4]>>> print(a[0].shape)(4,)# 注意这种取法的结果是产生了一个一维的结果,并不是行向量或者列向量,对a[0]求转置,是其自身# 所以,a[0]到底是个啥?>>> print(a[0].T)[1 2 3 4]>>> print(a[0].T.shape)(4,)# 数组是多维的,所以,切片也要对每个维度进行指定# python的切片类似于subList,所以我们可以直接看出,b的维度一定是(2,2)# 第1个维度上取0,1,那一定前两个方框;第2个维度上取1,2,每个方框内取第2,3个元素>>> b = a[:2, 1:3]>>> print(b)[[2 3][6 7]]# 可以看到a和b指向的是同一块内存区域,所以,对b做改变,a也随之改变# 所以,numpy的切片,产生的一个view,这是与subList以及Python的List都不同的地方>>> b[0, 0] = 77>>> print(a[0, 1])77# 这是个List的例子,可以看到,a切片产生的是一个复制,所以b的改变不影响a>>> a = [1, 2, 3]>>> b = a[0:2]>>> b[0] = 4>>> print(a)[1, 2, 3]
>>> a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])# 在某个维度上,取单个数字和取一个区间,会产生不同的结果>>> row_r1 = a[1, :]>>> row_r2 = a[1:2, :]>>> print(row_r1, row_r1.shape)[5 6 7 8] (4,)>>> print(row_r2, row_r2.shape)[[5 6 7 8]] (1, 4)>>> col_r1 = a[:, 1]>>> col_r2 = a[:, 1:2]>>> print(col_r1, col_r1.shape)[ 2 6 10] (3,)>>> print(col_r2, col_r2.shape)[[ 2][ 6][10]] (3, 1)
取数组的一部分,并产生一个新数据
不同于Slicing,每次产生的是原数组的view,这里每次产生的都是一个新的数组。
>>> import numpy as np>>> a = np.array([[1,2], [3,4], [5,6]])>>> print(a.shape)(3, 2)# 第1个[0,1,2]是对第1个维度来说,也就取第1个维度的第0,1,2元素,这里就是[1,2] [3,4] [5,6]# 第2个[0,1,0]是对第2个维度来说,也就是在第一个1维度确定的基础上,分别取第0,1,0个元素# 所以结果就是,1 4 5>>> print(a[[0,1,2], [0,1,0]])[1 4 5]# 只取1个维度,默认就是第1个维度,所以第1个维度的第 0 1 0个元素就是下面的结果>>> print(a[[0,1,0]])[[1 2][3 4][1 2]]# a[0,0],第1个0表示取第1个维度的第0个元素,第2个0表示取第2个维度的第0个元素# a[0,0] 表示取到的是1个数,如果是a[[0], [0]] 表示取到的是一个数组>>> print(np.array([a[0,0], a[1,1], a[2,0]]))[1 4 5]# 如上描述,这里取到的就是一个数组>>> b = a[[0],[0]]>>> print(b.shape)(1,)# 表示分别取第1个维度的0 0元素,然后在此基础上取第1 1 个元素>>> print(a[[0,0], [1,1]])[2 2]>>> print(np.array([a[0,1], a[0,1]]))[2 2]
有了上面的解释,下面的这段代码应该不难理解。可以利用另外一个array来选取arrray。
>>> import numpy as np>>> a = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])>>> print(a)[[ 1 2 3][ 4 5 6][ 7 8 9][10 11 12]]>>> b = np.array([0, 2, 0, 1])>>> print(b)[0 2 0 1]>>> print(a[np.arange(4), b])[ 1 6 7 11]>>> print(np.arange(4))[0 1 2 3]>>> a[np.arange(4), b] += 10>>> print(a)[[11 2 3][ 4 5 16][17 8 9][10 21 12]]
布尔类型数组的切片
>>> import numpy as np>>> a = np.array([[1,2], [3, 4], [5, 6]])>>> bool_idx = (a > 2)>>> print(bool_idx)[[False False][ True True][ True True]]# 这只是按照1个维度选,所以结果都是1维的>>> print(a[bool_idx])[3 4 5 6]>>> print(a[a > 2])[3 4 5 6]
拼接
vstack
hstack
数据类型
通常,numpy会尝试进行推断数据类型,但是也允许你指定数据类型。
>>> import numpy as np>>> x = np.array([1, 2])>>> print(x.dtype)int64>>> x = np.array([1.0, 2.0])>>> print(x.dtype)float64>>> x = np.array([1.0, 2.0], dtype=np.int64)>>> print(x.dtype)int64
数组运算
numpy提供对运算符的重载,因此可以直接使用运算符来进行数组间的运算,也可以使用提供的函数来进行数组间的运算。
数组元素间对应位置的+ - * /
>>> import numpy as np>>> x = np.array([[1,2],[3,4]], dtype=np.float64)>>> y = np.array([[5,6],[7,8]], dtype=np.float64)>>> print(x + y)[[ 6. 8.][10. 12.]]>>> print(np.add(x,y))[[ 6. 8.][10. 12.]]>>> print(x - y)[[-4. -4.][-4. -4.]]>>> print(np.subtract(x, y))[[-4. -4.][-4. -4.]]>>> print(x * y)[[ 5. 12.][21. 32.]]>>> print(np.multiply(x, y))[[ 5. 12.][21. 32.]]>>> print(x / y)[[0.2 0.33333333][0.42857143 0.5 ]]>>> print(np.divide(x, y))[[0.2 0.33333333][0.42857143 0.5 ]]>>> print(np.sqrt(x))[[1. 1.41421356][1.73205081 2. ]]
数组间的乘法 dot,向量间的dot相当于是向量的内积;矩阵间或矩阵和向量间,相当于矩阵的乘法。
>>> import numpy as np>>> x = np.array([[1,2],[3,4]])>>> y = np.array([[5,6],[7,8]])>>> v = np.array([9,10])>>> w = np.array([11, 12])>>> print(x)[[1 2][3 4]]>>> print(v.shape) # w,v是列向量(2,)>>> print(w.shape)(2,)# dot用于两个向量间是求向量的内积,所以这里是9*11+10*12=219>>> print(v.dot(w))219>>> print(np.dot(v, w))219# x.shape(2,2) v.shape=(2,)# x.dot(v)可以理解为是x的两个行向量分别于v做dot运算# 1*9+2*10=29 3*9+4*10=67>>> print(x.dot(v))[29 67]>>> print(np.dot(x, v))[29 67]>>> a = np.array([1,2])>>> print(a.shape)(2,)>>> b = np.array([[3,4]])>>> b.shape(1, 2)# a 是一个行向量,在np.dot中可以理解为一个矩阵,维度为(1,2)# b 是一个矩阵,维度(1,2),所以np.dot(a,b)维度是对不上的,会报错>>> np.dot(a,b)Traceback (most recent call last):File "<stdin>", line 1, in <module>ValueError: shapes (2,) and (1,2) not aligned: 2 (dim 0) != 1 (dim 0)# c 是一个矩阵,维度是(2,1),所以np.dot(a,b),结果是一个1x1的矩阵>>> c = np.array([[1],[2]])>>> np.dot(a,c)array([5])# 1*5+2*7=19 1*6+2*8=22# 3*5+4*7=43 3*6+4*8=50>>> print(x.dot(y))[[19 22][43 50]]>>> print(np.dot(x, y))[[19 22][43 50]]
作用于数组本身
>>> import numpy as np>>> x = np.array([[1,2],[3,4]])>>> print(x)[[1 2][3 4]]>>> print(np.sum(x)) # Compute sum of all elements10>>> print(np.sum(x, axis=0)) # Compute sum of each column[4 6]>>> print(np.sum(x, axis=1)) # Compute sum of each row[3 7]
向量和矩阵的转置
>>> import numpy as np>>> v = np.array([1,2,3])>>> print(v)[1 2 3]>>> print(v.T)[1 2 3]# 向量的转置等于其本身>>> print(v_t.shape)(3,)>>> print(v.T.shape)(3,)# 矩阵的转置,注意与向量的区别>>> m = np.array([[1,2,3]])>>> print(m.shape)(1, 3)>>> print(m.T.shape)(3, 1)>>> print(m.T)[[1][2][3]]>>> x = np.array([[1,2], [3,4]])>>> print(x)[[1 2][3 4]]>>> print(x.T)[[1 3][2 4]]
广播机制
broadcasting可以简化代码,提高运行速度,通常是在一个大的array和一个小的array间发生。
>>> import numpy as np# We will add the vector v to each row of the matrix x,# storing the result in the matrix y>>> x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])>>> v = np.array([1, 0, 1])>>> y = np.empty_like(x) # Create an empty matrix with the same shape as x# Add the vector v to each row of the matrix x with an explicit loop# 如果没有广播机制,我们需要将x的每一行,都加上v,存入到y的对应行中>>> for i in range(4):... y[i, :] = x[i, :] + v>>> print(y)[[ 2 2 4][ 5 5 7][ 8 8 10][11 11 13]]
当x有很多行时,循环是非常低效的,所以,我们想到也许可以用v生成一个与x同型的矩阵,然后通过矩阵加法来实现。
>>> import numpy as np>>> x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])>>> v = np.array([1, 0, 1])>>> vv = np.tile(v, (4, 1))>>> print(vv)[[1 0 1][1 0 1][1 0 1][1 0 1]]>>> y = x + vv>>> print(y)[[ 2 2 4][ 5 5 7][ 8 8 10][11 11 13]]
广播机制,提供了更简洁的代码和更高效的计算。
>>> import numpy as np# We will add the vector v to each row of the matrix x,# storing the result in the matrix y>>> x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])>>> v = np.array([1, 0, 1])>>> y = x + v # Add v to each row of x using broadcasting>>> print(y) # Prints "[[ 2 2 4][[ 2 2 4][ 5 5 7][ 8 8 10][11 11 13]]# 然而,x.shape=(4,3) v.shape=(3,)# 那么为什么会是行相加呢?>>> x = np.array([[1,2], [3,4], [5,6]])>>> print(x.shape)(3, 2)>>> v = np.array([1, 0, 1])>>> y = x + vTraceback (most recent call last):File "<stdin>", line 1, in <module>ValueError: operands could not be broadcast together with shapes (3,2) (3,)
这里有一篇broadcasting机制的官方文档翻译,可以看看。
现在,应该可以把向量(3,)理解为只有1个维度
也就是按照文档里说的,可以出现broadcasting的原则遵循,从后向前,对应位置进行维度匹配:
- 维度相等
- 有维度为1
例如:
Image (3d array): 256 x 256 x 3Scale (1d array): 3Result (3d array): 256 x 256 x 3
A (4d array): 8 x 1 x 6 x 1B (3d array): 7 x 1 x 5Result (4d array): 8 x 7 x 6 x 5
反例:
A (1d array): 3B (1d array): 4 # trailing dimensions do not matchA (2d array): 2 x 1B (3d array): 8 x 4 x 3 # second from last dimensions mismatched
所以,我们上面,第一个例子能进行broadcasting,是因为遵循了:
# We will add the vector v to each row of the matrix x,# storing the result in the matrix y>>> x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])>>> v = np.array([1, 0, 1])>>> y = x + v # Add v to each row of x using broadcasting>>> print(y) # Prints "[[ 2 2 4][[ 2 2 4][ 5 5 7][ 8 8 10][11 11 13]]x (2d array): 4 x 3v (1d array): 3y (2d array): 4 x 3
而第二个例子,报错,是因为:
>>> x = np.array([[1,2], [3,4], [5,6]])>>> print(x.shape)(3, 2)>>> v = np.array([1, 0, 1])>>> y = x + vTraceback (most recent call last):File "<stdin>", line 1, in <module>ValueError: operands could not be broadcast together with shapes (3,2) (3,)x (2d array): 3 x 2v (1d array): 3y mismatched可以看到,这里最后一个维度是不相等,且没有维度为1的,所以匹配失败,不能broadcasting。
{% hint style=”warning” %}
由此,我们可以总结一下numpy中的一维向量
{% endhint %}
由np.array([0, 1, 2])生成,shape为(3,),他其实不是一个向量的理解方式,所以其转置依然等于自身。
维度变换
np.newaxis
a = np.arange(1, 13).reshape(3,4)b = np.asarray([2, 1 ,1])print("a shape = ", a.shape)print(a)print("\n")print("b shape = ", b.shape)print(b)#print(a-b) 报错,维度不匹配,无法进行broadcastingc = b[:, np.newaxis]print("\n")print("c shape = ", c.shape)print(c)print("\n")print("a-c \n", a-c)# 结果a shape = (3, 4)[[ 1 2 3 4][ 5 6 7 8][ 9 10 11 12]]b shape = (3,)[2 1 1]c shape = (3, 1)[[2][1][1]]a-c[[-1 0 1 2][ 4 5 6 7][ 8 9 10 11]]
变量填充 np.pad
import numpy as npx = np.array([[1,2], [3,4]])print(x)[[1 2][3 4]]# 在第一个维度的前面填充0个0,后面填充1个0,在第二个维度前后都填充0个0print(np.pad(x, pad_width=((0,1), (0,0)), mode='constant', constant_values=0))[[1 2][3 4][0 0]]# 在第一个维度前后都填充0个0,在第二个维度前后都填充1个0print(np.pad(x, pad_width=((0,), (1,)), mode='constant', constant_values=0))[[0 1 2 0][0 3 4 0]]
可以看到,x是一个二维数组,所以pad_width是依次对每一个维度进行指定,pad_width=((0,1), (0,0))表示在第一个维度在后面填充1个;pad_width=((0,), (1,))里的(0, )表示前后都填充一样的个数,这里为0,就是前后都不填充。pad_width是指定对每个维度前后的填充个数,而mode表示如何填充,这里constant表示填充固定值,而最后值由constant_values指定。
排序
import numpy as npimport pandas as pd# =============================================================================a = np.array([0.8, 0.2, 0.9, 0.1])a.sort()# 将a变成排好序的array #不会输出结果 #直接更改arrayprint(a)# 结果显示为 [0.1 0.2 0.8 0.9]# =============================================================================a = np.array([0.8, 0.2, 0.9, 0.1])sorted(a)# 输出结果[0.1 0.2 0.8 0.9] # 但a不会变print(a)# 结果依然为[0.8 0.2 0.9 0.1]# =============================================================================np.argsort(a)# 结果为 array([3, 1, 0, 2])# 这个显示的是,如果排好序,排成[0.1 0.2 0.8 0.9]的样子,这些value在原来的array中的index是多少# =============================================================================np.argsort(np.argsort(a))# 结果为 array([2, 1, 3, 0])# 这个显示的是,按照原来在array中出现的顺序[0.8, 0.2, 0.9, 0.1],如果排序,各个位置上相应的value在序列中的序号是多少# =============================================================================#pd包中类似的pd.DataFrame(a).rank()# 结果如下,这个直接是其序号,从1开始,不是像np包中,从0开始# =============================================================================# 0# 0 3.0# 1 2.0# 2 4.0# 3 1.0# =============================================================================# =============================================================================# 上面结果跟下面这个相同np.argsort(np.argsort(a)) +1# 结果为 array([3, 2, 4, 1])# =============================================================================np.argsort(np.argsort(-a))# 这个是倒序排序的序号# =============================================================================# 排序的另一种方法,正序a[np.argsort(a)]# 结果为array([0.1, 0.2, 0.8, 0.9])# =============================================================================# 排序的另一种方法,倒序a[np.argsort(-a)]# 结果为array([0.9, 0.8, 0.2, 0.1])
Q&A
matrix与array的区别
class ‘numpy.matrixlib.defmatrix.matrix’
