数组

貌似也称为tensor?

  1. >>> import numpy as np
  2. >>> a = np.array([1, 2, 3])
  3. >>> print(type(a))
  4. <class 'numpy.ndarray'>
  5. >>> print(a.shape)
  6. (3,)
  7. >>> print(a[0], a[1], a[2])
  8. 1 2 3
  9. >>> a[0] = 5
  10. >>> print(a)
  11. [5 2 3]
  12. >>> b = np.array([[1,2,3], [4,5,6]])
  13. >>> print(b.shape)
  14. (2, 3)
  15. >>> print(b[0,0], b[0,1], b[1,0])
  16. 1 2 4

从第18行可以看出,array的维数是从外往内数的,看最左边,有2个方括号,那肯定是2维的,然后从外面一层层开始数,有2个方括号([1,2,3]和[4,5,6]),所以第一个维度是2,再数里面的方括号,有3个数字(例如1,2,3),所以,第二个维度是3。这个从第21行,也可以看出array是如何排序的。

创建array

  1. >>> import numpy as np
  2. >>> a = np.zeros((2,2))
  3. >>> print(a)
  4. [[0. 0.]
  5. [0. 0.]]
  6. >>> b = np.ones((1,2))
  7. >>> print(b)
  8. [[1. 1.]]
  9. >>> print(b.shape)
  10. (1, 2)
  11. >>> c = np.full((2,2), 7)
  12. >>> print(c)
  13. [[7 7]
  14. [7 7]]
  15. >>> d = np.eye(2)
  16. >>> print(d)
  17. [[1. 0.]
  18. [0. 1.]]
  19. >>> e = np.random.random((2,2))
  20. >>> print(e)
  21. [[0.7265488 0.51714818]
  22. [0.42346625 0.88364074]]

查询

切片

不产生新数据

  1. # 创建一个(3,4)的2维数组
  2. >>> a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])
  3. >>> print(a.shape)
  4. (3, 4)
  5. >>> print(a[0, 1])
  6. 2
  7. >>> print(a[0])
  8. [1 2 3 4]
  9. >>> print(a[0].shape)
  10. (4,)
  11. # 注意这种取法的结果是产生了一个一维的结果,并不是行向量或者列向量,对a[0]求转置,是其自身
  12. # 所以,a[0]到底是个啥?
  13. >>> print(a[0].T)
  14. [1 2 3 4]
  15. >>> print(a[0].T.shape)
  16. (4,)
  17. # 数组是多维的,所以,切片也要对每个维度进行指定
  18. # python的切片类似于subList,所以我们可以直接看出,b的维度一定是(2,2)
  19. # 第1个维度上取0,1,那一定前两个方框;第2个维度上取1,2,每个方框内取第2,3个元素
  20. >>> b = a[:2, 1:3]
  21. >>> print(b)
  22. [[2 3]
  23. [6 7]]
  24. # 可以看到a和b指向的是同一块内存区域,所以,对b做改变,a也随之改变
  25. # 所以,numpy的切片,产生的一个view,这是与subList以及Python的List都不同的地方
  26. >>> b[0, 0] = 77
  27. >>> print(a[0, 1])
  28. 77
  29. # 这是个List的例子,可以看到,a切片产生的是一个复制,所以b的改变不影响a
  30. >>> a = [1, 2, 3]
  31. >>> b = a[0:2]
  32. >>> b[0] = 4
  33. >>> print(a)
  34. [1, 2, 3]
  1. >>> a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])
  2. # 在某个维度上,取单个数字和取一个区间,会产生不同的结果
  3. >>> row_r1 = a[1, :]
  4. >>> row_r2 = a[1:2, :]
  5. >>> print(row_r1, row_r1.shape)
  6. [5 6 7 8] (4,)
  7. >>> print(row_r2, row_r2.shape)
  8. [[5 6 7 8]] (1, 4)
  9. >>> col_r1 = a[:, 1]
  10. >>> col_r2 = a[:, 1:2]
  11. >>> print(col_r1, col_r1.shape)
  12. [ 2 6 10] (3,)
  13. >>> print(col_r2, col_r2.shape)
  14. [[ 2]
  15. [ 6]
  16. [10]] (3, 1)

取数组的一部分,并产生一个新数据

不同于Slicing,每次产生的是原数组的view,这里每次产生的都是一个新的数组。

  1. >>> import numpy as np
  2. >>> a = np.array([[1,2], [3,4], [5,6]])
  3. >>> print(a.shape)
  4. (3, 2)
  5. # 第1个[0,1,2]是对第1个维度来说,也就取第1个维度的第0,1,2元素,这里就是[1,2] [3,4] [5,6]
  6. # 第2个[0,1,0]是对第2个维度来说,也就是在第一个1维度确定的基础上,分别取第0,1,0个元素
  7. # 所以结果就是,1 4 5
  8. >>> print(a[[0,1,2], [0,1,0]])
  9. [1 4 5]
  10. # 只取1个维度,默认就是第1个维度,所以第1个维度的第 0 1 0个元素就是下面的结果
  11. >>> print(a[[0,1,0]])
  12. [[1 2]
  13. [3 4]
  14. [1 2]]
  15. # a[0,0],第1个0表示取第1个维度的第0个元素,第2个0表示取第2个维度的第0个元素
  16. # a[0,0] 表示取到的是1个数,如果是a[[0], [0]] 表示取到的是一个数组
  17. >>> print(np.array([a[0,0], a[1,1], a[2,0]]))
  18. [1 4 5]
  19. # 如上描述,这里取到的就是一个数组
  20. >>> b = a[[0],[0]]
  21. >>> print(b.shape)
  22. (1,)
  23. # 表示分别取第1个维度的0 0元素,然后在此基础上取第1 1 个元素
  24. >>> print(a[[0,0], [1,1]])
  25. [2 2]
  26. >>> print(np.array([a[0,1], a[0,1]]))
  27. [2 2]

有了上面的解释,下面的这段代码应该不难理解。可以利用另外一个array来选取arrray。

  1. >>> import numpy as np
  2. >>> a = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
  3. >>> print(a)
  4. [[ 1 2 3]
  5. [ 4 5 6]
  6. [ 7 8 9]
  7. [10 11 12]]
  8. >>> b = np.array([0, 2, 0, 1])
  9. >>> print(b)
  10. [0 2 0 1]
  11. >>> print(a[np.arange(4), b])
  12. [ 1 6 7 11]
  13. >>> print(np.arange(4))
  14. [0 1 2 3]
  15. >>> a[np.arange(4), b] += 10
  16. >>> print(a)
  17. [[11 2 3]
  18. [ 4 5 16]
  19. [17 8 9]
  20. [10 21 12]]

布尔类型数组的切片

  1. >>> import numpy as np
  2. >>> a = np.array([[1,2], [3, 4], [5, 6]])
  3. >>> bool_idx = (a > 2)
  4. >>> print(bool_idx)
  5. [[False False]
  6. [ True True]
  7. [ True True]]
  8. # 这只是按照1个维度选,所以结果都是1维的
  9. >>> print(a[bool_idx])
  10. [3 4 5 6]
  11. >>> print(a[a > 2])
  12. [3 4 5 6]

拼接

vstack

hstack

数据类型

通常,numpy会尝试进行推断数据类型,但是也允许你指定数据类型。

  1. >>> import numpy as np
  2. >>> x = np.array([1, 2])
  3. >>> print(x.dtype)
  4. int64
  5. >>> x = np.array([1.0, 2.0])
  6. >>> print(x.dtype)
  7. float64
  8. >>> x = np.array([1.0, 2.0], dtype=np.int64)
  9. >>> print(x.dtype)
  10. int64

数组运算

numpy提供对运算符的重载,因此可以直接使用运算符来进行数组间的运算,也可以使用提供的函数来进行数组间的运算。

数组元素间对应位置的+ - * /

  1. >>> import numpy as np
  2. >>> x = np.array([[1,2],[3,4]], dtype=np.float64)
  3. >>> y = np.array([[5,6],[7,8]], dtype=np.float64)
  4. >>> print(x + y)
  5. [[ 6. 8.]
  6. [10. 12.]]
  7. >>> print(np.add(x,y))
  8. [[ 6. 8.]
  9. [10. 12.]]
  10. >>> print(x - y)
  11. [[-4. -4.]
  12. [-4. -4.]]
  13. >>> print(np.subtract(x, y))
  14. [[-4. -4.]
  15. [-4. -4.]]
  16. >>> print(x * y)
  17. [[ 5. 12.]
  18. [21. 32.]]
  19. >>> print(np.multiply(x, y))
  20. [[ 5. 12.]
  21. [21. 32.]]
  22. >>> print(x / y)
  23. [[0.2 0.33333333]
  24. [0.42857143 0.5 ]]
  25. >>> print(np.divide(x, y))
  26. [[0.2 0.33333333]
  27. [0.42857143 0.5 ]]
  28. >>> print(np.sqrt(x))
  29. [[1. 1.41421356]
  30. [1.73205081 2. ]]

数组间的乘法 dot,向量间的dot相当于是向量的内积;矩阵间或矩阵和向量间,相当于矩阵的乘法。

  1. >>> import numpy as np
  2. >>> x = np.array([[1,2],[3,4]])
  3. >>> y = np.array([[5,6],[7,8]])
  4. >>> v = np.array([9,10])
  5. >>> w = np.array([11, 12])
  6. >>> print(x)
  7. [[1 2]
  8. [3 4]]
  9. >>> print(v.shape) # w,v是列向量
  10. (2,)
  11. >>> print(w.shape)
  12. (2,)
  13. # dot用于两个向量间是求向量的内积,所以这里是9*11+10*12=219
  14. >>> print(v.dot(w))
  15. 219
  16. >>> print(np.dot(v, w))
  17. 219
  18. # x.shape(2,2) v.shape=(2,)
  19. # x.dot(v)可以理解为是x的两个行向量分别于v做dot运算
  20. # 1*9+2*10=29 3*9+4*10=67
  21. >>> print(x.dot(v))
  22. [29 67]
  23. >>> print(np.dot(x, v))
  24. [29 67]
  25. >>> a = np.array([1,2])
  26. >>> print(a.shape)
  27. (2,)
  28. >>> b = np.array([[3,4]])
  29. >>> b.shape
  30. (1, 2)
  31. # a 是一个行向量,在np.dot中可以理解为一个矩阵,维度为(1,2)
  32. # b 是一个矩阵,维度(1,2),所以np.dot(a,b)维度是对不上的,会报错
  33. >>> np.dot(a,b)
  34. Traceback (most recent call last):
  35. File "<stdin>", line 1, in <module>
  36. ValueError: shapes (2,) and (1,2) not aligned: 2 (dim 0) != 1 (dim 0)
  37. # c 是一个矩阵,维度是(2,1),所以np.dot(a,b),结果是一个1x1的矩阵
  38. >>> c = np.array([[1],[2]])
  39. >>> np.dot(a,c)
  40. array([5])
  41. # 1*5+2*7=19 1*6+2*8=22
  42. # 3*5+4*7=43 3*6+4*8=50
  43. >>> print(x.dot(y))
  44. [[19 22]
  45. [43 50]]
  46. >>> print(np.dot(x, y))
  47. [[19 22]
  48. [43 50]]

作用于数组本身

  1. >>> import numpy as np
  2. >>> x = np.array([[1,2],[3,4]])
  3. >>> print(x)
  4. [[1 2]
  5. [3 4]]
  6. >>> print(np.sum(x)) # Compute sum of all elements
  7. 10
  8. >>> print(np.sum(x, axis=0)) # Compute sum of each column
  9. [4 6]
  10. >>> print(np.sum(x, axis=1)) # Compute sum of each row
  11. [3 7]

向量和矩阵的转置

  1. >>> import numpy as np
  2. >>> v = np.array([1,2,3])
  3. >>> print(v)
  4. [1 2 3]
  5. >>> print(v.T)
  6. [1 2 3]
  7. # 向量的转置等于其本身
  8. >>> print(v_t.shape)
  9. (3,)
  10. >>> print(v.T.shape)
  11. (3,)
  12. # 矩阵的转置,注意与向量的区别
  13. >>> m = np.array([[1,2,3]])
  14. >>> print(m.shape)
  15. (1, 3)
  16. >>> print(m.T.shape)
  17. (3, 1)
  18. >>> print(m.T)
  19. [[1]
  20. [2]
  21. [3]]
  22. >>> x = np.array([[1,2], [3,4]])
  23. >>> print(x)
  24. [[1 2]
  25. [3 4]]
  26. >>> print(x.T)
  27. [[1 3]
  28. [2 4]]

广播机制

broadcasting可以简化代码,提高运行速度,通常是在一个大的array和一个小的array间发生。

  1. >>> import numpy as np
  2. # We will add the vector v to each row of the matrix x,
  3. # storing the result in the matrix y
  4. >>> x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
  5. >>> v = np.array([1, 0, 1])
  6. >>> y = np.empty_like(x) # Create an empty matrix with the same shape as x
  7. # Add the vector v to each row of the matrix x with an explicit loop
  8. # 如果没有广播机制,我们需要将x的每一行,都加上v,存入到y的对应行中
  9. >>> for i in range(4):
  10. ... y[i, :] = x[i, :] + v
  11. >>> print(y)
  12. [[ 2 2 4]
  13. [ 5 5 7]
  14. [ 8 8 10]
  15. [11 11 13]]

当x有很多行时,循环是非常低效的,所以,我们想到也许可以用v生成一个与x同型的矩阵,然后通过矩阵加法来实现。

  1. >>> import numpy as np
  2. >>> x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
  3. >>> v = np.array([1, 0, 1])
  4. >>> vv = np.tile(v, (4, 1))
  5. >>> print(vv)
  6. [[1 0 1]
  7. [1 0 1]
  8. [1 0 1]
  9. [1 0 1]]
  10. >>> y = x + vv
  11. >>> print(y)
  12. [[ 2 2 4]
  13. [ 5 5 7]
  14. [ 8 8 10]
  15. [11 11 13]]

广播机制,提供了更简洁的代码和更高效的计算。

  1. >>> import numpy as np
  2. # We will add the vector v to each row of the matrix x,
  3. # storing the result in the matrix y
  4. >>> x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
  5. >>> v = np.array([1, 0, 1])
  6. >>> y = x + v # Add v to each row of x using broadcasting
  7. >>> print(y) # Prints "[[ 2 2 4]
  8. [[ 2 2 4]
  9. [ 5 5 7]
  10. [ 8 8 10]
  11. [11 11 13]]
  12. # 然而,x.shape=(4,3) v.shape=(3,)
  13. # 那么为什么会是行相加呢?
  14. >>> x = np.array([[1,2], [3,4], [5,6]])
  15. >>> print(x.shape)
  16. (3, 2)
  17. >>> v = np.array([1, 0, 1])
  18. >>> y = x + v
  19. Traceback (most recent call last):
  20. File "<stdin>", line 1, in <module>
  21. ValueError: operands could not be broadcast together with shapes (3,2) (3,)

这里有一篇broadcasting机制的官方文档翻译,可以看看。

现在,应该可以把向量(3,)理解为只有1个维度

也就是按照文档里说的,可以出现broadcasting的原则遵循,从后向前,对应位置进行维度匹配:

  1. 维度相等
  2. 有维度为1

例如:

  1. Image (3d array): 256 x 256 x 3
  2. Scale (1d array): 3
  3. Result (3d array): 256 x 256 x 3
  1. A (4d array): 8 x 1 x 6 x 1
  2. B (3d array): 7 x 1 x 5
  3. Result (4d array): 8 x 7 x 6 x 5

反例:

  1. A (1d array): 3
  2. B (1d array): 4 # trailing dimensions do not match
  3. A (2d array): 2 x 1
  4. B (3d array): 8 x 4 x 3 # second from last dimensions mismatched

所以,我们上面,第一个例子能进行broadcasting,是因为遵循了:

  1. # We will add the vector v to each row of the matrix x,
  2. # storing the result in the matrix y
  3. >>> x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
  4. >>> v = np.array([1, 0, 1])
  5. >>> y = x + v # Add v to each row of x using broadcasting
  6. >>> print(y) # Prints "[[ 2 2 4]
  7. [[ 2 2 4]
  8. [ 5 5 7]
  9. [ 8 8 10]
  10. [11 11 13]]
  11. x (2d array): 4 x 3
  12. v (1d array): 3
  13. y (2d array): 4 x 3

而第二个例子,报错,是因为:

  1. >>> x = np.array([[1,2], [3,4], [5,6]])
  2. >>> print(x.shape)
  3. (3, 2)
  4. >>> v = np.array([1, 0, 1])
  5. >>> y = x + v
  6. Traceback (most recent call last):
  7. File "<stdin>", line 1, in <module>
  8. ValueError: operands could not be broadcast together with shapes (3,2) (3,)
  9. x (2d array): 3 x 2
  10. v (1d array): 3
  11. y mismatched
  12. 可以看到,这里最后一个维度是不相等,且没有维度为1的,所以匹配失败,不能broadcasting

{% hint style=”warning” %}
由此,我们可以总结一下numpy中的一维向量
{% endhint %}

由np.array([0, 1, 2])生成,shape为(3,),他其实不是一个向量的理解方式,所以其转置依然等于自身。

维度变换

np.newaxis

  1. a = np.arange(1, 13).reshape(3,4)
  2. b = np.asarray([2, 1 ,1])
  3. print("a shape = ", a.shape)
  4. print(a)
  5. print("\n")
  6. print("b shape = ", b.shape)
  7. print(b)
  8. #print(a-b) 报错,维度不匹配,无法进行broadcasting
  9. c = b[:, np.newaxis]
  10. print("\n")
  11. print("c shape = ", c.shape)
  12. print(c)
  13. print("\n")
  14. print("a-c \n", a-c)
  15. # 结果
  16. a shape = (3, 4)
  17. [[ 1 2 3 4]
  18. [ 5 6 7 8]
  19. [ 9 10 11 12]]
  20. b shape = (3,)
  21. [2 1 1]
  22. c shape = (3, 1)
  23. [[2]
  24. [1]
  25. [1]]
  26. a-c
  27. [[-1 0 1 2]
  28. [ 4 5 6 7]
  29. [ 8 9 10 11]]

变量填充 np.pad

  1. import numpy as np
  2. x = np.array([[1,2], [3,4]])
  3. print(x)
  4. [[1 2]
  5. [3 4]]
  6. # 在第一个维度的前面填充0个0,后面填充1个0,在第二个维度前后都填充0个0
  7. print(np.pad(x, pad_width=((0,1), (0,0)), mode='constant', constant_values=0))
  8. [[1 2]
  9. [3 4]
  10. [0 0]]
  11. # 在第一个维度前后都填充0个0,在第二个维度前后都填充1个0
  12. print(np.pad(x, pad_width=((0,), (1,)), mode='constant', constant_values=0))
  13. [[0 1 2 0]
  14. [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指定。

排序

  1. import numpy as np
  2. import pandas as pd
  3. # =============================================================================
  4. a = np.array([0.8, 0.2, 0.9, 0.1])
  5. a.sort()
  6. # 将a变成排好序的array #不会输出结果 #直接更改array
  7. print(a)
  8. # 结果显示为 [0.1 0.2 0.8 0.9]
  9. # =============================================================================
  10. a = np.array([0.8, 0.2, 0.9, 0.1])
  11. sorted(a)
  12. # 输出结果[0.1 0.2 0.8 0.9] # 但a不会变
  13. print(a)
  14. # 结果依然为[0.8 0.2 0.9 0.1]
  15. # =============================================================================
  16. np.argsort(a)
  17. # 结果为 array([3, 1, 0, 2])
  18. # 这个显示的是,如果排好序,排成[0.1 0.2 0.8 0.9]的样子,这些value在原来的array中的index是多少
  19. # =============================================================================
  20. np.argsort(np.argsort(a))
  21. # 结果为 array([2, 1, 3, 0])
  22. # 这个显示的是,按照原来在array中出现的顺序[0.8, 0.2, 0.9, 0.1],如果排序,各个位置上相应的value在序列中的序号是多少
  23. # =============================================================================
  24. #pd包中类似的
  25. pd.DataFrame(a).rank()
  26. # 结果如下,这个直接是其序号,从1开始,不是像np包中,从0开始
  27. # =============================================================================
  28. # 0
  29. # 0 3.0
  30. # 1 2.0
  31. # 2 4.0
  32. # 3 1.0
  33. # =============================================================================
  34. # =============================================================================
  35. # 上面结果跟下面这个相同
  36. np.argsort(np.argsort(a)) +1
  37. # 结果为 array([3, 2, 4, 1])
  38. # =============================================================================
  39. np.argsort(np.argsort(-a))
  40. # 这个是倒序排序的序号
  41. # =============================================================================
  42. # 排序的另一种方法,正序
  43. a[np.argsort(a)]
  44. # 结果为array([0.1, 0.2, 0.8, 0.9])
  45. # =============================================================================
  46. # 排序的另一种方法,倒序
  47. a[np.argsort(-a)]
  48. # 结果为array([0.9, 0.8, 0.2, 0.1])

Q&A

matrix与array的区别

class ‘numpy.matrixlib.defmatrix.matrix’