译者的话
Python的内置数据结构List也可被当做数组来使用,但由于列表的元素可是任何的对象,因此列表保存的是对象的指针。如此,比如[1,2,3]这样的数组,用List这样的数据结构需要三个指针和3个整数对象。我们发挥点想象力,在现代动辄要求亿级的数量级运算中,这显然既浪费内存更浪费cpu的计算时间。
Python中提供了array数据结构,它直接保存数值,但它不支持多维,也没有各种运算函数(这意味着人还需要花费一部分精力放在程序的设计与实现上,而不能全神贯注于需解决的问题),因此也并不适合做数值计算。
我还不敢担保Numpy克服了第一段所提到的缺点,但对于第二点,它显然做得很好了。不过!这对于普通使用者来说,能使用亿p数量级的数据集已经很少见了。
译文正式开始
在Python的众多包中,Numpy简直就是个大杂院,比如它可以被用于数据分析、机器学习以及科学计算。这是因为Numpy大大简化了使用者操纵、计算向量以及矩阵的过程。
事实上,一些主流的Python包也将Numpy作为其基础架构中的一部分,比如scikit-learn, SciPy, pandas, 以及tensorflow。在使用这些包时,精通Numpy不仅可让你切片数字型数据,而且会让你在处理、调试实际的复杂情况时显得游刃有余。
为使我们可以在机器学习模块(machine learning models)驾驭Numpy,我们会先介绍一些使用Numpy的主要方式以及它是如何呈现不同类型数据的(表格、图片、文本等等)。
#读者注意:使用import语句导入一个包创造的是一个命名空间
#只有如此,下面的各种函数(方法)才能被用起来
#否侧会出现NameError:name 'one of Numpy's functions' is not defined
#总之在以后的使用中也要遵循此必须要走的流程。
import numpy as np
创建数组(array)
使用np.array()函数以及用Python内置的数据结构list作为参数,我们就创建了一个Numpy数组了(啊哈!这是强大的N维数组!)。在这个例子,Python造的是下面这个数组,图例在右边。译者注:在实际的应用中,一般会给这个被创造的对象左边加一个名称(name),比如下面的data=np.array([1,2])。
以上便是给Numpy数组赋予初始值的基本方法.当然,Numpy还为此提供了其他方法,比如ones(),zeros(),以及random.random(),我们提供一个多大的数字参数,就可以建一个有多少元素的Numpy数组,如下图:
一旦我们建了数组,就可以开始用一些有趣的方式操纵这个它了!
数组的算数运算
我们先建两个数组来展示数组的加减乘,一个叫data,另一个叫ones:
加法
Numpy数组的加法规则是:把每一行位置对应的数值相加,使用操作符‘+’。
当我开始学习使用这些工具时,我发现这又是一层抽象,它使我不用去关注实现此种计算的循环程序,我就能够站在更高的角度去想怎么解决我的问题。
#译者注:在此用代码说明下使用循环的程序
#实际上,使你站在更高的角度思考问题不能说明Numpy这个包的此方法就是好的,归根到底还是要考虑时间和空间复杂度
#比如我给出的这个加法算法,其时间复杂度是O(n**2)级别,空间复杂度是O(n)也期待读者给出更好的实现方式。
#euqal to '+'
La = [1,2,3,4]
Lb = [5,6,7,8]
j = 0
Lc = []
for i in range(0,len(La)):#时间复杂度是O(n)
add = (La[i] + Lb[j])
Lc.append(add) #时间复杂度是O(n)
j += 1
print(Lc)
#乘法
#时间复杂度和空间复杂度同上
L0 = [1,2,3,4]
L1 = [5,6,7,8]
j = 0
L2 = []
for i in range(0,len(La)):
multi = L0[i] * L1[j]
L2.append(multi)
j += 1
print(L2)
#关于减法和除法就不再介绍
并且我还可以很方便地实现减、乘、除:
我们也可以在数组和数字之间实现加减乘除地运算,这就好比向量和标度之间地运算(如果大家相对向量有更深入地了解,请刷这个视频集,(https://www.bilibili.com/video/av44855426from=search&seid=17812337730266789752)。举个例子,一个数组代表了以英尺为单位的距离,我们想将其转换成以千米为单位的距离,只需简单的:data*1.6即可,如图:
从Numpy的角度去理解此次操作前的各种操作,也就不难理解为什么此次的乘法中每一个小单元格里的数都要进行一次乘法运算了。
索引
凡是适用于Pytho内置数据结构list的索引方式都可以被用在Numpy数组的索引上。
聚合函数
Numpy还给了我们一些其他便利,比如提供了聚合函数:
除了以上的最小值(min)、最大值(max)、和(sum),你也可以用mean函数计算出算术平均值(mean),prod函数则可让你得出所有数组元素的连乘,std函数让你得到数组元素的标准差,当然还有其他函数了,有需要的可以看看这个链接https://jakevdp.github.io/PythonDataScienceHandbook/02.04-computation-on-arrays-aggregates.html
更高维度的数组
以上所有例子适用的数组都可以被看做是一维向量,但Numpy的关键之处在于可以帮助我们处理任意维度大小的数组。
#译者注:Numpy对维度的定义不同与我们平时上课学到的向量对维度的定义
#比如,(1,2,3,4)是一个四维向量
#但在Numpy中此是一个一维数组。
#在Numpy中判断维度要用shape这个方法,比如上面的(1,2,3,4),代码如图:
#返回的元组中有几个元素Numpy的数组就有几个维度,下面这个只有一个元素,因此是一维的
创建矩阵
我们可以用把参数用作列表套列表的方式,来创建下面这个矩阵:
np.array([[1,2],[3,4]])
我们仍然可以用上面所提到的方法,比如ones(),zeros()以及random.random()来创建矩阵,只不过这时的参数,变成了一个二元组,第一个元表示矩阵有多少行,第二个表示矩阵有多少列,如下图:
矩阵的算数运算
如果上面的二元组参数的个数和值是相同的,我们就可以进行矩阵的加减乘除预算。Numpy很擅长处理这些计算:
如果其中一个矩阵只有一行,但列数与另一个矩阵相同,我们也可以进行加法运算(译者注:原文中作者在进行此类运算时,一直在强调:此时的运算遵循的是broadcast rules,不过我以为不去深究此概念反而能帮助我们这样水平的中文读者去运用Numpy,所以并不单独强调此概念。)
点乘
还有一个特别之处在于矩阵乘法中的点乘。Numpy给了矩阵一个方法(dot()),因此我们便可以轻易实现矩阵之间的点乘操作,如下图:
为了强调矩阵间点乘的条件:左矩阵的列等于右矩阵的行,我在矩阵的右下角注明了矩阵的列数量和行数量。关于这一点,我用下面的图片进行可视化解读:
矩阵索引
当我们进行矩阵操作时,索引和切片是非常有力的工具,如下图:
矩阵的聚合函数
凡是用来聚合数组的函数,在聚合矩阵时皆可被用:
以上只是把矩阵当成一个整体来考虑,我们也可以对列和行举行聚合,这是通过axis函数来实现的。(http://mathworld.wolfram.com/RotationMatrix.html给出了此种操作的完整说明,这里不再累述)
矩阵转置与维度调整
当进行矩阵间的操作时,旋转(rotate)矩阵成了一件必须做的事。比如上面的点乘,我们就把左矩阵逆时针旋转九十度。Numpy在这点做得很好,它直接给了我们一个方法(T),如下图:
在实际中却存在更复杂的情况,你需要转换特定矩阵的维度,比如在机器学习应用中,一个特定模块需要的矩阵与你数据集里的矩阵在维度上很可能不同。
不过针对这种情况,Numpy提供了一个方法——reshape()。你仅仅需把你的目标维度作为参数即可,比如即使你传入-1作为一个维度,Numpy也可基于你的矩阵推断出正确的维度。
图例:
更多的维度
Numpy的强大之处在于他可以拓展到任意维度。这是因为它的核心数据结构就是一个n维的数组。
在大多数情况中,要加一个新的维度仅仅需要将二元组变成一个三元组,图示如下:
注意:这个三维Numpy数组在IDE中被打印出来时,如下图:
array([[[1., 1.],
[1., 1.],
[1., 1.]],
[[1., 1.],
[1., 1.],
[1., 1.]],
[[1., 1.],
[1., 1.],
[1., 1.]],
[[1., 1.],
[1., 1.],
[1., 1.]]])
这是因为,如果我们从底层考虑,其实质是用循环来实现的,这就意味着np.ones((4,3,2))可以被这样拆解
for i in range(0,1):#代表三元组中的2
for j in range(0,2):#代表三元组中的3
for i in range(0,3):#代表三元组中的4
Numpy的应用
这里由于篇幅有限,译者不再一一介绍,不过读者可以参考以下两个链接做一练习。https://jalammar.github.io/visual-numpy/jalammar.github.ioQuickstart tutorialnumpy.org