数组 (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 的数据结构:
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 开始的,二维数组的索引如下图:
数组切片是原始数组的视图,也就是说对切片的所有操作都是会影响原数组,视图数据并不是原始数据的拷贝,如果要得到切片的副本,需要显示地进行复制,如 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
```python
b = a[-2,1:3] #取倒数第二行,从第二列开始的两列
print(b,b.shape)# 取出的元素的维数也跟着改变了
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')
```python
a = np.array([1.3,0.2], dtype=np.int64)
a
```
array([1, 0], dtype=int64)
```python
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]])
```python
# 用tile函数
a += np.tile(b,(4,1))
a
···
array([[ 17, 18, 19],
[ 20, 21, 23],
[ 24, 71, 104],
[ 19, 70, 115]])
```python
# 其实可以直接这么做
a + b
array([[ 25, 26, 27],
[ 28, 29, 31],
[ 32, 79, 112],
[ 27, 78, 123]])
不同维数的数组可以直接操作
这就是 numpy 数组的广播特性