美文网首页
[Python与数学建模-数据处理与可视化]-1数值计算工具Nu

[Python与数学建模-数据处理与可视化]-1数值计算工具Nu

作者: 六千宛 | 来源:发表于2020-08-20 16:30 被阅读0次

      虽然列表list可以完成数组操作,但不是真正意义上的数组,当数据量很大时,其速度很慢,故提供了NumPy扩展库完成数组操作。很多高级扩展库也依赖于它,比如Scipy、Pandas和Matplotlib等。
      NumPy提供了两种基本的对象:ndarray(N-dimensional Array Object)和ufunc(Universal Function Object)。ndarray(称为array数组,下文统一称为数组)是存储单一数据类型的多维数组,而ufunc则是能够对数组进行处理的通用函数。

    1数组的创建、属性和操作

    1.1数组的创建

      通过NumPy库的array函数实现数组的创建,如果向array函数中传入了一个列表或元组,将构造简单的一维数组;如果传入多个嵌套的列表或元组,则可以构造一个二维数组。构成数组的元素都具有相同的数据类型。下面分别构造一维数组和二维数组。
      例2.1 利用array函数创建数组示例。
      程序文件Pex2_1.py

    import numpy as np    #导入模块并命名为np
    a = np.array([2,4,8,20,16,30])  #单个列表创建一维数组
    #嵌套元组创建二维数组
    b = np.array(((1,2,3,4,5),(6,7,8,9,10),
                  (10,9,1,2,3),(4,5,6,8,9.0)))
    print("一维数组:",a)
    print("二维数组:\n",b)
    

      执行结果:

    一维数组:
     [ 2  4  8  20  16  30]
    二维数组:
     [[ 1.  2.  3.  4.  5.]
     [ 6.  7.  8.  9. 10.]
     [10.  9.  1.  2.  3.]
     [ 4.  5.  6.  8.  9.]]
    

      如上结果所示,可以将列表或元组转换为一个数组。在第二个数组b中,输入的元素含有整型和浮点型两种数据类型,但输出的数组元素都转化为相同的浮点型。
      例2.2 利用arange、linspace、empty等函数生成数组示例。
      程序文件Pex2_2.py

    import numpy as np
    a=np.arange(4,dtype=float)  #创建浮点型数组:[0., 1.,2., 3.]
    b=np.arange(0,10,2,dtype=int)  #创建整型数组:[0, 2, 4, 6, 8]
    c=np.empty((2,3),int)   #创建2×3的整型空矩阵
    d=np.linspace(-1,2,5)  #创建数组:[-1., -0.25,  0.5,  1.25,  2.]
    e=np.random.randint(0,3,(2,3))  #生成[0,3)上的2行3列的随机整数数组
    

      注2.1
      (1)上面程序运行后,没有输出,如果想看输出结果,读者可以自己加上print语句;我们以后的程序设计中,对于一些不重要的中间结果,也不输出了。或者使用Anaconda运行,在Spyder的控制台下可以直接看到输出结果。
      (2)empty函数只分配数组所使用的内存,不对数组元素值进行初始化操作,因此它的运行速度是最快的,上述程序中c=np.empty((2,3),int)的返回值是随机的,每次运行都是不一样的。
      例2.3 使用虚数单位“j”生成数组。
      程序文件Pex2_3.py

    import numpy as np  
    a=np.linspace(0,2,5)   #生成数组:[0., 0.5, 1., 1.5, 2.]
    b=np.mgrid[0:2:5j]     #等价于np.linspace(0,2,5)
    x,y=np.mgrid[0:2:4j,10:20:5j]  #生成[0,2]×[10,20]上的4×5的二维数组
    print("x={}\ny={}".format(x,y))
    

    1.2数组的属性

      为了更好地理解和使用数组,了解数组的基本属性是十分必要的。数组的属性及其说明如表2.1所列。


    图片.png
    图片.png

      程序文件Pex2_4.py

    import numpy as np
    a=np.random.randint(1,11,(3,5))  #生成[1,10]区间上3行5列的随机整数数组
    print("维数:",a.ndim);   #维数:2
    print("维度:",a.shape)       #维度:(3,5)
    print("元素总数:",a.size);    #元素总数:15
    print("类型:",a.dtype)       #类型:int32
    print("每个元素字节数:",a.itemsize)  #字节数:4
    

      例2.5 生成数学上一维向量的三种模式。
      程序文件Pex2_5.py

    import numpy as np
    a=np.array([1,2,3])
    print("维度为:",a.shape)    #维度为:(3,)
    b=np.array([[1,2,3]])
    print("维度为:",b.shape)    #维度为:(1,3)
    c=np.array([[1],[2],[3]])
    print("维度为:",c.shape)    #维度为:(3,1)
    

    1.3数组元素的索引

      NumPy中的array数组与Python基础数据结构列表(list)的区别是:列表中的元素可以是不同的数据类型,而array数组只允许存储相同的数据类型。
      ①对于一维数组来说,Python原生的列表和NumPy的数组的切片操作都是相同的,无非是记住一个规则:列表名(或数组名)[start: end: step],但不包括索引end对应的值。
      ②二维数据列表元素的引用方式为a[i][j];array数组元素的引用方式为a[i,j]。
      NumPy比一般的Python 序列提供更多的索引方式。除了用整数和切片的一般索引外,数组还可以布尔索引及花式索引。
      (1)一般索引
      例2.6 数组索
    引示例。
      程序文件Pex2_6.py

    import numpy as np
    a = np.array([2,4,8,20,16,30])  
    b = np.array(((1,2,3,4,5),(6,7,8,9,10),
                  (10,9,1,2,3),(4,5,6,8,9.0)))
    print(a[[2,3,5]])  #一维数组索引,输出:[ 8 20 30]
    print(a[[-1,-2,-3]])   #一维数组索引,输出:[30 16 20]
    print(b[1,2])  #输出第2行第3列元素:8.0
    print(b[2])    #输出第3行元素:[10.  9.  1.  2.  3.]
    print(b[2,:])  #输出第3行元素:[10.  9.  1.  2.  3.]
    print(b[:,1])  #输出第2列所有元素:[2.  7.  9.  5.]
    print(b[[2,3],1:4])  #输出第3、4行,第2、3、4列的元素
    print(b[1:3,1:3])    #输出第2、3行,第2、3列的元素
    

      如上结果所示,在一维数组的索引中,可以将任意位置的索引组装为列表,用作对应元素的获取;在二维数组中,位置索引必须写成[rows,cols]的形式,方括号的前半部分用于控制二维数组的行索引,后半部分用于控制数组的列索引。如果需要获取所有的行或列元素,那么,对应的行索引或列索引需要用英文状态的冒号表示。
      (2)布尔索引
      例2.7 布尔索引示例。
      程序文件Pex2_7.py

    from numpy import array, nan, isnan
    a=array([[1, nan, 2], [4, nan, 3]])
    b=a[~isnan(a)]  #提取a中非nan的数
    print("b=",b)
    print("b中大于2的元素有:", b[b>2])
    运行结果:
    b= [1. 2. 4. 3.]
    b中大于2的元素有: [4.  3.]
    

      (3)花式索引
      花式索引的索引值是一个数组。对于使用一维整型数组作为索引,如果被索引数据是一维数组,那么索引的结果就是对应位置的元素;如果被索引数据是二维数组,那么就是对应下标的行。
      对于二维被索引数据来说,索引值可以是二维数据,当索引值为两个维度相同的一维数组组成的二维数组时,以两个维度作为横纵坐标索引出单值后组合成新的一维数组。
      例2.8 花式索引示例。
      程序文件Pex2_8.py

    from numpy import array
    x = array([[1,2],[3,4],[5,6]])
    print("前两行元素为:\n", x[[0,1]])         #输出:[[1,2],[3,4]]
    print("x[0][0]和x[1][1]为:", x[[0,1],[0,1]])    #输出:[1 4]
    print("以下两种格式是一样的:")
    print(x[[0,1]][:,[0,1]]) # 输出:[[1,2],[3,4]],
    print(x[0:2,0:2])        #同上,输出第1、2行,第1、2列的元素
    

    1.4数组的修改

      这里数组的修改是指数组元素的修改,和数组维数的扩大或缩小。
      例2.9 数组修改示例。
      程序文件Pex2_9.py

    import numpy as np
    x = np.array([[1,2],[3,4],[5,6]])
    x[2,0] = -1  #修改第3行、第1列元素为-1
    y=np.delete(x,2,axis=0)   #删除数组的第3行
    z=np.delete(y,0, axis=1)  #删除数组的第1列
    t1=np.append(x,[[7,8]],axis=0) #增加一行
    t2=np.append(x,[[9],[10],[11]],axis=1) #增加一列
    

    1.5数组的变形

      在对数组进行操作时,经常要改变数组的维度。在NumPy中,常用reshape函数改变数据的形状,也就是改变数组的维度。其参数为一个正整数元组,分别指定数组在每个维度上的大小。reshape函数在改变原始数据的形状的同时不改变原始数据的值。如果指定的维度和数组的元素数目不吻合,则函数将抛出异常。
      数组变形和转换的一些函数(方法也统称函数)如表2.2所列。


    图片.png

      例2.10 reshape和resize变形示例。
      程序文件Pex2_10.py

    import numpy as np
    a=np.arange(4).reshape(2,2)  #生成数组[[0,1],[2,3]]
    b=np.arange(4).reshape(2,2)  #生成数组[[0,1],[2,3]]
    print(a.reshape(4,),'\n',a)  #输出:[0 1 2 3]和[[0,1],[2,3]]
    print(b.resize(4,),'\n',b)   #输出:None和[0 1 2 3] 
    

      如上结果所示,虽然reshape和resize都是用来改变数组形状的,但是reshape只是返回改变形状后的视图,数组本身是不变的;而resize没有返回,直接改变数组本身的形状。
      如果需要将多维数组降为一维数组,利用ravel、flatten和reshape三种方法均可以实现。
      例2.11 数组降维示例。
      程序文件Pex2_11.py

    import numpy as np
    a=np.arange(4).reshape(2,2)  #生成数组[[0,1],[2,3]]
    b=np.arange(4).reshape(2,2)  #生成数组[[0,1],[2,3]]
    c=np.arange(4).reshape(2,2)  #生成数组[[0,1],[2,3]]
    print(a.reshape(-1),'\n',a)  #输出:[0 1 2 3]和[[0,1],[2,3]]
    print(b.ravel(),'\n',b)      #输出:[0 1 2 3]和[[0,1],[2,3]]
    print(c.flatten(),'\n',c)    #输出:[0 1 2 3]和[[0,1],[2,3]]
    

      从显示效果看,三种方法是一样的,原数组都没有修改。但我们在平时使用时,flatten()比较合适,在使用过程中flatten()分配了新的内存;ravel()返回的是一个数组的视图,e=b.ravel()是允许的。
      例2.12 数组组合效果示例。
      程序文件Pex2_12.py

    import numpy as np
    a=np.arange(4).reshape(2,2)  #生成数组[[0,1],[2,3]]
    b=np.arange(4,8).reshape(2,2)  #生成数组[[4,5],[6,7]]
    c1=np.vstack([a,b])   #垂直方向组合
    c2=np.r_[a,b]        #垂直方向组合
    d1=np.hstack([a,b])   #水平方向组合
    d2=np.c_[a,b]        #水平方向组合
    

      例2.13 数组分割示例。
      程序文件Pex2_13.py

    import numpy as np
    a=np.arange(4).reshape(2,2)  #构造2行2列的数组
    b=np.hsplit(a,2)  #把a平均分成2个列数组
    c=np.vsplit(a,2)  #把a平均分成2个行数组
    print(b[0],'\n',b[1],'\n',c[0],'\n',c[1])
    

    2数组的运算、通用函数和广播运算

    2.1四则运算

      在NumPy库中,实现四则运算既可以使用运算符号+、-、、/,也可以使用函数add、substract、multiply、divide。需要注意的是,函数只能接受两个对象的运算,如果需要多个对象的运算,就得使用嵌套方法。
      另外还有三个数学运算符,分别是余数、整除和幂次,可以使用符号%、//、
    *,也可以使用函数fmod、modf和power。但是整除的函数应用会稍微复杂一点,需要写成np.modf(a/b)[1]的格式,因为modf可以返回数值的小数部分和整数部分,而整数部分就是要取的整数值。
      例2.14 数组简单运算示例。
      程序文件Pex2_14.py

    import numpy as np
    a=np.arange(10,15); b=np.arange(5,10)
    c=a+b; d=a*b  #对应元素相加和相乘
    e1=np.modf(a/b)[0]  #对应元素相除的小数部分
    e2=np.modf(a/b)[1]  #对应元素相除的整数部分
    

    2.2比较运算

      数组间的比较运算有表2.3所示的六种。


    图片.png

      运用比较运算符返回的是bool类型的值,即True和False。
      例2.15 比较运算示例。
      程序文件Pex2_15.py

    import numpy as np
    a=np.array([[3,4,9],[12,15,1]])
    b=np.array([[2,6,3],[7,8,12]])
    print(a[a>b])  #取出a大于b的所有元素,输出:[ 3  9  12  15]
    print(a[a>10]) #取出a大于10的所有元素,输出:[12  15]
    print(np.where(a>10,-1,a)) #a中大于10的元素改为-1
    print(np.where(a>10,-1,0)) #a中大于10的元素改为-1,否则为0
    

      最后一个print语句输出为:

    [[ 0  0  0]
    [-1 -1  0]]
    

      通过上述运行结果可以看出,多维数组通过bool索引返回的都是一维数组;np.where返回的数组保持原来的形状。

    2.3ufunc函数

    3.ufunc函数
      ufunc函数全称为通用函数,是一种能够对数组中的逐个元素进行操作的函数。ufunc函数是针对数组进行操作的,并且都以NumPy数组作为输出。使用ufunc函数比使用math库中的函数效率要高很多。目前NumPy支持超过60多种的通用函数。这些函数包括广泛的操作,如四则运算、求模、取绝对值、幂函数、指数函数、三角函数、位运算、比较运算和逻辑运算等。
      例2.16 ufunc函数效率示例。
      程序文件Pex2_16.py

    import numpy as np, time, math
    x=[i*0.01 for i in range(1000000)]
    start=time.time()  # 1970纪元后经过的浮点秒数
    for (i,t) in enumerate(x): x[i]=math.sin(t)
    print("math.sin:", time.time()-start)
    y=np.array([i*0.01 for i in range(1000000)])
    start=time.time()
    y=np.sin(y)
    print("numpy.sin:", time.time()-start)
    

      运行结果:

    math.sin: 0.3449997901916504
    numpy.sin: 0.010999917984008789
    

      可以发现对数组的操作,numpy函数整体花费时间比math模块函数要少得多。

    2.4ufunc函数的广播机制

      广播(Broadcasting)是指不同形状的数组之间执行算术运算的方式。当使用ufunc函数进行数组计算时,ufunc函数会对两个数组的对应元素进行计算。进行这种计算的前提是两个数组的维度相容。若两个数组的维度不相容时,则NumPy会实行广播机制。但是数组的广播功能是有规则的,如果不满足这些规则,运算时就会出错。数组的主要广播规则为:
      (1)各输入数组的维度可以不相等,但必须确保从右到左的对应维度值相等。
      (2)如果对应维度值不相等,就必须保证其中一个为1。
      例2.17 广播机制示例。
      程序文件Pex2_17.py

    import numpy as np
    a=np.arange(0, 20, 10).reshape(-1, 1)  #变形为1列的数组,行数自动计算
    b=np.arange(0, 3)
    print(a+b)
    运行结果:
    [[ 0   1   2]
    [10  11  12]]
    

    3NumPy.random模块的随机数生成

      虽然在Python内置的random模块中可以生成随机数,但是每次只能随机生成一个随机数,而且随机数的种类也不够丰富。建议使用NumPy.random模块的随机数生成函数,一方面可以生成随机向量,另一方面函数丰富。关于各种常见的随机数生成函数,如表2.4所列。


    图片.png
    图片.png

    4文本文件和二进制文件存取

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

    4.1文本文件的存取

    4.1.1savetxt()和loadtxt()存取文本文件

      savetxt()可以把1维和2维数组保存到文本文件。loadtxt()可以把文本文件中的数据加载到1维和2维数组中。
      例2.18 文本文件存取示例。
      程序文件Pex2_18.py

    import numpy as np
    a=np.arange(0,3,0.5).reshape(2,3)  #生成2×3的数组
    np.savetxt("Pdata2_18_1.txt", a)  #缺省按照'%.18e'格式保存数值,以空格分隔
    b=np.loadtxt("Pdata2_18_1.txt")  #返回浮点型数组
    print("b=",b)
    np.savetxt("Pdata2_18_2.txt", a, fmt="%d", delimiter=",")  #保存为整型数据,以逗号分隔
    c=np.loadtxt("Pdata2_18_2.txt",delimiter=",")  #读入的时候也需要指定逗号分隔
    print("c=",c)
    运行结果:
    b= [[0.  0.5  1. ]
    [1.5  2.  2.5]]
    c= [[0.  0.  1.]
    [1.  2.  2.]]
    

      例2.19 文本文件Pdata2_19.txt中存放如下格式的数据:

    6   2   6   7   4   2   5   9
    4   9   5   3   8   5   8   2
    5   2   1   9   7   4   3   3
    7   6   7   3   9   2   7   1
    2   3   9   5   7   2   6   5
    5   5   2   2   8   1   4   3
    

      把其中的数据读入到数组a,并提取数组a的前2行、第2列到第4列的元素,构造一个2行3列的数组b。
      程序文件Pex2_19.py

    import numpy as np
    a=np.loadtxt("Pdata2_19.txt")  #返回值a为浮点型数据
    b=a[0:2,1:4]  #获取a的第1,2行,第2,3,4列
    print("b=",b)
    程序运行结果如下:
    b= [[2.  6.  7.]
     [9.  5.  3.]]
    

      例2.20 文本文件Pdata2_20.txt中存放如下格式的数据:

    姓名,年龄,体重,身高
    张三,30,75,165
    李四,45,60,179
    王五,15,39,120
    

      提取其中的数值数据。
      程序文件Pex2_20.py

    import numpy as np
    a=np.loadtxt("Pdata2_20.txt",dtype=str,delimiter=",")
    b=a[1:,1:].astype(float)  #提取a矩阵的数值行和数值列,并转换类型
    print("b=",b)
    运行结果:
    b= [[ 30.  75.  165.]
     [ 45.  60.  179.]
     [ 15.  39.  120.]]
    

      如果需要处理复杂的数据结构,比如处理缺失数据等情况,可以使用genfromtxt。

    4.1.2genfromtxt读入文本文件数据

      它的调用格式为:

    genfromtxt(fname, dtype=<class 'float'>, comments='#', delimiter=None, skip_header=0, skip_footer=0, converters=None, missing_values=None, filling_values=None, usecols=None, names=None, excludelist=None, deletechars=None, replace_space='_', autostrip=False, case_sensitive=True, defaultfmt='f%i', unpack=None, usemask=False, loose=True, invalid_raise=True, max_rows=None, encoding='bytes')
    

      我们介绍其中的一些常用参数:

    (1)fname:指定需要读入数据的文件名。
    (2)dtype:指定读入数据的数据类型,默认为浮点型,如果原数据集中含有字符型数据,必须指定数据类型为“str”。
    (3)comments:指定注释符,默认为“#”,如果原数据的行首有“#”,将忽略这些行的读入。
    (4)delimiter:指定数据集的列分隔符。
    (5)skip_header:是否跳过数据集的首行,默认不跳过。
    (6)skip_footer:是否跳过数据集的脚注,默认不跳过。
    (7)converters:将指定列的数据转换成其他数值。
    (8)miss_values:指定缺失值的标记,如果原数据集含指定的标记,读入后这样的数据就为缺失值。
    (9)filling_values:指定缺失值的填充值。
    (10)usecols:指定需要读入的列。
    (11)names:为读入数据的列设置列名称
    (12)encoding:如果文件中含有中文,有时需要指定字符编码。
    

      例2.21 纯文本文件Pdata2_21.txt中存放如下数据。分别读取其中的前6行前8列数据、第9列的数值数据、最后一行数据。

    6       2       6       7       4       2       5       9      60kg
    4       9       5       3       8       5       8       2      55kg
    5       2       1       9       7       4       3       3      51kg
    7       6       7       3       9       2       7       1      43kg
    2       3       9       5       7       2       6       5      41kg
    5       5       2       2       8       1       4       -999       52kg
    35      37      22      32      41      32      43      38
    

      程序文件Pex2_21.py

    import numpy as np
    #读前6行前8列数据
    a=np.genfromtxt("Pdata2_21.txt",max_rows=6, usecols=range(8)) 
    b=np.genfromtxt("Pdata2_21.txt",dtype=str,max_rows=6,usecols=[8])  #读第9列数据
    b=[float(v.rstrip('kg')) for (i,v) in enumerate(b)]  #删除kg,并转换为浮点型数据
    c=np.genfromtxt("Pdata2_21.txt",skip_header=6)  #读最后一行数据
    print(a,'\n',b,'\n',c)
    

    4.2二进制格式文件存取

    4.2.1tofile()和fromfile()存取二进制格式文件

      使用数组对象的tofile()方法可以方便地将数组中的数据以二进制格式写进文件,tofile()输出的数据不保存数组形状和元素类型等信息。因此用fromfile()函数读回数据时需要用户指定元素类型,并对数组的形状进行适当的修改。
      例2.22 tofile和fromfile存取二进制格式文件示例。
      程序文件Pex2_22.py

    import numpy as np
    a=np.arange(6).reshape(2,3)
    a.tofile('Pdata2_22.bin')
    b=np.fromfile('Pdata2_22.bin',dtype=int).reshape(2,3)
    print(b)  
    

    4.2.2load()、save()和savez()存取NumPy专用的二进制格式文件

    load()和save()用NumPy专用的二进制格式存取数据,它们会自动处理元素类型和形状等信息。
      如果想将多个数组保存到一个文件中,可以使用savez()。savez()的第一个参数是文件名,其后的参数都是需要保存的数组,输出的是一个扩展名为npz的压缩文件。
      例2.23 存取NumPy专用的二进制格式文件示例。
      程序文件Pex2_23.py

    import numpy as np
    a=np.arange(6).reshape(2,3)
    np.save("Pdata2_23_1.npy",a)
    b=np.load("Pdata2_23_1.npy")
    c=np.arange(6,12).reshape(2,3)
    d=np.sin(c)
    np.savez("Pdata2_23_2.npz",c,d)
    e=np.load("Pdata2_23_2.npz")
    f1=e["arr_0"]  #提取第一个数组的数据
    f2=e["arr_1"]  #提取第二个数组的数据
    

      用解压软件打开“Pdata2_23_2.npz”文件,会发现其中有两个文件:“arr_0.npy”、“arr_1.npy”,其中分别保存着数组c、d的内容。load()自动识别npz文件,并且返回一个类似于字典的对象,可以通过数组名作为键获取数组的内容。

    相关文章

      网友评论

          本文标题:[Python与数学建模-数据处理与可视化]-1数值计算工具Nu

          本文链接:https://www.haomeiwen.com/subject/qjerjktx.html