python数据科学必不可少的库-numpy

python数据科学必不可少的库-numpy

numpy对python的意义非凡,本来Python仅仅是一门脚本语言,但是因为numpy的加持,使Python有了在数据科学领域能与matlib、spss等专业软件一较高下的资本

现在用python搞数据分析或机器学习经常使用的pandas、matplotlib、sklearn等库,都需要基于numpy构建。

毫不夸张地说,没有numpy,就没有python今天在数据分析与机器学习领域的广泛应用。

NumPy提供了两种基本的对象:ndarray(N-dimensional array object)和 ufunc(universal function object)。ndarray(下文统一称之为数组)是存储单一数据类型的多维数组,而ufunc则是能够对数组进行处理的函数。

1,ndarray对象

我们可以通过给array函数传递Python的序列对象创建数组,如果传递的是多层嵌套的序列,将创建多维数组

python数据科学必不可少的库-numpy

python数据科学必不可少的库-numpy

python数据科学必不可少的库-numpy

数组的大小可以通过其shape属性获得

python数据科学必不可少的库-numpy

可以通过修改数组的shape属性,在保持数组元素个数不变的情况下,改变数组每个轴的长度。下面的例子将数组c的shape改为(4,3),注意从(3,4)改为(4,3)并不是对数组进行转置,而只是改变每个轴的大小,数组元素在内存中的位置并没有改变

当某个轴的元素为-1时,将根据数组元素的个数自动计算此轴的长度,因此下面的程序将数组c的shape改为了(2,6)

python数据科学必不可少的库-numpy

使用数组的reshape方法,可以创建一个改变了尺寸的新数组,原数组的shape保持不变

python数据科学必不可少的库-numpy

数组的元素类型可以通过dtype属性获得

python数据科学必不可少的库-numpy

先创建一个Python序列,然后通过array函数将其转换为数组,这样做显然效率不高。因此NumPy提供了很多专门用来创建数组的函数

arange函数类似于python的range函数,通过指定开始值、终值和步长来创建一维数组,注意数组不包括终值:

python数据科学必不可少的库-numpy

linspace函数通过指定开始值、终值和元素个数来创建一维等差的数组,可以通过endpoint关键字指定是否包括终值,缺省设置是包括终值

python数据科学必不可少的库-numpy

logspace函数和linspace类似,不过它创建等比数列,下面的例子产生1(10^0)到100(10^2)、有20个元素的等比数列:

python数据科学必不可少的库-numpy

存取元素

数组元素的存取方法和Python的标准方法相同

python数据科学必不可少的库-numpy

多维数组的存取和一维数组类似,因为多维数组有多个轴,因此它的下标需要用多个值来表示,NumPy采用组元(tuple)作为数组的下标

python数据科学必不可少的库-numpy

python数据科学必不可少的库-numpy

2,ufunc

ufunc是universal function的缩写,它是一种能对数组的每个元素进行操作的函数。NumPy内置的许多ufunc函数都是在C语言级别实现的,因此它们的计算速度非常快。

python数据科学必不可少的库-numpy

广播

当我们使用ufunc函数对两个数组进行计算时,ufunc函数会对这两个数组的对应元素进行计算,因此它要求这两个数组有相同的大小(shape相同)。如果两个数组的shape不同的话,会进行如下的广播(broadcasting)处理:

1. 让所有输入数组都向其中shape最长的数组看齐,shape中不足的部分都通过在前面加1补齐

2. 输出数组的shape是输入数组shape的各个轴上的最大值

3. 如果输入数组的某个轴和输出数组的对应轴的长度相同或者其长度为1时,这个数组能够用来计算,否则出错

4. 当输入数组的某个轴的长度为1时,沿着此轴运算时都用此轴上的第一组值

python数据科学必不可少的库-numpy

ufunc函数本身还有些方法,这些方法只对两个输入一个输出的ufunc函数有效,其它的ufunc对象调用这些方法时会抛出ValueError异常。

reduce 方法和Python的reduce函数类似,它沿着axis轴对array进行操作,相当于将运算符插入到沿axis轴的所有子数组或者元素当中。

.reduce (array=, axis=0, dtype=None)

例如:

>>> np.add.reduce([1,2,3]) # 1 + 2 + 3

6

>>> np.add.reduce([[1,2,3],[4,5,6]], axis=1) # 1,4 + 2,5 + 3,6

array([ 6, 15])

accumulate 方法和reduce方法类似,只是它返回的数组和输入的数组的shape相同,保存所有的中间计算结果:

>>> np.add.accumulate([1,2,3])

array([1, 3, 6])

>>> np.add.accumulate([[1,2,3],[4,5,6]], axis=1)

array([[ 1, 3, 6],

[ 4, 9, 15]])

reduceat 方法计算多组reduce的结果,通过indices参数指定一系列reduce的起始和终了位置。reduceat的计算有些特别,让我们通过一个例子来解释一下:

>>> a = np.array([1,2,3,4])

>>> result = np.add.reduceat(a,indices=[0,1,0,2,0,3,0])

>>> result

array([ 1, 2, 3, 3, 6, 4, 10])

对于indices中的每个元素都会调用reduce函数计算出一个值来,因此最终计算结果的长度和indices的长度相同。结果result数组中除最后一个元素之外,都按照如下计算得出:

if indices[i] < indices[i+1]:

result[i] = np.reduce(a[indices[i]:indices[i+1]])

else:

result[i] = a[indices[i]

而最后一个元素如下计算:

np.reduce(a[indices[-1]:])

因此上面例子中,结果的每个元素如下计算而得:

1 : a[0] = 1

2 : a[1] = 2

3 : a[0] + a[1] = 1 + 2

3 : a[2] = 3

6 : a[0] + a[1] + a[2] = 1 + 2 + 3 = 6

4 : a[3] = 4

10: a[0] + a[1] + a[2] + a[4] = 1+2+3+4 = 10

可以看出result[::2]和a相等,而result[1::2]和np.add.accumulate(a)相等。

outer 方法,.outer(a,b)方法的计算等同于如下程序:

>>> a.shape += (1,)*b.ndim

>>> (a,b)

>>> a = a.squeeze()

其中squeeze的功能是剔除数组a中长度为1的轴。如果你看不太明白这个等同程序的话,让我们来看一个例子:

>>> np.multiply.outer([1,2,3,4,5],[2,3,4])

array([[ 2, 3, 4],

[ 4, 6, 8],

[ 6, 9, 12],

[ 8, 12, 16],

[10, 15, 20]])

可以看出通过outer方法计算的结果是如下的乘法表:

# 2, 3, 4

# 1

# 2

# 3

# 4

# 5

如果将这两个数组按照等同程序一步一步的计算的话,就会发现乘法表最终是通过广播的方式计算出来的。

2.3矩阵运算

NumPy和Matlab不一样,对于多维数组的运算,缺省情况下并不使用矩阵运算,如果你希望对数组进行矩阵运算的话,可以调用相应的函数。

matrix对象

numpy库提供了matrix类,使用matrix类创建的是矩阵对象,它们的加减乘除运算缺省采用矩阵方式计算,因此用法和matlab十分类似。但是由于NumPy中同时存在ndarray和matrix对象,因此用户很容易将两者弄混。这有违Python的"显式优于隐式"的原则,因此并不推荐在较复杂的程序中使用matrix。下面是使用matrix的一个例子:

>>> a = np.matrix([[1,2,3],[5,5,6],[7,9,9]])>>> a*a**-1matrix([[ 1.00000000e+00, 1.66533454e-16, -8.32667268e-17], [ -2.77555756e-16, 1.00000000e+00, -2.77555756e-17], [ 1.66533454e-16, 5.55111512e-17, 1.00000000e+00]])

因为a是用matrix创建的矩阵对象,因此乘法和幂运算符都变成了矩阵运算,于是上面计算的是矩阵a和其逆矩阵的乘积,结果是一个单位矩阵。

矩阵的乘积可以使用dot函数进行计算。对于二维数组,它计算的是矩阵乘积,对于一维数组,它计算的是其点积。当需要将一维数组当作列矢量或者行矢量进行矩阵运算时,推荐先使用reshape函数将一维数组转换为二维数组:

>>> a = array([1, 2, 3])>>> a.reshape((-1,1))array([[1], [2], [3]])>>> a.reshape((1,-1))array([[1, 2, 3]])

除了dot计算乘积之外,NumPy还提供了inner和outer等多种计算乘积的函数。这些函数计算乘积的方式不同,尤其是当对于多维数组的时候,更容易搞混。

· dot : 对于两个一维的数组,计算的是这两个数组对应下标元素的乘积和(数学上称之为内积);对于二维数组,计算的是两个数组的矩阵乘积;对于多维数组,它的通用计算公式如下,即结果数组中的每个元素都是:数组a的最后一维上的所有元素与数组b的倒数第二位上的所有元素的乘积和:

· dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])

下面以两个3为数组的乘积演示一下dot乘积的计算结果:

首先创建两个3维数组,这两个数组的最后两维满足矩阵乘积的条件:

>>> a = np.arange(12).reshape(2,3,2)>>> b = np.arange(12,24).reshape(2,2,3)>>> c = np.dot(a,b)

dot乘积的结果c可以看作是数组a,b的多个子矩阵的乘积:

>>> np.alltrue( c[0,:,0,:] == np.dot(a[0],b[0]) )True>>> np.alltrue( c[1,:,0,:] == np.dot(a[1],b[0]) )True>>> np.alltrue( c[0,:,1,:] == np.dot(a[0],b[1]) )True>>> np.alltrue( c[1,:,1,:] == np.dot(a[1],b[1]) )True

· inner : 和dot乘积一样,对于两个一维数组,计算的是这两个数组对应下标元素的乘积和;对于多维数组,它计算的结果数组中的每个元素都是:数组a和b的最后一维的内积,因此数组a和b的最后一维的长度必须相同:

· inner(a, b)[i,j,k,m] = sum(a[i,j,:]*b[k,m,:])

下面是inner乘积的演示:

>>> a = np.arange(12).reshape(2,3,2)>>> b = np.arange(12,24).reshape(2,3,2)>>> c = np.inner(a,b)>>> c.shape(2, 3, 2, 3)>>> c[0,0,0,0] == np.inner(a[0,0],b[0,0])True>>> c[0,1,1,0] == np.inner(a[0,1],b[1,0])True>>> c[1,2,1,2] == np.inner(a[1,2],b[1,2])True

· outer : 只按照一维数组进行计算,如果传入参数是多维数组,则先将此数组展平为一维数组之后再进行运算。outer乘积计算的列向量和行向量的矩阵乘积:

· >>> np.outer([1,2,3],[4,5,6,7])· array([[ 4, 5, 6, 7],· [ 8, 10, 12, 14],· [12, 15, 18, 21]])

矩阵中更高级的一些运算可以在NumPy的线性代数子库linalg中找到。例如inv函数计算逆矩阵,solve函数可以求解多元一次方程组。下面是solve函数的一个例子:

>>> a = np.random.rand(10,10)>>> b = np.random.rand(10)>>> x = np.linalg.solve(a,b)>>> np.sum(np.abs(np.dot(a,x) - b))3.1433189384699745e-15

solve函数有两个参数a和b。a是一个N*N的二维数组,而b是一个长度为N的一维数组,solve函数找到一个长度为N的一维数组x,使得a和x的矩阵乘积正好等于b,数组x就是多元一次方程组的解。


分享到:


相關文章: