Python数据分析与挖掘
上QQ阅读APP看书,第一时间看更新

1.3 NumPy数组常用函数

下面以学生的学习成绩为数据样本展开练习,介绍NumPy数组常用函数。

通过随机函数生成一个班50名同学的语文、数学、化学、物理和外语5门课的相应成绩,假设每个同学的成绩范围为46~100。


>>>import numpy as np
>>>score=np.random.randint(46,100,(50,5))

1.3.1 统计函数

1.计算数组/矩阵中的最大值和最小值

np.amax(np数组[,[axis=]0])用于计算数组中的元素沿指定轴的最大值;np.amin(np数组[,[axis=]0]) 用于计算数组中的元素沿指定轴的最小值。

二维数据对象的大多数方法都会有axis这个参数,它控制了用户指定的操作是沿着0轴还是1轴进行。一般而言,axis=0代表对列进行操作,axis=1代表对行进行操作,省略该项则默认找到整个数组的最大值、最小值。

【动动手练习1-6】 计算数组/矩阵中的最大值、最小值


>>>np.amax(score)    #产生一个单一的值
99 
>>>np.amin(score) 
46 
#或 
>>>score.max() 
>>>score.min()

分别计算语文、数学、化学、物理和外语的最高成绩和最低成绩。


>>> np.amax(score,axis=0)    #生成一个数组;np.amax()方法与np.max()方法相同
array([98, 99, 99, 97, 99])  #对应语文、数学、化学、物理和外语的最高成绩 
>>> np.amin(score,0)         #可自行验证np.amin()方法与np.min()方法相同 
array([46, 47, 46, 48, 47])  #对应语文、数学、化学、物理和外语的最低成绩

查看总成绩排在前三的同学的各门课程最高、最低成绩。


>>> np.max(score,axis=1)[:3]  #np.amax()和np.max()是同一个方法,可混用
array([96, 97, 97]) 
>>> np.min(score,axis=1)[:3]  #np.amin()和np.min()是同一个方法,可混用 
array([68, 53, 55])

再总结一下,np.amax(score)是求出整个数组中的最大值。np.amax(score,0)是沿着axis=0轴(按列)比较的最大值。np.amax(score,1)是沿着axis=1轴(按行)比较的最大值。

2.统计最大值与最小值之差

np.ptp()即极差函数,极差是一组数据的最大值与最小值之差。

将成绩差值进行分析,可以发现班级中同学总的学习差距、各门课程的差距,以及各位同学的偏科程度。

【动动手练习1-7】 使用极差函数


>>> np.ptp(score)          #求出所有成绩的最高值与最低值的差
53 
>>> np.ptp(score,axis=0)   #求出各门课程的最高成绩与最低成绩的差 
Out[49]: array([52, 52, 53, 49, 52]) 
>>> np.ptp(score,axis=1)   #求出每位同学各门课程最高成绩与最低成绩的差

3.统计数组的分位数(百分位数)

np.percentile()函数的作用是求百分位数,从而能够让用户知道数字大致的分布。

np.percentile()函数原型:np.percentile(a, q, axis=None)。

各参数说明如下。

● a:原始数组,可以是多维数组。

● q:要计算的百分位数,取值范围为0~100,多个值时要使用列表方式。

● axis:在a的轴上计算百分位数。

查看学生整体成绩的分布情况,从最低成绩(0%),处于10%、25%、50%、75%位置上的成绩,最高成绩(100%)查询,即批量查询。

【动动手练习1-8】 使用分位数函数


>>>np.percentile(score,[0,10,25,50,75,100])
array([46. , 51.9, 60. , 75.5, 87. , 99. ])

查看每门课程的成绩分布情况。


>>> n=np.percentile(score,[0,10,25,50,75,100],axis=0)
>>> n 
array([[46.  , 47.  , 46.  , 48.  , 47.  ], 
       [49.8 , 50.9 , 50.7 , 52.9 , 55.8 ], 
       [56.  , 65.  , 60.25, 61.25, 61.5 ], 
       [70.5 , 80.5 , 73.  , 71.  , 80.  ], 
       [87.75, 89.  , 79.5 , 86.  , 86.75], 
       [98.  , 99.  , 99.  , 97.  , 99.  ]])

可以发现,数组的列是每门课程的成绩分布数据,列出的是处于不同百分数位置上的成绩,实际上,处于50%位置上的数据就是相应的中位数,不是平均数。

可以通过T转置,将列转换为行的形式展示(原来的行自然就转为列)。


>>> n.T  #每一行分别对应语文、数学、化学、物理和外语的成绩情况

4.统计数组中的中位数

中位数又称中值,是统计学中的专有名词,代表一个样本、种群或概率分布中的数值,是把所有观察值(数组)按从高到低排序后找出的正中间的数。如果数组数据的个数是奇数,则中间那个数就是这组数据的中位数;如果数据的个数是偶数,则中间2个数的算术平均值就是这组数据的中位数。

下面使用np.median()函数分别查找所有课程成绩的中位数、每门课程的中位数和每一位同学成绩的中位数。

【动动手练习1-9】 使用中位数函数


>>>np.median(score)
75.5 
>>>np.percentile(score,50) 
75.5 
>>>np.median(score,axis=0) 
array([70.5, 80.5, 73. , 71. , 80. ]) 
>>> np.median(score,axis=1)[:3]   #仅显示总成绩排在前三的同学成绩的中位数 
array([72., 65., 83.])

有时候,中位数可能比平均值更有说服力,比如,全社会的工资中位数比平均工资更能体现全社会大多数人的收入水平。

5.统计数组中的平均数、加权平均值

在NumPy中,np.mean()函数和np.average()函数都有取平均数的意思,在不考虑加权平均的前提下,两者的输出是一样的。

求所有成绩的平均值、各门课程的平均值。

【动动手练习1-10】 使用均值函数


>>>np.mean(score)
73.676 
>>>np.average(score) 
73.676 
>>>np.mean(score,axis=0)        #求各列的平均值 
array([72.12, 76.16, 71.52, 73.12, 75.46]) 
>>>np.mean(score,axis=1)[-3:]   #axis=1时,则求每位学生的平均成绩 
array([70.2, 78.6, 77.4])

当每门课程权重不一样时,可以通过np.average()函数计算加权平均值,求出每一位学生成绩的加权平均值和全部成绩的加权平均值。

np.average()函数根据在另一个数组中给出的各自的权重,计算数组中元素的加权平均值。该函数可以接收一个轴参数。如果没有指定轴参数,则数组会被展开。

加权平均值即将各数值乘以相应的权数,然后相加求和得到总体值,再除以总的单位数。


>>>w=np.array([120,120,100,100,120])   #定义语文、数学、化学、物理和外语的成绩对应
重 
>>>np.average(score, weights=w, axis=1)[:3]    #显示成绩排在前三的值 
array([79.        , 70.10714286, 78.17857143]) #"79."后面的空表示未显示部分 
>>>np.average(score, weights=w, axis=1).mean() 
73.77285714285715

6.统计数组中的方差和标准差

方差是概率论中最基础的概念之一,是由统计学天才罗纳德·费雪提出的。方差用于衡量数据离散程度,因为它能体现变量与其数学期望(均值)之间的偏离程度。

【例题1-2】 计算方差

计算方差,是指先求一组数据中各个数减去这组数据平均数的平方和,再对平方和求平均数。如求(1, 2, 3, 4, 5)这组数据的方差,就要先求出这组数据的平均数(1+2+3+4+5)÷5=3,再求各个数与平均数的差的平方和,(1−3)2+(2−3)2+(3−3)2+(4−3)2+(5−3)2=10,最后求平方和的平均数10÷5=2,即这组数据的方差为2。

标准差是方差的算术平方根(开根号)。由于方差是数据的平方,与检测值本身相差太大,人们难以直观衡量,所以常用方差开根号来衡量。标准差的公式:std=sqrt(mean((x - x.mean())**2))。

标准差和方差一样反映了一个数据集的离散程度,但平均数相同,标准差未必相同。

【例题1-3】 计算标准差

A、B两组各有6名学生参加同一次语文测验,A组的分数为95、85、75、65、55、45,B组的分数为73、72、71、69、68、67。这两组分数的平均数都是70,但A组的标准差为17.08,B组的标准差为2.16,说明A组学生成绩之间的差距比B组学生成绩之间的差距大得多。

下面通过NumPy提供的方差、标准差函数查看学生成绩的离散程度。

【动动手练习1-11】 计算方差和标准差

使用NumPy计算标准差和方差的方法如下。

(1)np.std(a, axis = None):计算标准差。

(2)np.var(a, axis = None):计算方差。


>>>np.var(score)        #未指定轴,计算全部成绩的方差
 241.891024 
>>>np.std(score)        #未指定轴,计算全部成绩的标准差 
15.55284617039595       #只有一个标准差并不能确定其离散程度的高低 
>>>np.std(score,axis=0) #按列计算每门课程成绩的标准差 
 array([16.97603016, 16.16955163, 14.43224168, 14.96882093, 14.51648718])  
#计算语文、数学、化学、物理和外语的标准差,可以相互比较各科成绩的差距 
#计算每一位学生各科成绩的离散程度(差距) 
>>>np.std(score,axis=1)[4:7]  #显示其中的3个标准差 
 array([17.14176187,  1.74355958, 14.81890684])  #数值大代表偏科严重

7.求和:一般求和与按轴累积求和

【动动手练习1-12】 sum()求和


>>>np.sum(score)           #未指定轴,计算所有成绩的和
18419 
>>>np.sum(score, axis=0)   #每门课程的成绩和(按列) 
array([3606, 3808, 3576, 3656, 3773]) 
>>>np.sum(score, axis=1)[:3]    #排在前三的学生的成绩和(按行) 
array([392, 349, 385])

np.cumsum(score[,axis=0])是按轴累积求和,未指定轴则对所有元素累积求和。axis=0表示对多维数组按列累积求和,axis=1表示对数组按行累积求和。


>>>np.cumsum(score)[245:]     #对所有成绩累积求和,显示最后5个,共有50*5个
array([18120, 18167, 18229, 18326, 18419], dtype=int32) 
>>>np.cumsum(score,axis=0)[:3]    #按列对每门课程成绩累积求和,显示前3行 
array([[ 69,  96,  72,  68,  87], 
       [166, 161, 150, 121, 143], 
       [249, 258, 216, 176, 227]], dtype=int32)

读者可以自行试验对行成绩(每个同学的成绩)累积求和。

1.3.2 逻辑函数

1.np.any()函数和np.all()函数

【例题1-4】 使用np.any()函数和np.all()函数


>>>a1=score[0]
>>>a2=score[1] 
>>> print('第一行大于第二行:',a1>a2) 
第一行大于第二行: [False  True False  True  True] 
>>> print('第一行只要有大于第二行的元素:',np.any(a1>a2)) 
第一行只要有大于第二行的元素: True 
>>> print('第一行的元素全部都大于第二行的元素:',np.all(a1>a2)) 
第一行的元素全部都大于第二行的元素: False

使用np.any()函数时,只要结果中存在一个True,最后结果就为True。而使用np.all()函数时,只有结果中全为True,最后的结果才为True。

2.NumPy中的逻辑运算函数

Python中有and、or、not三个逻辑运算符,NumPy使用np.logical_and()、np.logical_or()、np.logical_not()三个函数来进行逻辑运算。

【动动手练习1-13】 数组逻辑运算综合练习

将第一位同学的各门课程成绩与其后的3位同学进行比较,获取其成绩均高于其他3位同学的课程名称。


>>>a3=score[0]>score[1]
>>>a4=score[0]>score[2] 
>>>a5=score[0]>score[3] 
>>>np.logical_and(a3,a4,a5)  #与a3&a4&a5等价 
array([False, False, False,  True,  True])  #对应语文、数学、化学、物理和外语成绩;物理与外语的成绩高于其他3位同学的相应成绩 
>>>score[0][a3&a4&a5]  #查看高于其他3位同学的物理与外语成绩 
array([68, 87]) 

np.logical_and()函数的参数必须是2个及以上,可以是多个布尔表达式列表数组。

查看前3位同学每门课程成绩有没有大于90分的。


>>>np.logical_or(score[0]>90,score[1]>90,score[2]>90)
 #等价于(score[0]>90) | (score[1]>90) | (score[2]>90),布尔表达式要有括号 
array([ True,  True, False, False, False])  #可以判断语文、数学成绩有大于90分的

np.logical_or()函数的参数是2个及以上。

查看某同学的成绩不在70~80分的课程。


>>>np.logical_not(score[0]>=70, score[0]<80)
array([ True, False, False, True, False]) 

np.logical_not()函数的参数是1个及以上。

3.NumPy的np.where()函数

np.where()函数其实是一个三元运算符。函数满足条件返回一个值,反之返回另一个值。

【动动手练习1-14】 数组条件运算

判断前4位同学的前4门课程是否及格,成绩大于60(及格)置为1,否则(不及格)置为0。


>>>temp = score[:4, :4]
>>>np.where(temp > 60, 1, 0) 
array([[1, 1, 1, 1], 
       [1, 1, 1, 0], 
       [1, 1, 1, 0], 
       [0, 1, 0, 1]])

复合逻辑运算则需要结合np.logical_and()函数和np.logical_or()函数使用,例如判断前4名学生的前4门课程的成绩,成绩大于60且小于90置为1,否则置为0。


>>>np.where(np.logical_and(temp > 60, temp < 90), 1, 0)
array([[1, 0, 1, 1], 
       [0, 1, 1, 0], 
       [1, 0, 1, 0], 
       [0, 1, 0, 1]])

1.3.3 离散差分函数和梯度函数

1.离散差分(差值)函数np.diff()

np.diff()函数的原型:np.diff(a, n=1,axis=1),其作用是求矩阵数组中后一个元素减去前一个元素的差值。各个参数的说明如下。

● a:输入的矩阵数组。

● n:可选,代表要执行几次差值运算。默认是1。

● axis:默认axis=1,表示按列相减(后一列减前一列);axis=0时表示按行相减(后一行减前一行)。如此,每执行一次差值运算就比原有的列或行少一列或一行。

【例题1-5】 求差分


>>>import numpy as np
>>>score=np.random.randint(46,100,(50,5)) 
>>>a=score[4:8,:]   #取一个4行5列的矩阵数组 
>>> a 
array([[97, 98, 78, 77, 95], 
       [64, 70, 90, 98, 54], 
       [99, 88, 68, 81, 91], 
       [58, 59, 96, 91, 58]]) 
>>>np.diff(a)    #按列相减,进行一次差值运算 
array([[  1, -20,  -1,  18],    #跨列相减,结果为4行4列(比原来少一列) 
       [  6,  20,   8, -44], 
       [-11, -20,  13,  10], 
       [  1,  37,  -5, -33]]) 
>>>np.diff(a,axis=0)    #按行相减,进行一次差值运算 
array([[-33, -28,  12,  21, -41],    #跨行相减,结果为3行5列(比原来少一行) 
       [ 35,  18, -22, -17,  37], 
       [-41, -29,  28,  10, -33]]) 
>>>np.diff(a,n=2,axis=0)  #按行相减,进行2次差值运算,就是对上一个结果再进行差值运算 
array([[ 68,  46, -34, -38,  78],  #结果为2行5列(比原来少2行) 
       [-76, -47,  50,  27, -70]])

定义的参数n大于1时,表示将进行多次差值运算,就是在上一次差值运算的矩阵基础上再进行差值运算,直到n次为止。读者可以自行完成多次行差值操作。

2.梯度函数np.gradient()

np.gradient(f):用于计算数组f中元素的梯度,当f为多维时,返回每个维度的梯度。返回的结果和原始数组大小相同。

梯度的计算过程:对于一维的数组,两个边界的元素直接用后一个值减去前一个值,得到梯度,即b−a;对于中间的元素,取相邻两个元素差的一半,即(c−a)/2。

【例题1-6】 求梯度


>>>score[0]    #得到一个一维数组
array([69, 96, 72, 68, 87]) 
>>>np.gradient(score[0]) 
array([ 27. ,  1.5,  -14. ,  7.5,  19. ])

对于二维数组:分别计算2个维度上的梯度,每个维度上的梯度和上面的一维数组梯度求法相同。对于二维数组,任意一个元素的梯度存在两个方向,所以求得的梯度为两个数组对象,第一个数组表示最外层维度的梯度值,第二个数组表示第二层维度的梯度值。即二维数组求梯度值的结果是包含两个ndarray数组的列表,第一个列表是按列(维度)求梯度获得,第二个列表是按行(维度)求梯度获得。不论按列还是按行求梯度,其方法与一维求梯度一致。


>>>score[:4,:4]
array([[69, 96, 72, 68], 
       [97, 65, 78, 53], 
       [83, 97, 66, 55], 
       [55, 81, 58, 80]]) 
>>>np.gradient(score[:4,:4])            #返回一个列表,列表中有两个数组 
[array([[ 28. , -31. ,   6. , -15. ],   #按列求梯度获得的数组 
        [  7. ,   0.5,  -3. ,  -6.5], 
        [-21. ,   8. , -10. ,  13.5], 
        [-28. , -16. ,  -8. ,  25. ]]), 
 array([[ 27. ,   1.5, -14. ,  -4. ],   #按行求梯度获得的数组 
        [-32. ,  -9.5,  -6. , -25. ], 
        [ 14. ,  -8.5, -21. , -11. ], 
        [ 26. ,  1.5,  -0.5,  22. ]])]

对于n维数组,np.gradient()函数会生成n个数组,每个数组代表元素在第n个维度的梯度变化值。梯度反映了元素的变化率,尤其是在进行图像、声音等数据处理时,梯度有助于我们发现图像和声音的边缘。

1.3.4 随机函数

1.np.random.rand()函数

np.random.rand()函数表示根据给定维度生成[0,1)的数据,包含0,不包含1。该函数的一般格式:np.random.rand(d0,d1,…,dn)。其中,dn表示表格的每个维度。

该函数的返回值为指定维度的数组。

【动动手练习1-15】 使用随机函数


np.random.rand()     #没有参数时,返回单个数据
0.49622070976935806 
np.random.rand(4)    #shape:4,生成一维数组 
Out[20]: array([0.73473476, 0.91185891, 0.25690118, 0.15300424]) 
np.random.rand(4,2)  #shape:4*2,生成二维数组 
array([[0.22766791, 0.44004844], 
       [0.77363055, 0.41738285], 
       [0.4954431 , 0.94661826], 
       [0.98884195, 0.66583906]]) 
np.random.rand(4,3,2)   #shape: 4*3*2,生成三维数组 
array([[[0.13606573, 0.75197037], 
        [0.10787889, 0.56756818], 
        [0.05749478, 0.21269762]], 
 
       [[0.16066188, 0.77395856], 
        [0.21734109, 0.8403221 ], 
        [0.15336456, 0.08972729]], 
 
       [[0.48081287, 0.65475862], 
        [0.8804478 , 0.56230535], 
        [0.5931349 , 0.61528502]], 
 
       [[0.3535239 , 0.76732365], 
        [0.12317966, 0.6788728 ], 
     [0.84390425, 0.0316394 ]]])

2.np.random.randn()函数

np.random.randn()函数表示返回一个或一组样本,其具有标准正态分布。该函数的一般格式:np.random.randn(d0, d1,…,dn)。其中,dn表示表格的每个维度。

该函数返回值为指定维度的数组。

标准正态分布是以0为均值、以1为标准差的正态分布,记为N(0,1)。


np.random.randn()    #没有参数时,返回单个数据
-1.1241580894939212 
np.random.randn(3) 
array([ 1.2128837 ,  1.13688113, -0.13342453]) 
np.random.randn(2,4) 
array([[-1.61193815, -0.75167637,  0.72141234, -1.19118825], 
       [-1.11790307,  0.11534838,  0.51955485, -2.15917405]])

随机生成的值基本是−3~+3,但这也不绝对,主要是结果满足标准差的正态分布。

3.np.random.randint()

np.random.randint()函数表示随机生成整数(或浮点数)数据。该函数的一般格式:np.random.randint(low, high=None, size=None, dtype='l'),取值范围为[low,high),包含low,不包含high。各参数说明如下。

● low为最小值,high为最大值。

● size为数组维度的大小。

● dtype为数据类型,默认的数据类型是np.int。

没有填写high时,默认生成随机数的范围是[0,low)。


np.random.randint(1,5)     #返回1个[1,5)的随机整数
4 
np.random.randint(1,5,3)   #返回3个[1,5)随机整数的数组 
array([1, 1, 3]) 
np.random.randint(-5,5,size=(2,2))   #返回一个二维数组 
array([[ 2, -1], 
       [ 2,  0]])

4.伪随机数生成器

在机器学习中,生成随机数通常使用伪随机数生成器np.random. RandomState(),要想复现具备随机性的代码的最终结果,需要设置相同的种子值。


import numpy as np
rng = np.random.RandomState(0)   #设定随机种子值,此时设置为0,也可以是任意整数 
rng.rand(4)    #产生4个随机数,空括号则表示产生一个随机数 
Out[377]: array([0.5488135 , 0.71518937, 0.60276338, 0.54488318]) 
rng = np.random.RandomState(0)   #随机种子数相同,结果相同 
rng.rand(4) 
Out[379]: array([0.5488135 , 0.71518937, 0.60276338, 0.54488318])

上面的代码生成了一样的随机数组。下面是另一种顺序的操作。


rng = np.random.RandomState(100)  #随机种子数为100
rng.rand(4) 
Out[35]: array([0.54340494, 0.27836939, 0.42451759, 0.84477613]) 
rng.rand(4) 
Out[36]: array([0.00471886, 0.12156912, 0.67074908, 0.82585276]) 
rng = np.random.RandomState(100) 
rng.rand(4) 
Out[38]: array([0.54340494, 0.27836939, 0.42451759, 0.84477613]) 
rng.rand(4) 
Out[39]: array([0.00471886, 0.12156912, 0.67074908, 0.82585276])

因为np.random.RandomState()是伪随机数,所以必须在rng这个变量下使用,如果不这样做,那么就不能得到相同的随机数组。在使用rng这个变量前要先定义,定义之后生成随机数的结果才有顺序关系。

1.3.5 其他常用函数

其他常用函数还包括数学函数、统计函数等,见表1-2。

表1-2 其他常用函数

续表