数组 (ndarray)介绍

这个数组我感觉更像是矩阵,官方文档说它是一个多维数组对象(ndarray)。

NumPy provides an N-dimensional array type, the ndarray, which describes a collection of “items” of the same type. The items can be indexed using for example N integers.

Array的数据结构:

%E5%9B%BE%E7%89%87.png

ndarray 是一个通用的同构数据多维容器,数组中的所有元素类型都必须是相同的。每个数组都有一个shape和一个dtype,分别表示数组的维度和元素的数据类型。

创建数组

直接用np.array()函数将其他序列转换成ndarray数组

输入数据可以是列表、元组、数组等序列类型

import numpy as np
# 列表
a = np.array([1,2,3,4])
a

array([1, 2, 3, 4])
# 元组
b = np.array((1,2,3,4))
b
array([1, 2, 3, 4])
print(np.rank(a))
print(np.ndim(a))
type(a)
1
1


i:\python364\lib\site-packages\ipykernel_launcher.py:1: VisibleDeprecationWarning: `rank` is deprecated; use the `ndim` attribute or function instead. To find the rank of a matrix see `numpy.linalg.matrix_rank`.
  """Entry point for launching an IPython kernel.





numpy.ndarray
  • rank 表示数组的维数
a.shape #查看维数,shape 是个元组,表示数组大小
(4,)

这表示是一维向量,但未指定是行向量还是列向量

  • 可以用reshape函数指定
a = a.reshape((1,-1)) # 前面的1表示一行,后面的-1是占位符,代表4
a.shape
# 行向量
(1, 4)
a = a.reshape((-1,1))
a.shape
# 列向量
(4, 1)
a = a.reshape((2,-1))
print(a)
a.shape
# 两行两列的数组
[[1 2]
 [3 4]]





(2, 2)
  • NumPy 的数组索引是从0开始的
print(a)
print('--------------')
print(a[1, 1])
print('--------------')
a[1,0] = 88
print(a)
[[1 2]
 [3 4]]
--------------
4
--------------
[[ 1  2]
 [88  4]]

还可以用这些函数新建数组

  • ones 创建元素为全1的数组
  • zeros 创建元素为全0的数组
  • full 还可以用full函数实现
  • eye/identity 创建单位矩阵
  • empty 创建新数组,只分配内存,但不填充值
  • ones_like 以另一个数组为参数,根据其形状和数据类型创建元素全1的数组,类似的有zeros_like、empty_like
  • random 创建元素值为(0,1)范围的随机值矩阵
a = np.ones((3,4))
a
array([[1., 1., 1., 1.],
       [1., 1., 1., 1.],
       [1., 1., 1., 1.]])
a = np.zeros((2,3))
a
array([[0., 0., 0.],
       [0., 0., 0.]])
a = np.full((3,4),1)
a
array([[1, 1, 1, 1],
       [1, 1, 1, 1],
       [1, 1, 1, 1]])
a =  np.eye(5)
a
array([[1., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0.],
       [0., 0., 1., 0., 0.],
       [0., 0., 0., 1., 0.],
       [0., 0., 0., 0., 1.]])
a =  np.empty((2,3)) # 元素的值是不定值,不是0哦
a
array([[6.17021380e-042, 8.98497795e-067, 8.32380315e-071],
       [1.22082702e+165, 3.97948961e-315, 1.89124308e+219]])
b = np.ones_like(a)
b
array([[1., 1., 1.],
       [1., 1., 1.]])
b = np.zeros_like(a)
b
array([[0., 0., 0.],
       [0., 0., 0.]])
a =  np.eye(3)
b = np.empty_like(a)
b
array([[5.97915591e-305, 6.01020264e-305, 5.98054658e-305],
       [5.98037274e-305, 5.99198485e-305, 5.98252833e-305],
       [5.96785670e-305, 2.15228723e-280, 5.98586589e-305]])
a =  np.identity(4)
a
array([[1., 0., 0., 0.],
       [0., 1., 0., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.]])

eye identity 都可以创建单位矩阵,除了参数不同之外,二者有何区别?

a = np.random.random((2,3))
a
array([[0.99217117, 0.26712817, 0.27010174],
       [0.30047328, 0.46375106, 0.37945124]])

索引 和切片

数组的索引是从0开始的,二维数组的索引如下图:

%E5%9B%BE%E7%89%87.png

数组切片是原始数组的视图,也就是说对切片的所有操作都是会影响原数组,视图数据并不是原始数据的拷贝,如果要得到切片的副本,需要显示地进行复制,如a[2:3].copy()

a = np.array([
    [1,2,3],
    [4,5,6],
    [7,8,9]
    ])
a
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])
a[1][1]
5
a[1,1]
5
b = a[-2,1:3] #取倒数第二行,从第二列开始的两列
print(b,b.shape)# 取出的元素的维数也跟着改变了
[5 6] (2,)
b = a[-2][1:3] #取倒数第二行,从第二列开始的两列
print(b,b.shape)# 取出的元素的维数也跟着改变了
[5 6] (2,)
b = a[-1:,1:3]
print(b)
b.shape
[[8 9]]





(1, 2)
b = a[-1,1:3]
print(b)
b.shape
[8 9]





(2,)
结果还是有点不同,索引后的数组不小心的话会出现降维的情况
在MATLAB中叫增量运算符,这里有点类似
  • 再看看花式索引(用整数数组进行索引)

和切片不同,它是复制数据到新数组

# 还可以对数组元素批量操作
a = np.arange(32).reshape((8,4))
print(a)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]
 [24 25 26 27]
 [28 29 30 31]]
np.arange(3)
array([0, 1, 2])
a[[0,1,2],1]
array([1, 5, 9])
a[[1,5,7,2],[0,1,3,2]]

# 这样选取的是行列交叉点的值
array([ 4, 21, 31, 13])
a[[1,5,7]][:,[0,2,3]]
# 这样选取的是行列矩形区域的值
array([[ 4,  9,  7],
       [20, 22, 23],
       [28, 30, 31]])
  • 还可以使用np.ix函数生成索引器
a[np.ix_([1,5,7], [0,2,3])]
array([[ 4,  9,  7],
       [20, 22, 23],
       [28, 30, 31]])
# 对第二列数据加三
a[np.arange(3),1] += 3
a
array([[ 0, 16,  2,  6],
       [ 4, 17,  9,  7],
       [ 8, 27, 13, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23],
       [24, 25, 26, 27],
       [28, 29, 30, 31]])
# 还可以这样写
a[np.arange(3),[1,1,1]] += 3
a
array([[ 0, 19,  2,  6],
       [ 4, 20,  9,  7],
       [ 8, 30, 13, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23],
       [24, 25, 26, 27],
       [28, 29, 30, 31]])
# 那这样呢?应该看出规律来了
a[np.arange(3),[1,2,1]] += 3
a
array([[ 0, 22,  2,  6],
       [ 4, 20, 12,  7],
       [ 8, 33, 13, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23],
       [24, 25, 26, 27],
       [28, 29, 30, 31]])
# 还能这样子
a[[0,2,2],[3,2,1]] += 3
a
array([[ 0, 22,  2,  9],
       [ 4, 20, 12,  7],
       [ 8, 36, 16, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23],
       [24, 25, 26, 27],
       [28, 29, 30, 31]])
  • 再来看看布尔型索引
# 还可以这样筛选
b = a > 10
b
array([[False,  True, False, False],
       [False,  True,  True, False],
       [False,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True]])
#把这些数取出来
a[b]
array([22, 20, 12, 36, 16, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
       23, 24, 25, 26, 27, 28, 29, 30, 31])
a[a>10]
array([22, 20, 12, 36, 16, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
       23, 24, 25, 26, 27, 28, 29, 30, 31])

ndarray的数据类型

可以将整个数组理解成是一整块内存,而dtype说明了这块内存的数据类型是什么。

在考虑需要控制数据在内存或磁盘中的存储方式时,了解dtype就有必要了

a = np.array([1,0.2])
a.dtype
dtype('float64')
a = np.array([1,2,3])
a.dtype
dtype('int32')
a = np.array([1.3,0.2], dtype=np.int64)
a
array([1, 0], dtype=int64)
a = np.array([1,0.2])
b = np.array(a,np.int64)
b
array([1, 0], dtype=int64)
  • 还可以用astype函数转换数据类型
a = np.array([1,0.2])
b = a.astype(np.int64)
b
array([1, 0], dtype=int64)

运算

标量运算

大小相等的数组之间的算术运算,都是元素之间对应相加减乘除,运算单位是数组,批量运算。这点和MATLAB也很相似。

  • 加减乘除
a = np.array([
    [1,2,3],
    [4,5,6]
])

b = np.array([
    [7,8,9],
    [10,11,0]
])
a + b
array([[ 8, 10, 12],
       [14, 16,  6]])
np.add(a,b)
array([[ 8, 10, 12],
       [14, 16,  6]])
a-b
array([[-6, -6, -6],
       [-6, -6,  6]])
np.subtract(a,b)
array([[-6, -6, -6],
       [-6, -6,  6]])
a*b
array([[ 7, 16, 27],
       [40, 55,  0]])
np.multiply(a,b)
array([[ 7, 16, 27],
       [40, 55,  0]])
a/b
i:\python364\lib\site-packages\ipykernel_launcher.py:1: RuntimeWarning: divide by zero encountered in true_divide
  """Entry point for launching an IPython kernel.





array([[0.14285714, 0.25      , 0.33333333],
       [0.4       , 0.45454545,        inf]])
np.divide(a,b)
i:\python364\lib\site-packages\ipykernel_launcher.py:1: RuntimeWarning: divide by zero encountered in true_divide
  """Entry point for launching an IPython kernel.





array([[0.14285714, 0.25      , 0.33333333],
       [0.4       , 0.45454545,        inf]])
1/a
array([[1.        , 0.5       , 0.33333333],
       [0.25      , 0.2       , 0.16666667]])
a ** 0.5
array([[1.        , 1.41421356, 1.73205081],
       [2.        , 2.23606798, 2.44948974]])

除零会有警告

矩阵运算

a = np.array([
    [1,2,3],
    [4,5,6]
])

b = np.array([
    [1,3,5],
    [2,4,8],
    [9,8,7]
])
# 矩阵乘法
a.dot(b)
array([[ 32,  35,  42],
       [ 68,  80, 102]])
np.dot(a,b)
array([[ 32,  35,  42],
       [ 68,  80, 102]])
# 矩阵转置
print(a)
a.T
[[1 2 3]
 [4 5 6]]





array([[1, 4],
       [2, 5],
       [3, 6]])
np.transpose(a)
array([[1, 4],
       [2, 5],
       [3, 6]])
# 矩阵的內积
a.dot(a.T)
array([[14, 32],
       [32, 77]])

常用函数

  • sqrt()
# 求根
np.sqrt(a)
array([[1.        , 1.41421356, 1.73205081],
       [2.        , 2.23606798, 2.44948974]])
  • power()
# 求幂
np.power(a,3)
array([[  1,   8,  27],
       [ 64, 125, 216]], dtype=int32)
a
array([[1, 2, 3],
       [4, 5, 6]])
  • sum()
# 对所有元素求和

np.sum(a)
21
np.sum(a, axis=0) #对没一列元素求和
array([5, 7, 9])
np.sum(a, axis=1) #对没一行元素求和
array([ 6, 15])
# 类似的还有mean函数
np.mean(a)
3.5
  • mean()
np.mean(a,axis=0)
array([2.5, 3.5, 4.5])
np.mean(a,axis=1)
array([2., 5.])
  • uniform()
np.random.random() #(0,1)之内
0.7807819763980599
np.random.uniform(50,70) # 指定范围
61.458442560252266
np.random.uniform(50,70,(2,3)) #指定维数
array([[59.88385648, 50.59647127, 60.02660835],
       [55.62225737, 54.49158301, 68.14669151]])
  • tile
a
array([[1, 2, 3],
       [4, 5, 6]])
np.tile(a,(1,3)) #将a作为结构元素,重复3次,形成新数组
array([[1, 2, 3, 1, 2, 3, 1, 2, 3],
       [4, 5, 6, 4, 5, 6, 4, 5, 6]])
np.tile(a,(3,1))
array([[1, 2, 3],
       [4, 5, 6],
       [1, 2, 3],
       [4, 5, 6],
       [1, 2, 3],
       [4, 5, 6]])
np.tile(a,(3,4))
array([[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3],
       [4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6],
       [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3],
       [4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6],
       [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3],
       [4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6]])
  • argsort()
a = np.array([[100, 20, 300],
       [400, 110, 60]])
np.argsort(a) # 默认是进行行排序,返回数组索引
array([[1, 0, 2],
       [2, 1, 0]], dtype=int32)
np.argsort(a,axis=0)
array([[0, 0, 1],
       [1, 1, 0]], dtype=int32)

广播

不同大小数组之间的运算叫广播

a = np.array([
    [1,2,3],
    [4,5,7],
    [8,55,88],
    [3,54,99]
])
b = np.array([
    [8,8,8]
])

如果要实现a的每一行的元素都加上b,可以这么做:

# 循环
for i in range(4):
    a[i:i+1,:] += b #注意维数要一致
a
array([[  9,  10,  11],
       [ 12,  13,  15],
       [ 16,  63,  96],
       [ 11,  62, 107]])
# 用tile函数
a += np.tile(b,(4,1))
a
array([[ 17,  18,  19],
       [ 20,  21,  23],
       [ 24,  71, 104],
       [ 19,  70, 115]])
# 其实可以直接这么做
a + b
array([[ 25,  26,  27],
       [ 28,  29,  31],
       [ 32,  79, 112],
       [ 27,  78, 123]])

不同维数的数组可以直接操作

这就是numpy数组的广播特性