【python】Numpy基础:数组与向量化计算

本文所有内容均总结自《利用python进行数据分析》。这本书太无趣了,如果不自己做一点笔记的话,根本读不下去。于是有了这些文章。

一、Numpy概述

Numpy是目前Python数值计算中最为重要的基础包,大多数计算包都提供了基于Numpy的科学函数功能,将Numpy的数组对象作为数据交换的通用语。简而言之,Numpy提供的数组太好用了,大家都喜欢。

我们将使用Numpy的以下几方面功能:

①、ndarry,一种高效的多维数组,提供了基于数组的便携算数操作以及灵活的广播功能。

②、对所有数据进行快速的矩阵计算,无需编写循环程序。

③、对硬盘中的数组数据进行读写的工具,并对内存映射文件进行操作。

④、线性代数、随机数生成以及傅里叶变换。

⑤、用于连接Numpy到C、C++和FORTRAN语言类库的C语言API。

在naive的我看来,这个Numpy库就是把一部分的matlab功能移植到python中。

二、Numpy ndarry:多维数组对象

1、ndarray简介

这是Numpy的核心特征之一,nd就是n维,array就是数组。

它的第一大特征就是可以进行对数组整体进行操作。注意不是矩阵操作。因为数组乘法是对应元素相乘,而不是矩阵乘法。如下:

使用np.random.rand(a,b)可以返回一个a行b列,元素是随机数的矩阵,目测这个随机数的范围是(0,1),保存在data中。

这个data的数据类型就是ndarray。

data*2就是数组中所有元素乘二,data+data就是对应元素相加。相乘用data比较难看出来,所以另外开两个数组:

看,也是对应的元素相乘。也就是说,对array进行计算,相当于对它的每一个元素分别计算,省去了循环的麻烦。

array既然是多维数组,那么就需要知道它的维度。使用a.shape就能够返回a在各个维度上的元素个数啦。比如上面的a,是一个二维数组,shape如下:

这说明a在第一维上有两个元素,在第二维上有三个元素。shape描绘数组的形状,“2”行“2”列。返回的元组的元素个数就是维数。对于维度,也可以用ndim来确定。

知道了维数,多维数组还有一个属性,就是元素的类型。使用a.dtype就可以知道。

2、ndarray生成

生成一个数组主要有以下几种方法:

np.array(a)
np.zeros(10)
np.ones((2,3))
np.empty((2,3,2))
np.full((2,3),1)

如图,对于np.array()方法,无论输入的是元组还是列表,输出的都是它们元素组成的array。

另外,如果输入是二维数组,输出自然也是二维数组。

我们有另外一个函数asarray,它与array的主要区别就在于,当输入类型是array,对于array函数,它将复制数组中的所有元素,然后返回一个array;对于asarray,它首先判断输入是不是array,如果是,就直接返回,如果不是,再复制。

array有一个参数dtype,可以用它指定生成的数组中的元素类型。

下面的zeros、ones、empty的输入参数就是数组在各个维度上的元素数量,比如说输入(2,3)那么zeros就返回一个2行3列元素为0的数组,ones返回的是元素全为1的数组,empty返回元素为空的数组,表现出来也是元素为0,但是并不安全,有时候会返回垃圾数值,因此要全0的最好还是用zero。

注意输入的参数不止一个的时候,要把所有参数用元组包起来。

最后一个函数full,其输入参数有两个,第一个是一个元组,用于描述输出数组的形状,也就是各维度上元素个数;第二个是元素,full会输出一个元素均是这个输入的数组。如下:

另外还有函数eye,identity,用于生成一个对角线为1,其余元素为0的数组。

3、ndarray的数据类型

数据类型和C差不多,有int有float,但是int里面还分几个小类int8,uint8什么的。这个可以自己去看。

重要的一点是数据类型的转换,在C中我们直接在前面加()就可以,但是python没有数据类型这么一说,所以需要借助astype来实现。如下:

将一个字符串列表转换为一个数组,要求元素类型为np.string_,这个类型要求等价于要求类型为S或S4,即长度为4的字符串类型,一定要小心使用,因为python会不告诉你而擅自把列表中的所有元素切到四或四以下。如果我们要求类型为S2,那么则会切到只剩两个字符,这样就有精度损失了。

我觉得特别厉害的一点是astype可以把字符串格式的数字直接转成真正的数字,如下:

注意转换成int8还会报错,转换成float就不会,真是聪明。

4、numpy数组算数

之前说过,ndarray的一大优势在于它可以批量对元素操作而不用循环,这被称之为向量化。

有三条原则:

①、等尺寸的数组之间操作为逐元素操作,就是一一对应。

②、带有标量计算的算数操作,会把计算参数传递给每一个元素。**表示乘方。

③、同尺寸数组间的比较,会生成一个布尔值数组,表明每个元素对应比较的真假。

另外,不同尺寸的数组比较,将会用到广播特性。

5、基础索引与切片

这部分的功能实际上就是让你精确、直白、方便的在一个多维数组中选择一些数据。

比较反直觉的一点在于,在选择出这一些数据之后,对这部分数据进行改动会反应在原来的数组上,我们称选择的这部分数组是一个“视图”,而不是原数据的复制。

如图,arr1是一个数组,arr2是arr1的一个切片,当我更改arr2中的元素,arr1也同步更新。

那问题来了,我要想对数组中某一部分元素进行更改,但不想影响原数组怎么办?使用copy函数。

如图,arr3由555变成了666,但arr1没变。

接下来有点复杂了。由于数组维数可以比二大,我们想要表达某一个元素或者某几个元素会变得困难。

比如说一个三维数组arr3d:

这个高维数组,维数越高越难理解。如果非得把它们映射成线啊面啊体啊,就有点难。我现在用这种方法来定位高维数组中的元素:

比如说arr3d[0,1,2],第一个0,表示要在[ 1, 2, 3], [ 4, 5, 6]里面找,第二个1,表示要在[ 4, 5, 6]里面找,第三个2,表示找到元素6。要是说第一个0表示0号面,第二个1表示第1行,第三个2表示第2列,这就不好解释了。

同样的,我们想选择[4,5,6]这个一维子数组,我们输入arr3d[0,1]即可。同理,选择[ 1, 2, 3], [ 4, 5, 6],输入arr3d[0]即可。

那我们想要[2,5,8,11]怎么办?就是每一个一维子数组的第一个?输入arr3d[:,:,1]即可。

注意,这里的2,5,8,11是一个2*2矩阵,而不是一个4*1向量。

想要[2,5,8]呢?这个做不到。因为这横跨了两个二维子数组,而且不是对称的,没法一次切片得到。可以两次切片,第一次2,5,8,11,第二次2,5,8。

想要[2,3,5,6]是可以的,arr3d[:1,1,1:]即可。第一个:1表示“到1之前”,第三个1:表示“从1开始”。

6、布尔索引

布尔索引即是将布尔表达式当做下标,返回对应的数据。原理就是布尔表达式是通过判断得到的,会返回一个元素为True或False的数组,根据这个数组,返回元素为True的部分。

例如,我们现在有一个1*7的人名数组names:

又有一个7*7的随机数数组rand:

其中每一行对应一个人名,那么我想选择名字为XueChengfei对应的一行该怎么办?

使用rand[names=="XueChengfei"]即可。

我们来看看names=='XueChengfei'这一句返回了什么。

看,返回了一个数组,只有XueChengfei对应的一行为True,于是可以返回该行数据。那么要是人名对应的不是行而是列呢?

很简单,将rand转置转即可。

不能将names=='XueChengfei'返回的数组转置,因为这个数组是一维的,转置并不会由1*7变成7*1,它不变。

我们也可以使用多个条件:

注意各个条件要用括号括起来。

或用~,且用&,非用|。

另外特别重要的一点,布尔索引返回的是拷贝,而不是视图。

7、神奇索引

神奇索引是通过输入“座标”来选择原数组中一些元素的方法,相比于基础索引,它更灵活,但也更复杂。

我们以二维数组为例:

现在我们想要返回一个数组,由arr的第4,3,0,6行组成,这用基础索引是无法实现的,但是用神奇索引可以很容易实现:输入[4,3,0,6]即可。

如果使用负的索引,将从尾部进行选择。

注意,-1是最后一行。

如果我们输入多个数组,那么这多个数组必定长度相同,每个数组对应位置的元素合起来组成一个座标,比如说[3,4,5,6],[1,2,3,1]就意味着我需要第3行第1个,第4行第2个,第5行第3个和第6行第1个。

要想选择子集呢?比如说我想选择上面1,2;1,3;4,2;4,3四个元素作为四个角所形成的矩形中的所有元素,可以输入[3,4,5,6],1:4。

8、数组转置和换轴

超过二维数组的转置就叫换轴,我觉得好难理解,先不用了。二维转置使用arr.T即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章