每日分享
You cannot protect yourself from sadness without protecting yourself from happiness.
稳不住幸福,亦躲不过悲伤。
Numpy
1.简单了解Numpy
Numpy
是一个开源的Python科学计算库,用于快速处理任意维度的数组,支持常见的数组和矩阵操作。
使用Numpy需要先安装这个模块。
Numpy
使用 ndarray
对象来处理多维数组,而且 ndarray
中所有元素的类型都是相同的。我们先来看看怎么创建一个 ndarray
:
import numpy as np
np.array(列表)
注意:本文中所使用的np都指 numpy
2.Numpy的优势
2.1内存块的分割
ndarray
在存储数据的时候,数据与数据的地址都是连续的,一体式存储使得批量操作数组元素的时候速度更快。而python中的列表元素类型是任意的,采用分离式存储,这样就使得list只能通过地址方式找到下一个元素。因此 numpy
的 ndarray
在科学计算中大放异彩。
2.2ndarray支持并行化运算(向量化运算)
2.3解除了GIL
numpy
底层使用了C语言编写,内部解除了GIL,其对数组的操作速度不受python解释器的限制,所以其效率远高于纯python代码。
3.N维数组-ndarray
3.1属性
ndarray.shape代表的是数组维度的元组。 1. In [3]: np.array([[1,2,3],[4,5,6]]).shape 2. Out[3]: (2, 3) # 二维数组 ndarray.ndim代表的是数组维数。 1. In [4]: np.array([[1,2,3],[4,5,6]]).ndim 2. Out[4]: 2 ndarray.size代表的是数组中元素数量。 1. In [5]: np.array([[1,2,3],[4,5,6]]).size 2. Out[5]: 6 ndarray.itemsize代表的是一个数组元素的长度(字节)。 1. In [7]: np.array([[1,2,3],[4,5,6]]).itemsize 2. Out[7]: 8 ndarray.dtype代表的是数组元素类型。 1. In [8]: np.array([[1,2,3],[4,5,6]]).dtype 2. Out[8]: dtype('int64') 3.2形状
数组分为一维数组、二位数组、三维数组到N维数组。一维数组类似是线性结构;二维数组则是有两个方向,可以简单的理解为咱们的表;三维数组则可以理解为多张表在另一个方向的叠加。N维数组无法比喻。
3.3类型
我们上面说过了, dtype
属性即元素类型,我们也知道 ndarray
中的元素类型一致,那么类型都有哪些呢?看下面表格:
名称 | 描述 | 简写 |
np.bool | 用一个字节存储的布尔类型(True或False) | 'b' |
np.int8 | 一个字节大小,-128 至 127 | 'i' |
np.int16 | 整数,-32768 至 32767 | 'i2' |
np.int32 | 整数,-2^31 至 2^31 -1 | 'i4' |
np.int64 | 整数,-2^63 至 2^63 - 1 | 'i8' |
np.uint8 | 无符号整数,0 至 255 | 'u' |
np.uint16 | 无符号整数,0 至 65535 | 'u2' |
np.uint32 | 无符号整数,0 至 2^32 - 1 | 'u4' |
np.uint64 | 无符号整数,0 至 2^64 - 1 | 'u8' |
np.float16 | 半精度浮点数:16位,正负号1位,指数5位,精度10位 | 'f2' |
np.float32 | 单精度浮点数:32位,正负号1位,指数8位,精度23位 | 'f4' |
np.float64 | 双精度浮点数:64位,正负号1位,指数11位,精度52位 | 'f8' |
np.complex64 | 复数,分别用两个32位浮点数表示实部和虚部 | 'c8' |
np.complex128 | 复数,分别用两个64位浮点数表示实部和虚部 | 'c16' |
np.object_ | python对象 | 'O' |
np.string_ | 字符串 | 'S' |
np.unicode_ | unicode类型 | 'U' |
我们创建数组的时候可以指定类型:
In [17]: np.array(['python','ethanyan'],dtype=np.string_).dtype Out[17]: dtype('S8')
注意:如果不指定类型,整数默认是 int64
,小数默认是 float64
。
4.基本操作
4.1生成数组
4.1.1生成0和1的数组
np.zeros() np.ones() np.zeros_like() np.ones_like()
以生成0的数组为例:
In [18]: np.zeros([3,4]) Out[18]: array([[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]]) In [19]: np.zeros_like([[1,2],[1,1]]) Out[19]: array([[0, 0], [0, 0]])
可以看到 zeros
中指定数组的形状维度,而 zeros_like
是传入一个数组,仿照其形状生成一个新的数组。
4.1.2从现有的数组中生成
a = np.array([[1,2,3],[4,5,6]]) # 从现有的数组当中创建 a1 = np.array(a) # 相当于索引的形式,并没有真正的创建一个新的 a2 = np.asarray(a)
需要传入一个数组,然后生成一个一模一样的数组。 array
类似于深拷贝, asarray
则类似于浅拷贝。
4.1.3生成固定范围的数组
np.linspace(start,stop,num,endpoint) # start 序列的起始值 # stop 序列的终止值, # num 要生成的等间隔样例数量,默认为50 # endpoint 序列中是否包含stop值,默认为ture np.arange(start,stop, step, dtype) np.logspace(start,stop, num)
linspace
生成等间隔的数组,其中num指定的是生成的数组中有多少个样例。
In [21]: np.linspace(0,100,11) Out[21]: array([ 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.])
arange
生成固定间隔的数组,其中的step指的是间隔大小。
In [22]: np.arange(10,50,2) Out[22]: array([10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42,44, 46, 48])
logspace
生成的则是10的指数的数据。
In [23]: np.logspace(0,2,3) Out[23]: array([ 1., 10., 100.])
其中0,2代表的是从 10^0
到 10^2
也就是1到100。3指的是生成固定间隔的3个样例。
4.1.4生成随机数组
生成均匀分布数组:
np.random.uniform(low, high, size) # low: 采样下界,float类型,默认值为0; # high: 采样上界,float类型,默认值为1; # size: 输出样本数目,为int或元组(tuple)类型。缺省时输出1个值。 # 返回值:ndarray类型,其形状和参数size中描述一致。例如,size=(m,n,k),仿照我们之前看到的维度的元组。
上面的功能是从一个均匀分布左开右闭的区间 [low,high)
中随机采样。
生成正态分布数据:
np.random.normal(loc=0.0, scale=1.0, size=None) # loc:float;此概率分布的均值(对应着整个分布的中心centre) # scale:float;此概率分布的标准差(对应于分布的宽度,scale越大越矮胖,scale越小,越瘦高) # size:int or tuple of ints;输出的shape,默认为None,只输出一个值
4.2数组的索引和切片
我们用一个难一点的例子来说明一下:
In [25]: a1 = np.array([ [[1,2,3],[4,5,6]], [[12,3,34],[5,6,7]]]) In [26]: a1 Out[26]: array([[[ 1, 2, 3], [ 4, 5, 6]], [[12, 3, 34], [ 5, 6, 7]]]) In [27]: a1[0,0,1] Out[27]: 2
注意:获取的时候先行后列,直接通过 []
获取即可。
4.3形状修改
对象.reshape(shape)
# shape代表的是要转换成的数组的形状
# shape可以不指定行或者列,然后通过-1表示。最后的结果计算机自动计算,但是结果必须可以整除,不能来一个1.5行的数组。
# 如:[-1,20]代表的是不知道多少行,但是生成20列。
注意:没有进行行列互换,而是按顺序原先元素顺序重新分组,新产生了一个 ndarray
。
对象.resize()
注意:没有进行行列互换,而且是在原来的 ndarray
上修改。
对象.T
注意:进行了行列互换,而且产生了新的 ndarray
。
4.4类型修改
ndarray.astype(type) ndarray.tostring() ndarray.tobytes()
4.5数组去重
np.unique(ndarray)
例如:
In [28]: np.unique(np.array([[1, 2, 3, 4],[3, 4, 5, 6]])) Out[28]: array([1, 2, 3, 4, 5, 6])
5.ndarray运算
5.1逻辑运算
可以直接通过大于号小于号进行运算,返回的是一个数组,其中符合条件的地方标记为True,不符合条件的地方标记为False。
5.2通用判断函数
判断所有元素是否符合条件,如果符合返回True,反之False。
np.all()
判断其中是否有满足条件的元素,有则返回True。
np.any()
5.3三元运算符
我们可以使用 where
来进行更加复杂的运算。比如我们判断股票的涨跌幅,数据为 temp
,大于0的置为1,否则为0。
np.where(temp >0,1,0)
还可以进行更加复杂的判断。结合两个函数的使用。 np.logical_and
和 np.logical_or
代表的是与和或。
# 判断股票涨跌幅 大于0.5并且小于1的,换为1,否则为0
np.where(np.logical_and(temp >0.5, temp <1),1,0)
# 判断股票涨跌幅 大于0.5或者小于-0.5的,换为1,否则为0
np.where(np.logical_or(temp >0.5, temp <-0.5),1,0)
5.4统计运算
np.max()
是最大。
np.main()
是最小。
np.std
是标准差。
np.mean()
是平均值。
np.argmax()
是数组中最大值的位置。
np.argmin()
是数组中最小值的位置。
注意:在运算的函数里都有一个参数axis,其中0代表列,1代表行。
6.数组间的运算
6.1数组和数的运算
In [29]: arr = np.array([[1,2,3,2,1,4],[5,6,1,2,3,1]]) In [30]: arr + 1 Out[30]: array([[2, 3, 4, 3, 2, 5], [6, 7, 2, 3, 4, 2]]) In [31]: arr / 2 Out[31]: array([[0.5, 1. , 1.5, 1. , 0.5, 2. ], [2.5, 3. , 0.5, 1. , 1.5, 0.5]])
可以看到数组和数能进行计算,而且类似咱们线性代数中的矩阵运算。
6.2数组和数组运算
数组和数组之间的运算符合广播机制。那么什么是广播机制呢?
在进行矩阵运算的时候,我们都知道加法是行列相等的时候才可以进行,而且对应位置元素进行加法运算。进行乘法的时候,m×n 的矩阵乘以 n×1 的向量,得到的是 m×1 的向量。
在数组与数组进行运算的时候,如果两个数组形状不相等,我们可以通过扩展数组的方法来实现相加减等运算,这种机制就是广播机制。但是它也是有原则的人,并不是所有的数组都可以进行运算的。只有符合下面情况,才可以:
1.维度相同,其中有个对应的轴长为1。
给大家举个例子。比如有两个数组,他们的shape分别为(4,3)和(4,1)。或者shape分别为(3,5,6)和(3,1,6)。再或者(1,2,1)和(5,2,6)。他们都符合维度相同,在不同轴上,要么相同,要么对应的轴有一个为1。
2.维度不相同,后缘维度(从末尾开始算起的维度)的轴长相同。
同样举例子说明。两个数组分别为(256,256,3)和(3,),虽然维度不同,但是都从尾部开始算,后一个数组后缘维度的轴长为3,前一个数组也是如此,符合广播机制可以运算。再比如(12,5,2)和(5,2)。
3.前两条可以综合。如(9,1,7,1)和(8,1,5)也符合广播机制,可以运算。
9 x 1 x 7 x 1
8 x 1 x 5
6.3矩阵乘法
矩阵乘法有两个api,分别是:
np.matmul(a,b) # a和b为两个数组
np.dot(a,b)
我们先来演示一下:
In [35]: a = np.array([[80, 86], ...: [82, 80], ...: [85, 78], ...: [90, 90], ...: [86, 82], ...: [82, 90], ...: [78, 80], ...: [92, 94]]) In [36]: b = np.array([[0.7],[0.3]]) In [37]: np.matmul(a,b) Out[37]: array([[81.8], [81.4], [82.9], [90. ], [84.8], [84.4], [78.6], [92.6]]) In [39]: np.dot(a,b) Out[39]: array([[81.8], [81.4], [82.9], [90. ], [84.8], [84.4], [78.6], [92.6]])
可以看到两种方法在计算上面的例子时,结果一样。那么他们有区别吗?答案是肯定的。
区别:
np.matmul
中禁止矩阵与标量的乘法。 在矢量乘矢量的內积运算中, np.matmul
与 np.dot
没有区别。
6.4矩阵应用场景
大部分的机器学习算法都需要用到矩阵。当然也不难理解,算法嘛,为什么很多岗位招的是计算机或者数学,因为算法运用的都是数学知识,计算机像这些模块只是工具,算法数学才是精髓。