Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

标准安装的Python中用列表(list)保存一组值,可以用来当作数组使用,不过由于列表的元素可以是任何对象,因此列表中所保存的是对象的指 针。这样为了保存一个简单的[1,2,3],需要有3个指针和三个整数对象。对于数值运算来说这种结构显然比较浪费内存和CPU计算时间。

此外Python还提供了一个array模块,array对象和列表不同,它直接保存数值,和C语言的一维数组比较类似。但是由于它不支持多维,也没有各种运算函数,因此也不适合做数值运算。

NumPy的诞生弥补了这些不足,NumPy提供了两种基本的对象:ndarray(N-dimensional array object)和 ufunc(universal function object)。ndarray(下文统一称之为数组)是存储单一数据类型的多维数组,而ufunc则是能够对数组进行处理的函数。喜欢的话关注收藏评论转发比心么么哒!Python学习交流企鹅群526929231内有大量的项目开发和新手教学视频PDF书籍的五千人大群等着你来加入

2.1 ndarray对象

函数库的导入

本书的示例程序假设用以下推荐的方式导入NumPy函数库:

import numpy as np

2.1.1 创建

首先需要创建数组才能对其进行其它操作。

我们可以通过给array函数传递Python的序列对象创建数组,如果传递的是多层嵌套的序列,将创建多维数组(下例中的变量c):

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

数组a和d其实共享数据存储内存区域,因此修改其中任意一个数组的元素都会同时修改另外一个数组的内容:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

数组的元素类型可以通过dtype属性获得。上面例子中的参数序列的元素都是整数,因此所创建的数组的元素类型也是整数,并且是32bit的长整型。可以通过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全世界最长基础教程最适合小白比全球的还长 还详细

此外,使用frombuffer, fromstring, fromfile等函数可以从字节序列创建数组,下面以fromstring为例:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

Python的字符串实际上是字节序列,每个字符占一个字节,因此如果从字符串s创建一个8bit的整数数组的话,所得到的数组正好就是字符串中每个字符的ASCII编码:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

如果从字符串s创建16bit的整数数组,那么两个相邻的字节就表示一个整数,把字节98和字节97当作一个16位的整数,它的值就是98*256+97 = 25185。可以看出内存中是以little endian(低位字节在前)方式保存数据的。

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

如果把整个字符串转换为一个64位的双精度浮点数数组,那么它的值是:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

显然这个例子没有什么意义,但是可以想象如果我们用C语言的二进制方式写了一组double类型的数值到某个文件中,那们可以从此文件读取相应的数据,并通过fromstring函数将其转换为float64类型的数组。

我们可以写一个Python的函数,它将数组下标转换为数组中对应的值,然后使用此函数创建数组:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

fromfunction函数的第一个参数为计算每个数组元素的函数,第二个参数为数组的大小(shape),因为它支持多维数组,所以第二个参数必须是一个序列,本例中用(10,)创建一个10元素的一维数组。

下面的例子创建一个二维数组表示九九乘法表,输出的数组a中的每个元素a[i, j]都等于func2(i, j):

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

2.1.2 存取元素

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

和Python的列表序列不同,通过下标范围获取的新的数组是原始数组的一个视图。它与原始数组共享同一块数据空间:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

除了使用下标范围存取元素之外,NumPy还提供了两种存取元素的高级方法。

使用整数序列

当使用整数序列对数组元素进行存取时,将使用整数序列中的每个元素作为下标,整数序列可以是列表或者数组。使用整数序列作为下标获得的数组不和原始数组共享数据空间。

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

使用布尔数组

当使用布尔数组b作为下标存取数组x中的元素时,将收集数组x中所有在数组b中对应下标为True的元素。使用布尔数组作为下标获得的数组不和原始数组共享数据空间,注意这种方式只对应于布尔数组,不能使用布尔列表。

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

布尔数组一般不是手工产生,而是使用布尔运算的ufunc函数产生,关于ufunc函数请参照 ufunc运算 一节。

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

2.1.3 多维数组

多维数组的存取和一维数组类似,因为多维数组有多个轴,因此它的下标需要用多个值来表示,NumPy采用组元(tuple)作为数组的下标。如图2.1所示,a为一个6x6的数组,图中用颜色区分了各个下标以及其对应的选择区域。

组元不需要圆括号

虽然我们经常在Python中用圆括号将组元括起来,但是其实组元的语法定义只需要用逗号隔开即可,例如 x,y=y,x 就是用组元交换变量值的一个例子。

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

如何创建这个数组

你也许会对如何创建a这样的数组感到好奇,数组a实际上是一个加法表,纵轴的值为0, 10, 20, 30, 40, 50;横轴的值为0, 1, 2, 3, 4, 5。纵轴的每个元素都和横轴的每个元素求和,就得到图中所示的数组a。你可以用下面的语句创建它,至于其原理我们将在后面的章节进行讨论:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

多维数组同样也可以使用整数序列和布尔数组进行存取。

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

  • a[(0,1,2,3,4),(1,2,3,4,5)] : 用于存取数组的下标和仍然是一个有两个元素的组元,组元中的每个元素都是整数序列,分别对应数组的第0轴和第1轴。从两个序列的对应位置取出两个整数组成 下标: a[0,1], a[1,2], ..., a[4,5]。

  • a[3:, [0, 2, 5]] : 下标中的第0轴是一个范围,它选取第3行之后的所有行;第1轴是整数序列,它选取第0, 2, 5三列。

  • a[mask, 2] : 下标的第0轴是一个布尔数组,它选取第0,2,5行;第1轴是一个整数,选取第2列。

2.1.4 结构数组

在C语言中我们可以通过struct关键字定义结构类型,结构中的字段占据连续的内存空间,每个结构体占用的内存大小都相同,因此可以很容易地定义 结构数组。和C语言一样,在NumPy中也很容易对这种结构数组进行操作。只要NumPy中的结构定义和C语言中的定义相同,NumPy就可以很方便地读 取C语言的结构数组的二进制数据,转换为NumPy的结构数组。

假设我们需要定义一个结构数组,它的每个元素都有name, age和weight字段。在NumPy中可以如下定义:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

我们先创建一个dtype对象persontype,通过其字典参数描述结构类型的各个字段。字典有两个关键字:names,formats。每个关键字对应的值都是一个列表。names定义结构中的每个字段名,而formats则定义每个字段的类型:

  • S32 : 32个字节的字符串类型,由于结构中的每个元素的大小必须固定,因此需要指定字符串的长度

  • i : 32bit的整数类型,相当于np.int32

  • f : 32bit的单精度浮点数类型,相当于np.float32

然后我们调用array函数创建数组,通过关键字参数 dtype=persontype, 指定所创建的数组的元素类型为结构persontype。运行上面程序之后,我们可以在IPython中执行如下的语句查看数组a的元素类型

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

这里我们看到了另外一种描述结构类型的方法: 一个包含多个组元的列表,其中形如 (字段名, 类型描述) 的组元描述了结构中的每个字段。类型描述前面为我们添加了 '|', '

  • | : 忽视字节顺序

  • < : 低位字节在前

  • > : 高位字节在前

结构数组的存取方式和一般数组相同,通过下标能够取得其中的元素,注意元素的值看上去像是组元,实际上它是一个结构:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

2.1.5 内存结构

下面让我们来看看ndarray数组对象是如何在内存中储存的。如图2.3所示,关于数组的描述信息保存在一个数据结构中,这个结构引用两个对象:一块用于保存数据的存储区域和一个用于描述元素类型的dtype对象。

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

图2.3 ndarray数组对象在内存中的储存方式

数据存储区域保存着数组中所有元素的二进制数据,dtype对象则知道如何将元素的二进制数据转换为可用的值。数组的维数、大小等信息都保存在ndarray数组对象的数据结构中。图中显示的是如下数组的内存结构:

>>> a = np.array([[0,1,2],[3,4,5],[6,7,8]], dtype=np.float32)

strides中保存的是当每个轴的下标增加1时,数据存储区中的指针所增加的字节数。例如图中的strides为12,4,即第0轴的下标增加1 时,数据的地址增加12个字节:即a[1,0]的地址比a[0,0]的地址要高12个字节,正好是3个单精度浮点数的总字节数;第1轴下标增加1时,数据 的地址增加4个字节,正好是单精度浮点数的字节数。

如果strides中的数值正好和对应轴所占据的字节数相同的话,那么数据在内存中是连续存储的。然而数据并不一定都是连续储存的,前面介绍过通过下标范围得到新的数组是原始数组的视图,即它和原始视图共享数据存储区域:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

由于数组b和数组a共享数据存储区,而b中的第0轴和第1轴都是数组a中隔一个元素取一个,因此数组b的strides变成了24,8,正好都是数组a的两倍。 对照前面的图很容易看出数据0和2的地址相差8个字节,而0和6的地址相差24个字节。

元素在数据存储区中的排列格式有两种:C语言格式和Fortan语言格式。在C语言中,多维数组的第0轴是最上位的,即第0轴的下标增加1时,元素 的地址增加的字节数最多;而Fortan语言的多维数组的第0轴是最下位的,即第0轴的下标增加1时,地址只增加一个元素的字节数。在NumPy中,元素 在内存中的排列缺省是以C语言格式存储的,如果你希望改为Fortan格式的话,只需要给数组传递order="F"参数:

>>> c = np.array([[0,1,2],[3,4,5],[6,7,8]], dtype=np.float32, order="F")
>>> c.strides
(4, 12)

2.2 ufunc运算

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

先用linspace产生一个从0到2*PI的等距离的10个数,然后将其传递给sin函数,由于np.sin是一个ufunc函数,因此它对x中 的每个元素求正弦值,然后将结果返回,并且赋值给y。计算之后x中的值并没有改变,而是新创建了一个数组保存结果。如果我们希望将sin函数所计算的结果 直接覆盖到数组x上去的话,可以将要被覆盖的数组作为第二个参数传递给ufunc函数。例如::

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

sin函数的第二个参数也是x,那么它所做的事情就是对x中的每给值求正弦值,并且把结果保存到x中的对应的位置中。此时函数的返回值仍然是整个计算的结果,只不过它就是x,因此两个变量的id是相同的(变量t和变量x指向同一块内存区域)。

我用下面这个小程序,比较了一下numpy.math和Python标准库的math.sin的计算速度::

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

在我的电脑上计算100万次正弦值,numpy.sin比math.sin快10倍多。这得利于numpy.sin在C语言级别的循环计算。 numpy.sin同样也支持对单个数值求正弦,例如:numpy.sin(0.5)。不过值得注意的是,对单个数的计算math.sin则比 numpy.sin快得多了,让我们看下面这个测试程序:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

请注意numpy.sin的计算速度只有math.sin的1/5。这是因为numpy.sin为了同时支持数组和单个值的计算,其C语言的内部实 现要比math.sin复杂很多,如果我们同样在Python级别进行循环的话,就会看出其中的差别了。此外,numpy.sin返回的数的类型和 math.sin返回的类型有所不同,math.sin返回的是Python的标准float类型,而numpy.sin则返回一个 numpy.float64类型:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

通过上面的例子我们了解了如何最有效率地使用math库和numpy库中的数学函数。因为它们各有长短,因此在导入时不建议使用*号全部载入,而是应该使用import numpy as np的方式载入,这样我们可以根据需要选择合适的函数调用。

NumPy中有众多的ufunc函数为我们提供各式各样的计算。除了sin这种单输入函数之外,还有许多多个输入的函数,add函数就是一个最常用的例子。先来看一个例子:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

add函数返回一个新的数组,此数组的每个元素都为两个参数数组的对应元素之和。它接受第3个参数指定计算结果所要写入的数组,如果指定的话,add函数就不再产生新的数组。

由于Python的操作符重载功能,计算两个数组相加可以简单地写为a+b,而np.add(a,b,a)则可以用a+=b来表示。下面是数组的运 算符和其对应的ufunc函数的一个列表,注意除号"/"的意义根据是否激活__future__.division有所不同。

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

数组对象支持这些操作符,极大地简化了算式的编写,不过要注意如果你的算式很复杂,并且要运算的数组很大的话,会因为产生大量的中间结果而降低程序的运算效率。例如:假设a b c三个数组采用算式x=a*b+c计算,那么它相当于:

t = a * b
x = t + c
del t

也就是说需要产生一个数组t保存乘法的计算结果,然后再产生最后的结果数组x。我们可以通过手工将一个算式分解为x = a*b; x += c,以减少一次内存分配。

通过组合标准的ufunc函数的调用,可以实现各种算式的数组计算。不过有些时候这种算式不易编写,而针对每个元素的计算函数却很容易用 Python实现,这时可以用frompyfunc函数将一个计算单个元素的函数转换成ufunc函数。这样就可以方便地用所产生的ufunc函数对数组 进行计算了。让我们来看一个例子。

我们想用一个分段函数描述三角波,三角波的样子如图2.4所示:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

图2.4 三角波可以用分段函数进行计算

我们很容易根据上图所示写出如下的计算三角波某点y坐标的函数:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

显然triangle_wave函数只能计算单个数值,不能对数组直接进行处理。我们可以用下面的方法先使用列表包容(List comprehension),计算出一个list,然后用array函数将列表转换为数组:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

这种做法每次都需要使用列表包容语法调用函数,对于多维数组是很麻烦的。让我们来看看如何用frompyfunc函数来解决这个问题:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

frompyfunc的调用格式为frompyfunc(func, nin, nout),其中func是计算单个元素的函数,nin是此函数的输入参数的个数,nout是此函数的返回值的个数。虽然triangle_wave函数 有4个参数,但是由于后三个c, c0, hc在整个计算中值都是固定的,因此所产生的ufunc函数其实只有一个参数。为了满足这个条件,我们用一个lambda函数对 triangle_wave的参数进行一次包装。这样传入frompyfunc的函数就只有一个参数了。这样子做,效率并不是太高,另外还有一种方法:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

我们通过函数triangle_func包装三角波的三个参数,在其内部定义一个计算三角波的函数trifunc,trifunc函数在调用时会采用triangle_func的参数进行计算。最后triangle_func返回用frompyfunc转换结果。

值得注意的是用frompyfunc得到的函数计算出的数组元素的类型为object,因为frompyfunc函数无法保证Python函数返回的数据类型都完全一致。因此还需要再次 y2.astype(np.float64)将其转换为双精度浮点数组。

2.2.1 广播

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

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

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

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

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

上述4条规则理解起来可能比较费劲,让我们来看一个实际的例子。

先创建一个二维数组a,其shape为(6,1):

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

计算a和b的和,得到一个加法表,它相当于计算a,b中所有元素组的和,得到一个shape为(6,5)的数组:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

由于a和b的shape长度(也就是ndim属性)不同,根据规则1,需要让b的shape向a对齐,于是将b的shape前面加1,补齐为(1,5)。相当于做了如下计算:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

这样加法运算的两个输入数组的shape分别为(6,1)和(1,5),根据规则2,输出数组的各个轴的长度为输入数组各个轴上的长度的最大值,可知输出数组的shape为(6,5)。

由于b的第0轴上的长度为1,而a的第0轴上的长度为6,因此为了让它们在第0轴上能够相加,需要将b在第0轴上的长度扩展为6,这相当于:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

由于a的第1轴的长度为1,而b的第一轴长度为5,因此为了让它们在第1轴上能够相加,需要将a在第1轴上的长度扩展为5,这相当于:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

当然,numpy在执行a+b运算时,其内部并不会真正将长度为1的轴用repeat函数进行扩展,如果这样做的话就太浪费空间了。

由于这种广播计算很常用,因此numpy提供了一个快速产生如上面a,b数组的方法: ogrid对象:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

ogrid是一个很有趣的对象,它像一个多维数组一样,用切片组元作为下标进行存取,返回的是一组可以用来广播计算的数组。其切片下标有两种形式:

  • 开始值:结束值:步长,和np.arange(开始值, 结束值, 步长)类似

  • 开始值:结束值:长度j,当第三个参数为虚数时,它表示返回的数组的长度,和np.linspace(开始值, 结束值, 长度)类似:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

ogrid为什么不是函数

根据Python的语法,只有在中括号中才能使用用冒号隔开的切片语法,如果ogrid是函数的话,那么这些切片必须使用slice函数创建,这显然会增加代码的长度。

利用ogrid的返回值,我能很容易计算x, y网格面上各点的值,或者x, y, z网格体上各点的值。下面是绘制三维曲面 x * exp(x**2 - y**2) 的程序:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

此程序使用mayavi的mlab库快速绘制如图2.5所示的3D曲面,关于mlab的相关内容将在今后的章节进行介绍。

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

图2.5 使用ogrid创建的三维曲面

2.2.2 ufunc的方法

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

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

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

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

2.3 矩阵运算

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

matrix对象

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

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

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

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

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

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

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

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

有关线性代数方面的内容将在今后的章节中详细介绍。

2.4 文件存取

NumPy提供了多种文件操作函数方便我们存取数组内容。文件存取的格式分为两类:二进制和文本。而二进制格式的文件又分为NumPy专用的格式化二进制类型和无格式类型。

使用数组的方法函数tofile可以方便地将数组中数据以二进制的格式写进文件。tofile输出的数据没有格式,因此用numpy.fromfile读回来的时候需要自己格式化数据:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

从上面的例子可以看出,需要在读入的时候设置正确的dtype和shape才能保证数据一致。并且tofile函数不管数组的排列顺序是C语言格式的还是Fortran语言格式的,统一使用C语言格式输出。

此外如果fromfile和tofile函数调用时指定了sep关键字参数的话,数组将以文本格式输入输出。

numpy.load和numpy.save函数以NumPy专用的二进制类型保存数据,这两个函数会自动处理元素类型和shape等信息,使用它们读写数组就方便多了,但是numpy.save输出的文件很难和其它语言编写的程序读入:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

如果你想将多个数组保存到一个文件中的话,可以使用numpy.savez函数。savez函数的第一个参数是文件名,其后的参数都是需要保存的数 组,也可以使用关键字参数为数组起一个名字,非关键字参数传递的数组会自动起名为arr_0, arr_1, ...。savez函数输出的是一个压缩文件(扩展名为npz),其中每个文件都是一个save函数保存的npy文件,文件名对应于数组名。load函数 自动识别npz文件,并且返回一个类似于字典的对象,可以通过数组名作为关键字获取数组的内容:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

如果你用解压软件打开result.npz文件的话,会发现其中有三个文件:arr_0.npy, arr_1.npy, sin_array.npy,其中分别保存着数组a, b, c的内容。

使用numpy.savetxt和numpy.loadtxt可以读写1维和2维的数组:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

文件名和文件对象

本节介绍所举的例子都是传递的文件名,也可以传递已经打开的文件对象,例如对于load和save函数来说,如果使用文件对象的话,可以将多个数组储存到一个npy文件中:

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

一、数组方法

创建数组:arange()创建一维数组;array()创建一维或多维数组,其参数是类似于数组的对象,如列表等

创建数组:np.zeros((2,3)),或者np.ones((2,3)),参数是一个元组分别表示行数和列数

对应元素相乘,a * b,得到一个新的矩阵,形状要一致;但是允许a是向量而b是矩阵,a的列数必须等于b的列数,a与每个行向量点乘

+ - / 与 * 的运算规则相同。

数学上定义的矩阵乘法 np.dot(a, b)。如果形状不匹配会报错;但是允许允许a和b都是向量,返回两个向量的内积。只要有一个参数不是向量,就应用矩阵乘法。

(PS:总之就是,向量很特殊,在运算中可以自由转置而不会出错,运算的返回值如果维度为1,也一律用行向量[]表示)

读取数组元素:如a[0],a[0,0]

数组变形:如b=a.reshape(2,3,4)将得到原数组变为2*3*4的三维数组后的数组;或是a.shape=(2,3,4)或a.resize(2,3,4)直接改变数组a的形状

数组组合:水平组合hstack((a,b))或concatenate((a,b),axis=1);垂直组合vstack((a,b))或concatenate((a,b),axis=0);深度组合dstack((a,b))

数组分割(与数组组合相反):分别有hsplit,vsplit,dsplit,split(split与concatenate相对应)

将np数组变为py列表:a.tolist()

数组排序(小到大):列排列np.msort(a),行排列np.sort(a),np.argsort(a)排序后返回下标

复数排序:np.sort_complex(a)按先实部后虚部排序

数组的插入:np.searchsorted(a,b)将b插入原有序数组a,并返回插入元素的索引值

类型转换:如a.astype(int),np的数据类型比py丰富,且每种类型都有转换方法

条件查找,返回满足条件的数组元素的索引值:np.where(条件)

条件查找,返回下标:np.argwhere(条件)

条件查找,返回满足条件的数组元素:np.extract([条件],a)

根据b中元素作为索引,查找a中对应元素:np.take(a,b)一维

数组中最小最大元素的索引:np.argmin(a),np.argmax(a)

多个数组的对应位置上元素大小的比较:np.maximum(a,b,c,…..)返回每个索引位置上的最大值,np.minimum(…….)相反

将a中元素都置为b:a.fill(b)

每个数组元素的指数:np.exp(a)

生成等差行向量:如np.linspace(1,6,10)则得到1到6之间的均匀分布,总共返回10个数

求余:np.mod(a,n)相当于a%n,np.fmod(a,n)仍为求余且余数的正负由a决定

计算平均值:np.mean(a)

计算最大值:amax(a, axis=None, out=None, keepdims=False) 。Return the maximum of an array or maximum along an axis.

计算加权平均值:np.average(a,b),其中b是权重

计算数组的极差:np.pth(a)=max(a)-min(a)

计算方差(总体方差):np.var(a)

标准差:np.std(a)

算术平方根,a为浮点数类型:np.sqrt(a)

对数:np.log(a)

修剪数组,将数组中小于x的数均换为x,大于y的数均换为y:a.clip(x,y)

所有数组元素乘积:a.prod()

数组元素的累积乘积:a.cumprod()

数组元素的符号:np.sign(a),返回数组中各元素的正负符号,用1和-1表示

数组元素分类:np.piecewise(a,[条件],[返回值]),分段给定取值,根据判断条件给元素分类,并返回设定的返回值。

判断两数组是否相等: np.array_equal(a,b)

判断数组元素是否为实数: np.isreal(a)

去除数组中首尾为0的元素:np.trim_zeros(a)

对浮点数取整,但不改变浮点数类型:np.rint(a)

二、数组属性

1.获取数组每一维度的大小:a.shape

2.获取数组维度:a.ndim

3.元素个数:a.size

4.数组元素在内存中的字节数:a.itemsize

5.数组字节数:a.nbytes==a.size*a.itemsize

6.数组元素覆盖:a.flat=1,则a中数组元素都被1覆盖

7.数组转置:a.T

不能求逆、求协方差、迹等,不适用于复杂科学计算,可以将array转换成matrix。

三、矩阵方法

创建矩阵:np.mat(‘…’)通过字符串格式创建,np.mat(a)通过array数组创建,也可用matrix或bmat函数创建

创建复合矩阵:np.bmat(‘A B’,’AB’),用A和B创建复合矩阵AB(字符串格式)

创建n*n维单位矩阵:np.eye(n)

矩阵的转置:A.T

矩阵的逆矩阵:A.I

计算协方差矩阵:np.cov(x),np.cov(x,y)

计算矩阵的迹(对角线元素和):a.trace()

相关系数:np.corrcoef(x,y)

给出对角线元素:a.diagonal()

四、线性代数

估计线性模型中的系数:a=np.linalg.lstsq(x,b),有b=a*x

求方阵的逆矩阵:np.linalg.inv(A)

求广义逆矩阵:np.linalg.pinv(A)

求矩阵的行列式:np.linalg.det(A)

解形如AX=b的线性方程组:np.linalg.solve(A,b)

求矩阵的特征值:np.linalg.eigvals(A)

求特征值和特征向量:np.linalg.eig(A)

Svd分解:np.linalg.svd(A)

五、概率分布

产生二项分布的随机数:np.random.binomial(n,p,size=…),其中n,p,size分别是每轮试验次数、概率、轮数

产生超几何分布随机数:np.random.hypergeometric(n1,n2,n,size=…),其中参数意义分别是物件1总量、物件2总量、每次采样数、试验次数

产生N个正态分布的随机数:np.random.normal(均值,标准差,N)

产生N个对数正态分布的随机数:np.random.lognormal(mean,sigma,N)

六、多项式

多项式拟合:poly= np.polyfit(x,a,n),拟合点集a得到n级多项式,其中x为横轴长度,返回多项式的系数

多项式求导函数:np.polyder(poly),返回导函数的系数

得到多项式的n阶导函数:多项式.deriv(m = n)

多项式求根:np.roots(poly)

多项式在某点上的值:np.polyval(poly,x[n]),返回poly多项式在横轴点上x[n]上的值

两个多项式做差运算: np.polysub(a,b)

Matpoltlib简单绘图方法

引入简单绘图的包import matplotlib.pyplot as plt,最后用plt.show()显示图像

基本画图方法:plt.plot(x,y),plt.xlabel(‘x’),plt.ylabel(‘y’),plt.title(‘…’)

子图:plt.subplot(abc),其中abc分别表示子图行数、列数、序号

创建绘图组件的顶层容器:fig = plt.figure()

添加子图:ax = fig.add_subplot(abc)

设置横轴上的主定位器:ax.xaxis.set_major_locator(…)

绘制方图:plt.hist(a,b),a为长方形的左横坐标值,b为柱高

绘制散点图:plt.scatter(x,y,c = ‘..’,s = ..),c表示颜色,s表示大小

添加网格线:plt.grid(True)

添加注释:如ax.annotate('x', xy=xpoint, textcoords='offsetpoints',xytext=(-50, 30), arrowprops=dict(arrowstyle="->"))

增加图例:如plt.legend(loc='best', fancybox=True)

对坐标取对数:横坐标plt.semilogx(),纵坐标plt.semilogy(),横纵同时plt.loglog()

Python Numpy全世界最长基础教程最适合小白比全球的还长 还详细

在此所写文章做成笔记记录的形式,书写代码过程中难免取之互联网,有的是本人书写,推崇自己一些见解想法,在看文章之前呢,小编推荐一个群,群里分子非常踊跃交流经验遇坑问题,也有初学者交流讨论,群内整理了也整理了大量的PDF书籍和学习资料,程序员也很热心的帮助解决问题,还有讨论工作上的解决方案,非常好的学习交流地方!群内大概有好几千人了,喜欢python的朋友可以加入python群:526929231欢迎大家交流讨论各种奇技淫巧,一起快速成长


分享到:


相關文章: