一个用python实现的科学计算,包括:1、一个强大的N维数组对象Array;2、比较成熟的(广播)函数库;3、用于整合C/C++和Fortran代码的工具包;4、实用的线性代数、傅里叶变换和随机数生成函数。numpy和稀疏矩阵运算包scipy配合使用更加方便。
NumPy(Numeric Python)提供了许多高级的数值编程工具,如:矩阵数据类型、矢量处理,以及精密的运算库。专为进行严格的数字处理而产生。多为很多大型金融公司使用,以及核心的科学计算组织如:Lawrence Livermore,NASA用其处理一些本来使用C++,Fortran或Matlab等所做的任务。---《百度百科》
pip install numpy
import numpy as np
a = np.array([1, 2, 3, 4, 5])
b = np.arange(0, 6, 1)
c = np.random.random((3, 3))
d = np.random.randint(0, 9, size=(3, 3))
print("a:", a)
print("b:", b)
print("c:", c)
print("d:", d)
①np.array直观创建数组,输入是啥生成就是啥。
②np.arange创建数组,需要给定起始值、终止值的后一位数字、步长。
③np.random.random创建数组,需要给定数组的维度,其元素是0-1之间的随机数。
④np.random.randint创建一个N阶数组,需要给定元素属于的区间、数组的维度。元素是整数。
a: [1 2 3 4 5]
b: [0 1 2 3 4 5]
c: [[0.10739085 0.99541616 0.76174493]
[0.30140398 0.87467374 0.30959958]
[0.23803194 0.47848497 0.38842102]]
d: [[2 0 4]
[7 0 7]
[3 4 5]]
zeros = np.zeros((1, 4))
ones = np.ones((2, 2))
full = np.full((2, 3), 9)
eye = np.eye(3)
①np.zeros生成指定维数,元素全为0的数组
②np.ones生成指定维数,元素全为1的数组
③np.full需要指定数组的维度以及填充数组的数字
④np.eye生成N阶数组,除了对角线元素为1,其它元素为0
zero: [[0. 0. 0. 0.]]
ones: [[1. 1.]
[1. 1.]]
full [[9 9 9]
[9 9 9]]
eye: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
注意:数组中的数据类型必须是一致的,包括整型以及浮点型。
数据类型 | 描述 | 唯一标识符 |
bool | 用一个字节存储的布尔类型(True或False) | b |
int8 | 一个字节大小,-128 至 127 | i1 |
int16 | 整数,16 位整数(-32768 ~ 32767) | i2 |
int32 | 整数,32 位整数(-2147483648 ~ 2147483647) | i4 |
int64 | 整数,64 位整数(-9223372036854775808 ~ 9223372036854775807) | i8 |
uint8 | 无符号整数,0 至 255 | u1 |
uint16 | 无符号整数,0 至 65535 | u2 |
uint32 | 无符号整数,0 至 2 ** 32 - 1 | u4 |
uint64 | 无符号整数,0 至 2 ** 64 - 1 | u8 |
float16 | 半精度浮点数:16位,正负号1位,指数5位,精度10位 | f2 |
float32 | 单精度浮点数:32位,正负号1位,指数8位,精度23位 | f4 |
float64 | 单精度浮点数:64位,正负号1位,指数11位,精度52位 | f8 |
complex64 | 复数,分别用两个32位浮点数表示实部和虚部 | c8 |
complex128 | 复数,分别用两个64位浮点数表示实部和虚部 | c16 |
object_ | python对象 | U |
string_ | 字符串 | S |
unicode_ | unicode类型 | U |
①创建数组指定数据类型
zeros = np.zeros((1, 4), dtype='int16')
ones = np.ones((2, 2), dtype='float32')
zero: [[0 0 0 0]]
ones: [[1. 1.]
[1. 1.]]
②查询数据类型
full = np.full((2, 3), 9, dtype='int8')
eye = np.eye(3, dtype='float16')
print(full.dtype)
print(eye.dtype)
int8
float16
③修改数据类型
full = np.full((2, 3), 9, dtype='int8')
print(full.dtype)
full = full.astype('int16')
print(full.dtype)
int8
int16
注意:
1.Numpy是基于C语言编写的,引用了C语言的数据类型。
2.针对不同的数据赋予不同的数据类型,可以有效节省空间。
(大白话来说,第一个出现的数字前面有多少个'[',那么这个数组就是几维的。)
①定义1维、2维、3维数组
arr1 = np.array([1,2,3]) # 一维数组
arr2 = np.array([[1,2,3],[4,5,6]]) # 二维数组
arr3 = np.array([ # 三维数组
[
[1,2,3],
[4,5,6]
],
[
[7,8,9],
[10,11,12]
]
])
②数组维度查询
print(arr1.shape)
print(arr2.shape)
print(arr3.shape)
③修改数组形状
a1 = np.array([ # 创建新数组a1
[
[1,2,3],
[4,5,6]
],
[
[7,8,9],
[10,11,12]
]
])
a2 = a1.reshape((2,6)) # 保持元素个数不变的情况下,修改形状为2*6
a3 = a2.flatten() # 铺平为一维向量
④查看元素个数与所占内存
print(a1.size)
print(a1.itemsize)
print(a1.itemsize * a1.size)
其中:
1.a1.size代表数组元素个数
2.a1.itemsize代表单个元素所占的内存,单位是字节
3.两者相乘代表数组一共所占用的内存
①一维数组
a1 = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
print(a1[4]) # 索引操作
print(a1[4:6]) # 切片操作
print(a1[::2]) # 使用步长
print(a1[-1]) # 使用负数作为索引(从右往左数第一位数字)
[0 1 2 3 4 5 6 7 8 9]
4
[4 5]
[0 2 4 6 8]
9
②二维数组
arr2 = np.random.randint(0,10,size=(4,6))
print(arr2[0]) # 获取第0行数据
print(arr2[1:3]) # 获取第1,2行数据
print(arr2[[0, 2, 3]]) # 获取0,2,3行数据
print(arr2[2, 1]) # 获取第二行第一列数据
print(arr2[[1, 2], [4,5]]) # 获取多个数据 例:第一行第四列、第二行第五列数据
print(arr2[1:3, 4:6]) # 获取多个数据 例:第一、二行的第四、五列的数据
print(arr2[:, 1]) # 获取某一列数据 例:第一列的全部数据
print(arr2[:, [1,3]]) # 获取多列数据 例:第一、三列的全部数据
③布尔索引
a3 = np.arange(24).reshape((4,6))
print(a3[a3<10]) # 挑选出小于10的元素
print(a3[(a3 < 5) | (a3 > 10)]) # 挑选出小于5或者大于10的元素
说明:
1.布尔索引是通过相同数据上的True还是False来进行提取的
2.同时满足用&,满足其一即可用|
3.有多个条件时,每个条件用圆括号括起来
①索引
a3 = np.random.randint(0,10,size=(3,5))
a3[1] = 0 # 将第一行数据全部更换为0
a3[1] = np.array([1,2,3,4,5]) # 将a3数组第一行数据更换为[1,2,3,4,5]
②条件索引
a3[a3 < 3] = 1 # 数组中值小于3的元素全部替换为1
③函数(用where函数来实现替换值)
result = np.where(a3<5,0,1)
代码的作用是a3数组中小于5的值全部更换为0,其余的元素更换为1
数组的广播原则:如果两个数组的后缘维度(即从末尾开始算起的维度)的轴长度相符或其中一方的长度为1,那么他们是广播兼容的。广播会在全是和(或)长度为1的维度上进行。
①数组与数字运算
a1 = np.random.randint(0,5,size=(3,5))
print(a1*2) # 数组中的所有元素都乘2
print(a1.round(2)) # 数组中所有的元素只保留2位小数
②数组与数组运算
a1 = np.random.randint(0,5,size=(3,5))
a2 = np.random.randint(0,5,size=(3,5)) # a1+a2满足数组广播机制:形状一致
a3 = np.random.randint(0,5,size=(3,4)) # a1+a3不满足:形状不一致的数组不能相加减
a4 = np.random.randint(0,5,size=(3,1)) # a1+a4满足:俩数组行数相同,其中一个数组列数为1
a5 = np.random.randint(0,5,size=(1,5)) # a1+a5满足,俩数组列数相同,其中一个数组行数为1
总结:
1.数组可以直接和数字进行运算
2.两个shape想要的数组是可以进行运算的
3.如果两个shape不同的数组想要进行运算,那要看看二者满不满足广播原则
①数组形状的改变
a1 = np.random.randint(0,10,size=(3,4))
a2 = a1.reshape((2,6)) # 有返回
a1.resize((4,3)) # 无返回
reshape和resize的区别:
reshape和resize都是用来修改数组形状的,但是结果不一样。reshape是将数组转换为指定的形状,然后返回转换后的结果。resize是将数组转换为指定的形状,会直接修改数组本身,并且不会返回任何值。
②flatten与ravel(都是将多维数组转换为一维数组,但是方式不一样)
a3 = np.random.randint(0,10,size=(3,4))
a4 = a3.flatten() # 拷贝一份返回
a5 = a3.ravel() # 返回这个视图的引用
也就是说:
修改a4的值不会对a3造成影响;但是修改a5的值会一并修改a3的值。
③数组的叠加
vstack:代表在垂直方向叠加,想要叠加成功必须满足列数一致;
hstack:代表在水平方向叠加,要想叠加成功必须满足行数一致;
concatenate:可以手动指定axis参数具体在哪个方向叠加。
1>axis = 0代表在水平方向叠加
2>axis = 1代表在垂直方向叠加
3>axis = None代表先进行叠加,再转化为1维数组
vstack1 = np.random.randint(0,10,size=(3,4)) # 垂直方向待叠加的数组
vstack2 = np.random.randint(0,10,size=(2,4)) # 垂直方向待叠加的数组
vstack3 = np.vstack([vstack1,vstack2]) # 垂直叠加方法一
vstack4 = np.concatenate([vstack1,vstack2],axis=0) # 垂直叠加方法二
h1 = np.random.randint(0,10,size=(3,4)) # 水平方向待叠加的数组
h2 = np.random.randint(0,10,size=(3,1)) # 水平方向待叠加的数组
h3 = np.hstack([h2,h1]) # 水平叠加方法一
h4 = np.concatenate([h2,h1],axis=1) # 水平叠加方法二
h5 = np.concatenate([h2,h1],axis=None) # 先识别垂直或者水平叠加,后转换为一维数组
④数组的切割
hsplit:代表在水平方向切割,按列进行切割。切割方式如下:
1.直接指定平均切割成多少列
2.指定切割的下标值
vsplit:代表在垂直方向切割,按行进行切割。切割方式与hsplit相同。
hs1 = np.random.randint(0, 10, size=(3, 4))
np.hsplit(hs1, 2) # 水平方向平均分为2份,要求列数可被此整数整除
np.hsplit(hs1, (1, 2)) # 水平方向分为1,1,2列(在1,2处切割)
[array([[9, 4],
[4, 2],
[4, 7]]), array([[4, 6],
[9, 6],
[7, 3]])]
[array([[9],
[4],
[4]]), array([[4],
[2],
[7]]), array([[4, 6],
[9, 6],
[7, 3]])]
vs1 = np.random.randint(0, 10, size=(4, 5))
np.vsplit(vs1, 4) # 垂直方向平均分为4份
np.vsplit(vs1, (1, 3)) # 垂直方向分为1,2,1行
[array([[0, 3, 7, 4, 7]]), array([[0, 1, 2, 1, 1]]), array([[9, 8, 4, 7, 5]]), array([[9, 8, 2, 7, 1]])]
[array([[0, 3, 7, 4, 7]]), array([[0, 1, 2, 1, 1],
[9, 8, 4, 7, 5]]), array([[9, 8, 2, 7, 1]])]
⑤矩阵转置
t1 = np.random.randint(0,10,size=(3,4))
t1.T # 数组t1转置
t2 = t1.transpose() # 返回的是一个view,对返回值上进行修改会影响到原来的数组
①如果只是简单的赋值,那么就不会进行拷贝
a = np.arange(12)
b = a
print(b is a) # 返回为True,说明b和a是相同的
②浅拷贝
有些情况,会进行变量的拷贝,但他们所指向的内存空间都是一样的,那么这种情况叫做浅拷贝,或者叫做View(视图)
c = a.view()
print(c is a) # 返回false,说明c和a栈区空间不同,但是所指向的内存空间是一样的
c[0] = 100 # 修改c的值,a也会受到影响
③深拷贝
将之前数据完完整整的拷贝一份放到另外一块内存空间中,这样就是两个完全不同的值了。
d = a.copy()
print(d is a) # 返回false,说明在不同栈区
d[1]=200 # 数组d被修改,而a原封不动,说明两者内存空间不一样。
总结:
在数组操作中分成三种拷贝:
1.不拷贝:直接赋值,那么栈区没有拷贝,只是用同一个栈区定义了不同的名称。
2.浅拷贝:只拷贝栈区,栈区指定的堆区并没有拷贝。
3.深拷贝:栈区和堆区都拷贝
未完待续~