Numpy
NumPy是一个开源的Python科学计算基础库,包含:
- 一个强大的N维数组对象
ndarray
- 广播功能函数
- 整合
C
/C++
/Fortran
代码的工具 - 线性代数、傅里叶变换、随机数生成等功能
导入使用:
import numpy as np
N维数组对象:ndarray
ndarray
: 数组(在程序中的别名是:array
)
- 一个多维数组(注:这里的维度是指数据的组织形式):
- 1维数组:1层,eg:
[]
,可用来表示一个向量 - 2维数组:2层,eg:
[[],[],...,[]]
,可用来表示一个矩阵 - N维数组:N层数组,可用来表示多个矩阵
- 1维数组:1层,eg:
- 由两部分构成:
- 实际的数据
- 描述这些数据的元数据(数据维度、数据类型等)
- 元素类型:
- 一般要求为相同(同质)类型
- 也可以由非同质对象构成,此时元素类型为object,无法有效发挥NumPy优势,尽量避免使用
- vs. Python
list
列表:ndarray
对象采用相同的数据类型(有助于节省运算和存储空间)ndarray
对象运算更直接高效(如:可去掉元素间运算所需的循环,使一维向量更像单个数据)
ndarray对象的属性
属性 | 说明 |
---|---|
.ndim |
秩(轴数量或维度数量) |
.shape |
每个维度的尺度,返回一个tuple (eg: (n,) ,(n,m) ,(a,b,c) ) |
.size |
元素个数(=.shape 中n*m*... ) |
.dtype |
元素类型 |
.itemsize |
每个元素的大小,以字节为单位 |
ndarray的元素类型
对比Python语法仅支持整数、浮点数和复数3种类型,ndarray支持更多的元素类型,因为:
- 科学计算涉及数据较多,对存储和性能都有较高要求
- 对元素类型精细定义,有助于NumPy合理使用存储空间并优化性能
数据类型 | 说明 |
---|---|
bool |
布尔类型,True 或False |
intc |
与C语言中的int类型一致,一般是int32 或int64 |
intp |
用于索引的整数,与C语言中ssize_t 一致,int32 或int64 |
int8 |
字节长度的整数,取值:[‐128, 127] |
int16 |
16位长度的整数,取值:[‐32768, 32767] |
int32 |
32位长度的整数,取值:[‐2^31, 2^31‐1] |
int64 |
64位长度的整数,取值:[‐2^63, 2^63‐1] |
uint8 |
8位无符号整数,取值:[0, 255] |
uint16 |
16位无符号整数,取值:[0, 65535] |
uint32 |
32位无符号整数,取值:[0, 2^32‐1] |
uint64 |
64位无符号整数,取值:[0, 2^64‐1] |
float16 |
16位半精度浮点数:1位符号位,5位指数,10位尾数 |
float32 |
32位半精度浮点数:1位符号位,8位指数,23位尾数 |
float64 |
64位半精度浮点数:1位符号位,11位指数,52位尾数 |
complex64 |
复数类型,实部和虚部都是32位浮点数 |
complex128 |
复数类型,实部和虚部都是64位浮点数 |
ndarray数组的创建
函数 | 说明 | 示例 |
---|---|---|
np.array(list/tuple,dtype=None) |
从list/tuple/array等类型创建(不指定dtype 时,将根据数据情况自动关联一个dtype 类型) |
np.array([i for i in range(10)]) |
np.zeros(shape,dtype=float) |
根据shape 生成一个全0 数组(shape : int or tuple) |
np.zeros(shape=(3,5),dtype=int) |
np.ones(shape,dtype=float) |
根据shape 生成一个全1 数组(shape : int or tuple) |
np.ones(shape=(3,5),dtype=int) |
np.full(shape,fill_value,dtype=None) |
根据shape 生成一个全是fill_value 的数组(shape :int or tuple,dtype 默认根据fill_value 的数据类型) |
np.full(shape=(3,5),fill_value=6 |
np.zeros_like(a,dtype=None) |
根据数组a 的形状生成一个全0 数组(dtype 默认根据a 的dtype ) |
np.zeros_like(a) |
np.ones_like(a,dtype=None) |
根据数组a 的形状生成一个全1 数组(dtype 默认根据a 的dtype ) |
np.ones_like(a) |
np.full_like(a,val,dtype=None) |
根据数组a 的形状生成一个全是val 的数组(dtype 默认根据a 的dtype ) |
np.full_like(a,0.1,dtype=float) |
np.arange([start=0,] stop[, step=1,], dtype=None) |
[start,stop-1] 等step 间隔形成一维数组(类似range() ,但可以是浮点数) |
np.arange(0,1,0.2) |
np.linspace(start, stop, num=50,endpoint=True,dtype=None) |
[start,stop] 等间距提取num 个数据形成一维数组(endpoint=True : 默认sample 包括stop ) |
np.linspace(0,20,11) |
np.eye(n,dtype=float) |
创建一个正方的n*n 单位矩阵(对角线为1,其余为0) |
np.eye(3) |
np.random.randint([low=0,] high, size=None, dtype='l') |
[low,high) 随机整数数组(size : int or tuple for output shape),默认生成一个数 |
np.random.randint(5, size=(2, 4)) |
np.random.random(size=None) |
[0.0,1.0) 随机均匀分布的浮点数数组(size : int or tuple for output shape),默认生成一个数 |
np.random.random((3, 2)) |
np.random.normal(loc=0.0, scale=1.0, size=None) |
生成符合loc 均值,scale 方差的随机正态分布的浮点数数组(size : int or tuple for output shape),默认生成一个数 |
np.random.normal(loc=0,scale=10,size=(3,5)) |
np.random.shuffle(a) |
根据数组a的0轴进行随排列(行乱序),改变原数组 | np.random.shuffle(X) |
np.random.permutation(a) |
根据数组a的0轴进行随排列(行乱序),生成一个新数组 | X1=np.random.permutation(X) |
np.random.choice(a, size=None, replace=True, p=None) |
从a(一维数组或int)中以概率p抽取元素,形成size形状新数组 replace表示是否可以重用元素 | np.random.choice(a) |
Samples
np.array
x=np.array([i for i in range(10)]) # array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) y=np.array((1.1, 2, 3.5)) # array([1.1, 2. , 3.5]) X=np.array([ [1, 2], [3, 4] ]) # array([[1, 2],[3, 4]])
.ndim
,.shape
,.size
,.dtype
,.itemsize
# 1, (10,), 10, dtype('int64'), 8 x.ndim, x.shape, x.size, x.dtype, x.itemsize # 1, (3,), 3, dtype('float64'), 8 y.ndim, y.shape, y.size, y.dtype, y.itemsize # 2, (2, 2), 4, dtype('int64'), 8 X.ndim, X.shape, X.size, X.dtype, X.itemsize # 注意: a 与 b 的维度不同 a,b = np.array([1,2,3]), np.array([[1,2,3]]) # 1, (3,), 3 a.ndim,a.shape,a.size # 2, (1, 3), 3 b.ndim,b.shape,b.size
np.zeros
,np.ones
,np.full
# np.zeros(shape,dtype=float) np.zeros(5) # array([0., 0., 0., 0., 0.]) np.zeros(5,dtype=int) # array([0, 0, 0, 0, 0]) np.zeros(shape=(3,2),dtype=int) # array([[0, 0], [0, 0], [0, 0]]) # np.ones(shape,dtype=float) np.ones(5) # array([1., 1., 1., 1., 1.]) np.ones(5,dtype=int) # array([1, 1, 1, 1, 1]) np.ones(shape=(3,2),dtype=int) # array([[1, 1], [1, 1], [1, 1]]) # np.full(shape,fill_value,dtype=None) np.full(5,2) # array([2, 2, 2, 2, 2]) np.full((3,2),2.5) # array([[2.5, 2.5], [2.5, 2.5], [2.5, 2.5]])
np.zeros_like
,np.ones_like
,np.full_like
X # array([[1, 2], [3, 4]]) # np.zeros_like(a,dtype=None) np.zeros_like(X) # array([[0, 0], [0, 0]]) # np.ones_like(a,dtype=None) np.ones_like(X) # array([[1, 1], [1, 1]]) # np.full_like(a,val,dtype=None) np.full_like(X,2.5) # array([[2, 2],[2, 2]]) np.full_like(X,2.5,dtype=float) # array([[2.5, 2.5], [2.5, 2.5]])
np.arange
,np.linspace
# np.arange([start=0,] stop[, step=1,], dtype=None) => [start,stop-1] np.arange(5) # array([0, 1, 2, 3, 4]) np.arange(0,5) # array([0, 1, 2, 3, 4]) np.arange(0,10,2) # array([0, 2, 4, 6, 8]) np.arange(0,5.0) # array([0., 1., 2., 3., 4.]) np.arange(0,1,0.2) # array([0. , 0.2, 0.4, 0.6, 0.8]) # np.linspace(start, stop, num=50,endpoint=True,dtype=None) => [start,stop] np.linspace(0,10,5) # array([ 0. , 2.5, 5. , 7.5, 10. ]) np.linspace(0,10,6) # array([ 0., 2., 4., 6., 8., 10.]) np.linspace(0,10,5,endpoint=False) # array([0., 2., 4., 6., 8.])
np.eye
# np.eye(n,dtype=float) => n*n I np.eye(2) # array([[1., 0.], [0., 1.]])
np.random.randint
,np.random.random
,np.random.normal
,np.random.seed
# np.random.randint([low=0,] high, size=None, dtype='l') => [low, high) 均匀分布的整数 np.random.randint(0,10) # 7 np.random.randint(0,10,size=5) # array([6, 3, 9, 5, 4]) np.random.randint(0,10,size=(2,6)) # array([[0, 5, 5, 2, 2, 6], [5, 3, 5, 7, 4, 3]]) # np.random.random(size=None) => [0.0, 1.0) 均匀分布的浮点数 np.random.random() # 0.35112599596274263 np.random.random(size=5) # array([0.84018916, 0.6768928 , 0.37947955, 0.10752063, 0.42760159]) np.random.random(size=(2,3)) # array([[0.3148762 , 0.87591898, 0.29565754], [0.97317656, 0.58963764, 0.19674398]]) # np.random.normal(loc=0.0, scale=1.0, size=None) => 正态分布的浮点数 np.random.normal() # -1.6000374192847393 : 均值为0,方差为1的一个随机浮点数 np.random.normal(10,100) # -83.71302590581439 : 均值为10,方差为100的一个随机浮点数 np.random.normal(loc=0,scale=10,size=(2,3)) # array([[-5.43979747, 16.91181992, -4.96602424], [-5.8348921 , -8.60388572, -9.39083007]]) # np.random.seed(seed=None) => 生成随机数前设置一个随机种子,保证生成的随机数一样 np.random.seed(55) np.random.randint(10) # 7 np.random.randint(10) # 8 : 未使用相同的seed,生成的随机数不一样 np.random.seed(55) np.random.randint(10) # 7 : 使用相同的seed,生成的随机数一样 np.random.seed(55) np.random.randint(10) # 7 : 使用相同的seed,生成的随机数一样 np.random.seed(55) np.random.randint(100) # 83 : seed相同,范围不同,生成的随机数不一样
np.random.shuffle
,np.random.permutation
,np.random.choice
''' X: array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14]]) ''' np.random.shuffle(X) # 根据数组a的0轴进行随排列 => 行乱序,直接改变原数组 X ''' array([[10, 11, 12, 13, 14], [ 5, 6, 7, 8, 9], [ 0, 1, 2, 3, 4]]) ''' Y=np.random.permutation(X) # 根据数组a的0轴进行随排列 => 行乱序,不改变原数组 ''' array([[10, 11, 12, 13, 14], [ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9]]) ''' np.random.choice(10,(2,3)) # = np.random.randint(0,10,(2,3)) ''' array([[8, 7, 1], [5, 7, 8]]) ''' np.random.choice(5, 3, replace=False, p=[0.1, 0, 0.3, 0.6, 0]) ''' 从 array[0,1,2,3,4] 中按概率p无重复的随机挑出3个(元素1,4取出的概率为0) => array([2, 3, 0]) '''
ndarray数组的基础操作
变换
维度变换
方法 说明 .reshape(shape)
返回一个shape形状的新数组(注:数组元素个数一致) .resize(shape)
与 .reshape(shape)
功能一致,但修改原数组.swapaxes(ax1,ax2)
将数组 n
个维度中两个维度进行调换,返回一个新数组,原数组不变.flatten()
对数组进行降维,返回折叠后的一维数组,原数组不变 元素类型变换
方法 说明 dtype=
np.array(a,dtype=np.int32)
创建时指定astype()
a.astype(np.float)
返回一个新数组
Sample1:维度变换
x=np.arange(10) # array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
# 1. reshape(shape) => 原数组不变
x.reshape(2,5) # array([[0, 1, 2, 3, 4],[5, 6, 7, 8, 9]])
# => Note: 原数组x不变 = array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
x.reshape(2,-1) # array([[0, 1, 2, 3, 4],[5, 6, 7, 8, 9]])
# => Note: x.reshape(3,-1)会出错,因为10不能被3整除
x.reshape(1,-1) # array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]])
# => Note: 生成的是一个新的1*10的二维数组(矩阵),不是一维数组(向量)
# 2. resize(shape) => 原数组改变
x.resize(5,2) # None
x # array([[0, 1],[2, 3],[4, 5],[6, 7],[8, 9]])
# 3. swapaxes(ax1,ax2) => 原数组不变
# 2, (5,2)
x.ndim,x.shape
x.swapaxes(0,1) # array([[0, 2, 4, 6, 8],[1, 3, 5, 7, 9]])
# => 0,1 维度数据交换
# 4. flatten() => 原数组不变
x.flatten() # array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
# => 压缩到一维
Sample2:元素类型变换
a # array([1, 2, 3])
a.dtype # dtype('int64')
# 1. dtype=xxx
c = np.array(a,dtype=np.int32) # array([1, 2, 3], dtype=int32)
# 2. astype(dtype)
d = a.astype(float) # array([1., 2., 3.])
访问
- 索引: 获取数组中特定位置元素
- 每个维度一个索引值,逗号分割
- 切片: 获取数组元素子集
- 每个维度一个切片,逗号分割
- 一个切片使用3元素冒号分割(
起始编号: 终止编号(不含): 步长
)
- 注意:
- 编号
0
开始从左递增,或‐1
开始从右递减 - 切片产生的子数组是原数组的引用(创建与原数组无关的新的子数组可以使用
.copy()
复制一份)
- 编号
Sample1:索引
一维数组使用索引
''' x = np.arange(10) => array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) ''' x[2] # 2 x[-1] # 9
- 二维数组使用索引
''' X = np.arange(10).reshape(3,5) => array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14]]) ''' X[2][2] # 12 X[(2,2)] # 12 X[2,2] # 12 <= 推荐方式
- 二维数组使用索引
Sample2:切片
一维数组使用切片
''' x = np.arange(10) => array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) ''' x[5:9:2] # [5,9),step:2 => array([5, 7]) x[5:9] # [5,9),step:1 => array([5, 6, 7, 8]) x[:5] # [s,5),s=0 => array([0, 1, 2, 3, 4]) x[5:] # [5,e),e=len => array([5, 6, 7, 8, 9]) x[::2] # step:2 => array([0, 2, 4, 6, 8]) x[::-1] # step:-1 => array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) 倒数
二维数组使用切片
''' X = np.arange(10).reshape(3,5) => array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14]]) ''' X[0], X[0,:] # 取第0行 array([0, 1, 2, 3, 4]) X[:,0] # 取第0列 array([ 0, 5, 10]) X[::-1,::-1] # 行列都反转 ''' array([[14, 13, 12, 11, 10], [ 9, 8, 7, 6, 5], [ 4, 3, 2, 1, 0]]) ''' X[:2,::2] # 取前2行,列step为2的元素 ''' array([[0, 2, 4], [5, 7, 9]]) ''' X[:2,:3] # 取前2*3 (注意与X[:2][:3]的区别) ''' array([[0, 1, 2], [5, 6, 7]]) ''' X[:2][:3] # X[:2]数组的前3个元素 ''' X[:2] array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]) X[:2][:3] array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]) '''
切片产生的子数组是原数组的引用
subX=X[:2,:3] # 切片,产生子矩阵数组 ''' array([[0, 1, 2], [5, 6, 7]]) ''' subX[0,0]=100 # 1. 修改子矩阵[0,0]位置的元素值 ''' array([[100, 1, 2], [ 5, 6, 7]]) ''' X # 原数组中[0,0]位置的元素值也改变了 ''' array([[100, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [ 10, 11, 12, 13, 14]]) ''' X[0,0]=50 # 2. 修改原数组[0,0]位置元素值 ''' array([[50, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14]]) ''' subX # 子数组[0,0]位置的元素值也跟着改变了 ''' array([[50, 1, 2], [ 5, 6, 7]]) ''' subX=X[:2,:3].copy() # 3. 使用`.copy()`创建与原数组无关的新的子数组 ''' array([[50, 1, 2], [ 5, 6, 7]]) ''' subX[0,0]=20 # 改变子数组[0,0]位置元素值 ''' array([[20, 1, 2], [ 5, 6, 7]]) ''' X # 原数组不受影响 ''' array([[50, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14]]) '''
合并
np.concatenate((a1, a2, ...), axis=0, out=None)
- 同维度数组合并
- 不同维度需先
reshape
- more:
np.stack(arrays, axis=0, out=None)
: Join a sequence of arrays along a new axis.np.vstack(tup)
: Stack arrays in sequence vertically (row wise).- 垂直方向堆叠,水平方向size需相同(即列size需相同)
- 内部使用
np.concatenate
- 特:可直接合并一维数组(向量)
np.hstack(tup)
: Stack arrays in sequence horizontally (column wise).- 水平方向堆叠,垂直方向size需相同(即行size需相同)
- 内部使用
np.concatenate
np.dstack(tup)
: Stack arrays in sequence depth wise (along third dimension)
Sample1:np.concatenate
一维数组间(向量)合并
x=np.array([1,2,3]) y=np.array([3,2,1]) z=np.array([6,6,6]) np.concatenate([x,y,z]) # 组成一个新的一维向量: ''' array([1, 2, 3, 3, 2, 1, 6, 6, 6]) '''
二维数组间(矩阵)合并
''' A : 2维矩阵(2*3) array([[1, 2, 3], [4, 5, 6]]) ''' np.concatenate([A,A]) # 1. 维度0上合并,组成一个新的4*3矩阵 ''' array([[1, 2, 3], [4, 5, 6], [1, 2, 3], [4, 5, 6]]) ''' np.concatenate([A,A],axis=1) # 维度1上合并,组成2*6矩阵 ''' array([[1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6]]) ''' np.concatenate([A,A],axis=None) # = flatten 压缩成一维 ''' array([1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]) '''
一维数组(向量)合并到二维数组(矩阵)中
''' z : 1维向量 array([6,6,6]) A : 2维矩阵(2*3) array([[1, 2, 3], [4, 5, 6]]) ''' np.concatenate([A,z.reshape(1,-1)]) # concatenate只能合并相同维度的数组,z.reshape成2维后再合并 ''' array([[1, 2, 3], [4, 5, 6], [6, 6, 6]]) '''
Sample2:np.vstack
,np.hstack
np.vstack
''' z : 1维向量 array([6,6,6]) A : 2维矩阵(2*3) array([[1, 2, 3], [4, 5, 6]]) B: 2维矩阵(2*2) array([[1,2], [3,4]]) ''' # 1. 同维数组堆叠 np.vstack([A,A]) # 垂直方向堆叠 A: 2*3矩阵 & A: 2*3矩阵 => 4*3矩阵 ''' array([[1, 2, 3], [4, 5, 6], [1, 2, 3], [4, 5, 6]]) ''' # 2. 不同维数组堆叠 np.vstack([A,z]) # 垂直方向堆叠 A: 2*3矩阵 & z: 1*3向量 => 3*3矩阵 ''' array([[1, 2, 3], [4, 5, 6], [6, 6, 6]]) '''
np.hstack
''' A : 2维矩阵(2*3) array([[1, 2, 3], [4, 5, 6]]) B: 2维矩阵(2*2) array([[1,2], [3,4]]) ''' np.hstack([A,B]) # 水平方向堆叠 => A: 2*3矩阵 & B: 2*2矩阵 => 2*5矩阵 ''' array([[1, 2, 3, 1, 2], [4, 5, 6, 3, 4]]) '''
分割
np.split(ary, indices_or_sections, axis=0)
- more:
np.array_split
: Split an array into multiple sub-arrays of equal or near-equal size. Does not raise an exception if an equal division cannot be made.np.hsplit
: Split array into multiple sub-arrays horizontally (column-wise).np.vsplit
: Split array into multiple sub-arrays vertically (row wise).np.dsplit
: Split array into multiple sub-arrays along the 3rd axis (depth).
Sample1:np.split
一维数组(向量)分割
''' x array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) ''' np.split(x,[3,7]) # 2个分割点:3,7 => 将数组分割成三段 => 生成3个数组 ''' [array([0, 1, 2]), array([3, 4, 5, 6]), array([7, 8, 9])] ''' np.split(x,[5]) # 1个分割点:5 => 将数组分割成两段 => 生成2个数组 ''' [array([0, 1, 2, 3, 4]), array([5, 6, 7, 8, 9])] '''
二维数组(矩阵)分割:
''' A array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15]]) ''' np.split(A,[2]) # 基于维度0,即行,分割 => 2个2*4数组 ''' [ array([[0, 1, 2, 3], [4, 5, 6, 7]]), array([[ 8, 9, 10, 11], [12, 13, 14, 15]]) ] ''' np.split(A,[2],axis=1) # 基于维度1,即列,分割 => 2个4*2数组 ''' [ array([[ 0, 1], [ 4, 5], [ 8, 9], [12, 13]]), array([[ 2, 3], [ 6, 7], [10, 11], [14, 15]]) ] '''
Sample2:np.vsplit
,np.hsplit
np.vsplit
''' A array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15]]) ''' np.vsplit(A,[2]) # 垂直方向分割,分为上下两部分 => 2个2*4数组 ''' [ array([[0, 1, 2, 3], [4, 5, 6, 7]]), array([[ 8, 9, 10, 11], [12, 13, 14, 15]]) ]
np.hsplit
''' x array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) A array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15]]) ''' np.hsplit(x,[3,7]) # 水平方向分割,分为三部分部分 => 3个1维数组 ''' [ array([0, 1, 2]), array([3, 4, 5, 6]), array([7, 8, 9]) ] ''' np.hsplit(A,[2]) # 水平方向分割,分为左右两部分 => 2个4*2数组 ''' [ array([[ 0, 1], [ 4, 5], [ 8, 9], [12, 13]]), array([[ 2, 3], [ 6, 7], [10, 11], [14, 15]]) ] '''
Sample3:应用
'''
data : 4*4矩阵
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
'''
X,Y=np.hsplit(data,[-1]) # 分割出最后一列
'''
X: 4*3矩阵
array([[ 0, 1, 2],
[ 4, 5, 6],
[ 8, 9, 10],
[12, 13, 14]])
Y: 4*1矩阵
array([[ 3],
[ 7],
[11],
[15]])
'''
y=Y[:,0] # 取列(Y的子集,是Y的引用) => 1*4向量
'''
array([ 3, 7, 11, 15])
'''
运算
作用于数组的每一个元素
基本操作符:
+
,-
,*
,/
,//
,%
,**
>
,<
,>=
,<=
,==
,!=
一元函数: 数组中各元素进行对应运算
函数 说明 np.abs(x)
np.fabs(x)
计算数组各元素的绝对值 np.sqrt(x)
计算数组各元素的平方根 np.square(x)
计算数组各元素的平方 np.log(x)
np.log10(x)
np.log2(x)
计算数组各元素的自然对数、10底对数和2底对数 np.ceil(x)
np.floor(x)
计算数组各元素的ceiling值、floor值 np.rint(x)
计算数组各元素的四舍五入值 np.modf(x)
将数组各元素的小数和整数部分以两个独立数组形式返回 np.cos(x)
np.cosh(x)
np.sin(x)
np.sinh(x)
np.tan(x)
np.tanh(x)
计算数组各元素的普通型和双曲型三角函数 np.exp(x)
计算数组各元素的指数值 np.sign(x)
计算数组各元素的符号值, 1(+)
,0
,‐1(‐)
二元函数: 两个数组各元素进行对应运算
函数 说明 np.maximum(x,y)
np.fmax()
np.minimum(x,y)
np.fmin()
元素级的最大值/最小值计算 np.mod(x,y)
元素级的模运算 np.copysign(x,y)
将数组y中各元素值的符号赋值给数组x对应元素
基本运算
+
,-
,*
,/
,//
,%
,**
>
,<
,>=
,<=
,==
,!=
(得到bool数组)
作用于数组的每一个元素(与python的list不同)
与标量
''' X: 3*5 矩阵 array([[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10], [11, 12, 13, 14, 15]]) ''' X+2 ''' array([[ 3, 4, 5, 6, 7], [ 8, 9, 10, 11, 12], [13, 14, 15, 16, 17]]) ''' X-2 ''' array([[-1, 0, 1, 2, 3], [ 4, 5, 6, 7, 8], [ 9, 10, 11, 12, 13]]) ''' X*2 ''' array([[ 2, 4, 6, 8, 10], [12, 14, 16, 18, 20], [22, 24, 26, 28, 30]]) ''' X/2 ''' array([[0, 1, 1, 2, 2], [3, 3, 4, 4, 5], [5, 6, 6, 7, 7]]) ''' X//2 ''' array([[0, 1, 1, 2, 2], [3, 3, 4, 4, 5], [5, 6, 6, 7, 7]]) ''' X%2 ''' array([[1, 0, 1, 0, 1], [0, 1, 0, 1, 0], [1, 0, 1, 0, 1]]) ''' 1/X ''' array([[1. , 0.5 , 0.33333333, 0.25 , 0.2 ], [0.16666667, 0.14285714, 0.125 , 0.11111111, 0.1 ], [0.09090909, 0.08333333, 0.07692308, 0.07142857, 0.06666667]]) ''' X>5 ''' array([[False, False, False, False, False], [False, True, True, True, True], [ True, True, True, True, True]]) '''
数组与数组
''' A: 2*2 矩阵 array([[0, 1], [2, 3]]) B: 2*2 矩阵 array([[10, 10], [10, 10]]) ''' A+B ''' array([[10, 11], [12, 13]]) ''' A-B ''' array([[-10, -9], [ -8, -7]]) ''' A*B ''' array([[ 0, 10], [20, 30]]) ''' A/B ''' array([[0. , 0.1], [0.2, 0.3]]) ''' A>B ''' array([[False, False], [False, False]]) '''
矩阵和向量: 向量和矩阵每一行做运算
''' v: 一维向量 array([1, 2]) A: 2*2 矩阵 array([[0, 1], [2, 3]]) ''' v+A ''' array([[1, 3], [3, 5]]) ''' v-A ''' array([[ 1, 1], [-1, -1]]) ''' v*A ''' array([[0, 2], [2, 6]]) ''' A/v ''' array([[0. , 0.5], [2. , 1.5]]) ''' v>A ''' array([[ True, True], [False, False]]) ''' np.vstack([v]*A.shape[0])+A # = `v+A` ''' A.shape : (2,2) [v]*A.shape[0] : [array([1, 2]), array([1, 2])] np.vstack([v]*A.shape[0]) : 垂直方向上堆叠2个v,生成2*2的矩阵 array([[1, 2], [1, 2]]) => array([[1, 3], [3, 5]]) ''' np.tile(v,(2,1))+A # = `v+A` ''' `np.tile(A,repeats)` np.tile(v,(2,1)) : 将v,行方向上重复2次,列方向上重复1次,形成2*2矩阵 array([[1, 2], [1, 2]]) => array([[1, 3], [3, 5]]) '''
常用运算函数
一元函数
''' X: 3*5 矩阵 array([[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10], [11, 12, 13, 14, 15]]) ''' np.sin(X) ''' array([[ 0.84147098, 0.90929743, 0.14112001, -0.7568025 , -0.95892427], [-0.2794155 , 0.6569866 , 0.98935825, 0.41211849, -0.54402111], [-0.99999021, -0.53657292, 0.42016704, 0.99060736, 0.65028784]]) ''' np.power(3,X) # 3^x , 同 3**X ''' array([[ 3, 9, 27, 81, 243], [ 729, 2187, 6561, 19683, 59049], [ 177147, 531441, 1594323, 4782969, 14348907]]) ''' np.exp(X) # e^x ''' array([[2.71828183e+00, 7.38905610e+00, 2.00855369e+01, 5.45981500e+01, 1.48413159e+02], [4.03428793e+02, 1.09663316e+03, 2.98095799e+03, 8.10308393e+03, 2.20264658e+04], [5.98741417e+04, 1.62754791e+05, 4.42413392e+05, 1.20260428e+06, 3.26901737e+06]]) ''' np.log(X) # ln(X) ''' array([[0. , 0.69314718, 1.09861229, 1.38629436, 1.60943791], [1.79175947, 1.94591015, 2.07944154, 2.19722458, 2.30258509], [2.39789527, 2.48490665, 2.56494936, 2.63905733, 2.7080502 ]]) ''' np.log2(X) # log2(X) ''' array([[0. , 1. , 1.5849625 , 2. , 2.32192809], [2.5849625 , 2.80735492, 3. , 3.169925 , 3.32192809], [3.45943162, 3.5849625 , 3.70043972, 3.80735492, 3.9068906 ]]) ''' np.log10(X) # log10(X) ''' array([[0. , 0.30103 , 0.47712125, 0.60205999, 0.69897 ], [0.77815125, 0.84509804, 0.90308999, 0.95424251, 1. ], [1.04139269, 1.07918125, 1.11394335, 1.14612804, 1.17609126]]) ''' np.sign(X) ''' array([[0, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]) '''
二元函数
''' A: 2*2 矩阵 array([[0, 1], [2, 3]]) B: 2*2 矩阵 array([[10.0, 10.0], [10.0, 10.0]]) v: 一维向量 array([1, 2]) ''' np.minimum(A,B) ''' B是浮点数,所以运算结果为浮点数 array([[0., 1.], [2., 3.]]) ''' np.maximum(A,v) ''' array([[1, 2], [2, 3]]) ''' np.mod(A,B) ''' = A%B array([[0., 1.], [2., 3.]]) ''' np.mode(A,v) ''' = A%v array([[0, 1], [0, 1]]) '''
线性运算
向量
''' v: array([1, 2]) w: array([2, 3]) ''' v.T # 1. 转置:还是1维行向量,无变化 => array([1,2]) v.dot(w) # 2. 点积(v·w): 结果是一个标量(对应元素相乘后相加) 1*2+2*3 => 8
矩阵
''' A: 2*2 矩阵 array([[0, 1], [2, 3]]) B: 2*3 矩阵 array([[0, 1, 2], [3, 4, 5]]) ''' A.T # 1. 转置: 行列交换 ''' array([[0, 2], [1, 3]]) ''' invA = np.linalg.inv(A) # 2. 逆 ''' array([[-1.5, 0.5], [ 1. , 0. ]]) ''' pinvB = np.linalg.pinv(B) # 3. 伪逆: 方阵才有逆,非方阵可使用伪逆(近似逆) ''' array([[-0.77777778, 0.27777778], [-0.11111111, 0.11111111], [ 0.55555556, -0.05555556]]) ''' A.dot(B) # 4. 矩阵乘法(A·B):2*2 · 2*3 => 2*3 ''' array([[ 3, 4, 5], [ 9, 14, 19]]) ''' A.dot(invA) # 5. A · invA => 单位矩阵 ''' array([[1., 0.], [0., 1.]]) ''' B.dot(pinvB) # 6. B · pinvB => 近似单位矩阵:主对角线为1,次对角线几乎为0(浮点误差造成) ''' array([[ 1.00000000e+00, -1.11022302e-16], [ 2.66453526e-15, 1.00000000e+00]]) '''
向量 & 矩阵
''' v: array([1, 2]) A: 2*2 矩阵 array([[0, 1], [2, 3]]) ''' v.dot(A) ''' v:1*2 · A:2*2 : 1*2 => auto transfer to vector, 1 dim => array([4, 7]) ''' A.dot(v) ''' A:2*2 · v:1*2 (auto treat as 2*1) : 2*1 => auto transfer to vector, 1 dim => array([2, 8])
高级
聚合(统计函数)
函数 | 说明 |
---|---|
sum(a, axis=None) |
元素之和 |
min(a, axis=None) max(a, axis=None) |
最小值、最大值 |
average(a, axis=None,weights=None) |
加权平均值(expection) |
ptp(a, axis=None) |
元素最大值与最小值的差 |
median(a, axis=None) |
中位数(中值) |
mean(a, axis=None) |
均值 |
std(a, axis=None) |
标准差 |
var(a, axis=None) |
方差 |
percentile(a,q, axis=None) |
百分位 |
prod(a, axis=None) |
元素乘积 |
Note: axis=None
是统计函数的标配参数
Sample
'''
X=np.arange(16).reshape(4,-1)
=>
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
'''
np.sum(X) # 120
np.sum(X,axis=0) # 维度0上聚合(压缩维度0,即行)=> 每列的和 => array([24, 28, 32, 36])
np.sum(X,axis=1) # 维度1上聚合(压缩维度1,即列)=> 每行的和 => array([ 6, 22, 38, 54])
np.max(X) # 15
np.max(X,aixs=0) # 维度0上聚合 => 每列最大值 => array([12, 13, 14, 15])
np.max(X,axis=1) # 维度1上聚合 => 每行最大值 => array([12, 13, 14, 15])
np.mean(X) # 7.5
np.mean(X,axis=0) # 维度0上聚合 => 每列均值 => array([6., 7., 8., 9.])
np.mean(X,axis=1) # 维度1上聚合 => 每行均值 => array([ 1.5, 5.5, 9.5, 13.5])
np.average(X) # 7.5
np.average(X,axis=0) # 维度0上聚合 => 每列平均值 => array([6., 7., 8., 9.])
np.average(X,axis=0,,weights=[10,5,1,4]) # 维度0上聚合 => 每列加权平均值 => array([3.8, 4.8, 5.8, 6.8])
np.percentile(X,q=25) # 百分位, 25%的元素都是小于等于的值 => 3.75
np.percentile(X,q=50) # = np.median(X) => 7.5
np.percentile(X,q=0) # = np.min(X) => 0.0
np.percentile(X,q=100) # = np.max(X) => 15.0
np.percentile(X,q=[0,25,50,100]) # array([ 0. , 3.75, 7.5 , 15. ])
np.percentile(X,q=50,axis=0) # 维度0上聚合 => array([6., 7., 8., 9.])
np.percentile(X,q=50,axis=1) # 维度0上聚合 => array([ 1.5, 5.5, 9.5, 13.5])
np.prod(X) # 0
np.prod(X,axis=0) # 维度0上聚合 => array([ 0, 585, 1680, 3465])
np.prod(X,axis=1) # 维度1上聚合 => array([ 0, 840, 7920, 32760])
x=np.random.normal(0,1,size=1000000) #[0,1) 正态分布随机浮点一维数组
np.mean(x) # -0.00019733605984642867 <= 均值,非常趋近0
np.std(x) # 1.0001027608240785 <= 标准差,非常趋近1
np.var(x) # 1.000205532207944 <= 方差,非常趋近1
索引函数
函数 | 说明 |
---|---|
argmin(a, axis=None) argmax(a, axis=None) |
计算数组a中元素最小值、最大值所在的索引 |
unravel_index(index, shape) |
将一维索引重塑成多维索引 |
argsort(a, axis=-1, kind='quicksort', order=None) |
元素排好序,对应的索引组成的数组 |
argpartition(a, kth, axis=-1, kind='introselect', order=None) |
元素分区后,对应的索引组成的数组 |
'''
X
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
'''
np.argmin(X) # 扁平化后的,最小值的下标 => 0
np.argmax(X) # 扁平化后的,最大值的下标 => 15
np.argmin(X,axis=0) # 维度0上聚合 => 每列最小值所在的下标 => array([0, 0, 0, 0])
np.unravel_index(12,shape=(4,4)) # (3, 0) <= 将12重塑成多维下标
np.random.shuffle(X) # 对X进行乱序处理
X '''
array([[ 0, 1, 2, 3],
[ 8, 9, 10, 11],
[12, 13, 14, 15],
[ 4, 5, 6, 7]])
'''
np.argsort(x,axis=0) '''
array([[0, 0, 0, 0],
[3, 3, 3, 3],
[1, 1, 1, 1],
[2, 2, 2, 2]])
'''
np.sort(x,axis=0) '''
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
'''
x=np.arange(10) # array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
np.random.shuffle(x) # 对x进行乱序处理
x # array([3, 5, 2, 7, 6, 0, 8, 1, 9, 4])
np.argpartition(x,5) # <5,=5,>5 => array([5, 0, 2, 7, 9, 1, 4, 6, 8, 3])
np.partition(x,3) # <5,=5,>5 => array([0, 3, 2, 1, 4, 5, 6, 8, 9, 7])
np.argpartition(x,[3,7]) # array([5, 7, 2, 0, 9, 1, 4, 3, 8, 6])
np.partition(x,[3,7]) # array([0, 1, 2, 3, 4, 5, 6, 7, 9, 8])
Fancy Indexing
应用:统计判断,方便抽出符合条件的元素 (且返回的数组维度可控)
使用索引数组
''' x = np.arange(16) array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) ''' x_inds=[1,3,7,12] x[x_inds] # array([ 1, 3, 7, 12]) x_inds=np.array([[0,2],[1,3]]) x[x_inds] ''' array([[0, 2], [1, 3]]) ''' ''' X=x.reshape(4,-1) array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15]]) ''' row_inds = np.array([0,1,2]) col_inds = np.array([1,2,3]) X[row_inds,0] # array([0, 4, 8]) X[0,col_inds] # array([1, 2, 3]) X[row_inds,col_inds] # array([ 1, 6, 11]) <= nodes [0,1],[1,2],[2,3]
使用bool值数组(array中的元素进行批量比较,得到bool值数组)
''' x = np.arange(5) array([ 0, 1, 2, 3, 4 ]) ''' x<3 # array([ True, True, True, False, False]) x[x<3] # array([0, 1, 2]) 2*x == 24-4*x # array([False, False, False, False, True]) x[2*x == 24-4*x] # array([4]) x%2==0 # array([ True, False, True, False, True]) x[x%2==0] # array([0, 2, 4]) np.sum(x<3) # 3 np.count_nonzero(x<3) # 3 np.sum((x>2)&(x<4)) # 1 <= 多个条件,使用位运算符 np.sum((x%2==0)|(x>3)) # 3 np.sum(~(x==0)) # 4 np.any(x==0) # True np.any(x<0) # False np.all(x>=0) # True ''' X=x.reshape(4,-1) array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15]]) ''' X<6 ''' array([[ True, True, True, True], [ True, True, False, False], [False, False, False, False], [False, False, False, False]]) ''' X[X<6] # array([0, 1, 2, 3, 4, 5]) X[:,3]%3==0 # array([ True, False, False, True]) <= 每行最后一个元素能否被3整除 X[X[:,3]%3==0,:] # 选出最后一个元素可被3整除的行 ''' array([[ 0, 1, 2, 3], [12, 13, 14, 15]]) ''' np.sum(X%2==0) # 8 <= 统计偶数个数 np.sum(X%2==0,axis=0) # array([4, 0, 4, 0]) <= 每列有多少偶数(行方向上压缩) np.sum(X%2==0,axis=1) # array([2, 2, 2, 2]) <= 每行有多少偶数(列方向上压缩)) np.all(X>3,axis=0) # array([False, False, False, False]) <= 每列(行方向上判断) np.all(X>3,axis=1) # array([False, True, True, True]) <= 每行(列方向上判断)
文件存取
CSV文件存取
np.savetxt(fname,X,fmt='%.18e',delimiter=' ',newline='\n',header='',footer='',comments='# ',encoding=None,)
frame
: 文件、字符串或产生器,可以是.gz
或.bz2
的压缩文件array
: 存入文件的数组fmt
: 写入文件的格式,例如:%d
%.2f
%.18e
delimiter
: 分割字符串,默认是任何空格
np.loadtxt(fname,dtype=<class 'float'>,comments='#',delimiter=None,converters=None,skiprows=0,usecols=None,unpack=False,ndmin=0,encoding='bytes',max_rows=None,)
frame
: 文件、字符串或产生器,可以是.gz
或.bz2
的压缩文件dtype
: 数据类型,可选delimiter
: 分割字符串,默认是任何空格unpack
: 如果True
,读入属性将分别写入不同变量
局限:只能有效存储一维和二维数组
X=np.arange(100).reshape(5,20)
np.savetxt("a.csv",X,fmt='%d',delimiter=",")
'''
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,32,33,34,35,36,37,38,39
40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59
60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79
80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99
'''
Y=np.loadtxt("a.csv",delimiter=",")
'''
array([[ 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., 32.,
33., 34., 35., 36., 37., 38., 39.],
[40., 41., 42., 43., 44., 45., 46., 47., 48., 49., 50., 51., 52.,
53., 54., 55., 56., 57., 58., 59.],
[60., 61., 62., 63., 64., 65., 66., 67., 68., 69., 70., 71., 72.,
73., 74., 75., 76., 77., 78., 79.],
[80., 81., 82., 83., 84., 85., 86., 87., 88., 89., 90., 91., 92.,
93., 94., 95., 96., 97., 98., 99.]])
'''
多维数据的存取
a.tofile(frame, sep='', format='%s')
frame
: 文件、字符串sep
: 数据分割字符串,如果是空串,写入文件为二进制format
: 写入数据的格式
np.fromfile(frame, dtype=float, count=‐1, sep='')
frame
: 文件、字符串dtype
: 读取的数据类型count
: 读入元素个数,‐1
表示读入整个文件sep
: 数据分割字符串,如果是空串,写入文件为二进制
- 注:tofile将array按一维存储,所以读取到后需reshape恢复(可以通过元数据文件来存储数组相关的额外信息)
X=np.arange(100).reshape(2,10,5) # 2*10*5
'''
array([[[ 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, 32, 33, 34],
[35, 36, 37, 38, 39],
[40, 41, 42, 43, 44],
[45, 46, 47, 48, 49]],
[[50, 51, 52, 53, 54],
[55, 56, 57, 58, 59],
[60, 61, 62, 63, 64],
[65, 66, 67, 68, 69],
[70, 71, 72, 73, 74],
[75, 76, 77, 78, 79],
[80, 81, 82, 83, 84],
[85, 86, 87, 88, 89],
[90, 91, 92, 93, 94],
[95, 96, 97, 98, 99]]])
'''
X.tofile("b.txt",sep=",",format="%d") # => 扁平化存储(1维)
'''
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,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99
'''
Y=np.fromfile("b.txt",sep=",")
'''
array([ 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., 32., 33., 34., 35., 36., 37., 38.,
39., 40., 41., 42., 43., 44., 45., 46., 47., 48., 49., 50., 51.,
52., 53., 54., 55., 56., 57., 58., 59., 60., 61., 62., 63., 64.,
65., 66., 67., 68., 69., 70., 71., 72., 73., 74., 75., 76., 77.,
78., 79., 80., 81., 82., 83., 84., 85., 86., 87., 88., 89., 90.,
91., 92., 93., 94., 95., 96., 97., 98., 99.])
'''
Y.reshape(2,10,5) # reshape回原维度
便捷存取
np.save(fname, array, allow_pickle=True, fix_imports=True)
,np.savez(fname, array)
fname
: 文件名,以.npy
为扩展名,压缩扩展名为.npz
array
: 数组变量
np.load(fname,mmap_mode=None,allow_pickle=True,fix_imports=True,encoding='ASCII')
fname
: 文件名,以.npy
为扩展名,压缩扩展名为.npz
X=np.arange(100).reshape(2,10,5) # 2*10*5
np.save("c.npy",X) # 二进制,包含数组元信息
Y=np.load("c.npy") # 可直接恢复成原数组
'''
array([[[ 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, 32, 33, 34],
[35, 36, 37, 38, 39],
[40, 41, 42, 43, 44],
[45, 46, 47, 48, 49]],
[[50, 51, 52, 53, 54],
[55, 56, 57, 58, 59],
[60, 61, 62, 63, 64],
[65, 66, 67, 68, 69],
[70, 71, 72, 73, 74],
[75, 76, 77, 78, 79],
[80, 81, 82, 83, 84],
[85, 86, 87, 88, 89],
[90, 91, 92, 93, 94],
[95, 96, 97, 98, 99]]])
'''