数组
貌似也称为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 elements
10
>>> 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 + v
Traceback (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 3
Scale (1d array): 3
Result (3d array): 256 x 256 x 3
A (4d array): 8 x 1 x 6 x 1
B (3d array): 7 x 1 x 5
Result (4d array): 8 x 7 x 6 x 5
反例:
A (1d array): 3
B (1d array): 4 # trailing dimensions do not match
A (2d array): 2 x 1
B (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 3
v (1d array): 3
y (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 + v
Traceback (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 2
v (1d array): 3
y 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) 报错,维度不匹配,无法进行broadcasting
c = 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 np
x = np.array([[1,2], [3,4]])
print(x)
[[1 2]
[3 4]]
# 在第一个维度的前面填充0个0,后面填充1个0,在第二个维度前后都填充0个0
print(np.pad(x, pad_width=((0,1), (0,0)), mode='constant', constant_values=0))
[[1 2]
[3 4]
[0 0]]
# 在第一个维度前后都填充0个0,在第二个维度前后都填充1个0
print(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 np
import pandas as pd
# =============================================================================
a = np.array([0.8, 0.2, 0.9, 0.1])
a.sort()
# 将a变成排好序的array #不会输出结果 #直接更改array
print(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’