Python学习笔记(9-1):数据分析——数据运算

在这里插入图片描述

文章导读

 - 课程难度:★★☆☆☆
 - 重要度:★★★☆☆
 - 预计学习时间:1小时
 - 简介:本节主要讲解了python数据分析中数据运算的部分,包括:(1)基于基础一、二元的各种运算、三角函数的创建与运算、指对数的各种运算、调整精度的方法以及求向量和、差分及乘积等运算;(2)列表、numpy数组、Series和Dataframe数据的排序与查找问题。
 - 重点涉及的函数和内容:np.round()、.sort()、sorted()、.argsort()、.sort_values()、.sort_index()、np.where()。

关于数学运算集中在numpy和scipy,而数据分析应用则主要集中在pandas。因此,我们在数据分析方面以DataFrame为素材讲解,在数学运算方面主要以numpy 数组为素材讲解,但有一些内置库的相似方法我们也会涉及。
我们首先将如下函数库引入:

import numpy as np
import pandas as pd
import scipy

一、算术运算

我们一般用numpy实现数学运算领域的各种功能,因此在这里主要围绕numpy进行讲解。当然,有许多函数名称、调用方法和返回结果相同的函数,也存在于内置方法库和pandas中,我们这里也会进行一一地讲解。
首先提一下我们常使用的一些科学常数。这里包括自然对数的底数e、欧拉常数γ(调和级数和自然对数差值的极限)、以及π。其余的np.nannp.inf等在之前讲解过:

np.e
np.euler_gamma
np.pi

我们首先建立部分将在后续经常用于展示运算效果的数据:

x = np.array([100.45, 100.02, 100.0, 99.97, 100.05])
x1 = np.array([104.15, 98.72, 100.0, 102.25, 99.65])
y = np.array([1.639, 0.578, 1.031, 1.031, 0.59])
z = np.array([-4.5, -3, 2.5, 0.76, -0.5])
w = np.array([np.inf, np.nan, float('nan'), 1e10, 500])

x_int = np.array([13, 25, 24, 16, 33])
y_int = np.array([7, 5, 15, 20, 22])

x_bit = np.array([0b0101, 0b0110, 0b0101, 0b1010, 0b1101])
y_mv = np.array([1, 1, 1, 1, 1])
y_bit = np.array([0b0011, 0b1010, 0b0101, 0b1101, 0b0111])

x_bool = np.array([True, False, True, True, False])
y_bool = np.array([False, False, True, False, True])

x_array = np.array([[1, 2],[3, 4],[5, 6]]) # 3 * 2矩阵
y_array = np.array([[1, 2, 3, 4],[5, 6, 7, 8]]) # 2 * 4矩阵

1、基础一元及二元运算

我们首先介绍我们最为基础的一些运算,包括我们的加减乘除等等。这些运算可以分成几类,例如分成一元或二元运算、分成算数计算、逻辑计算,算数计算其中还有位运算等等。
常用的算术运算符包括:

+-*/**			# 加、减、乘、除、乘方
//%					# 整数除法、整数除法的余数
&|~^				# 与、或、非、亦或运算
<<>>					# 位运算
@						# 矩阵乘法

常用的比较运算符包括:

<<=					# 小于、小于等于
>>=					# 大于、大于等于
==!=					# 等于、不等于

(1)一元算术运算

一元运算符首先包括+-~,分别表示正、负和按位取反:

+ x   			# np.positive(x) 
- x   			# np.negative(x) 
~x   			# np.invert(x_bit) 
~x_bool   		# np.invert(x_bool) 

执行上述代码,各自的运算结果如下:

[100.45 100.02 100.    99.97 100.05] 
[-100.45 -100.02 -100.    -99.97 -100.05] 
[ -6  -7  -6 -11 -14] 
[False  True False False  True]

其余一元运算函数包括:

np.modf(x) 		    # 原值的整数和小数部分,形式为以两个ndarray数组组成的元组
np.fabs(x)			# 原值的绝对值,另有np.absolute(x)
np.reciprocal(x) 	# 原值的倒数
np.sqrt(x) 		    # 原值的平方根 square root,等同于x ** (1/2)
np.cbrt(x) 		    # 原值的立方根 cube root,等同于x ** (1/3)
np.square(x) 		# 原值的平方,等同于x ** (2)

(2)二元算术运算

可以通过运算符完成的包括+-*///%**,这些运算符实现的运算含义和前面所描述的一致:

x + y 				# np.add(x, y) 
 x - y 				# np.subtract(x, y) 
 x * y 				# np.multiply(x, y) 
 x / y 				# np.divide(x, y), 另有np.true_divide(x, y)
 x // y 			# np.floor_divide(x, y) 
 x % y 				# np.mod(x, y), 另有np.fmod(x, y)、np.remainder(x, y)
 x ** y 			# np.power(x, y), 另有np.float_power(x, y)
 x_array @ y_array 	# np.dot(x_array, y_array) 

各自的运算结果如下:

[102.089 100.598 101.031 101.001 100.64 ]
[98.811 99.442 98.969 98.939 99.46 ]
[164.63755  57.81156 103.1     103.06907  59.0295 ]
[ 61.28737035 173.0449827   96.99321048  96.96411251 169.57627119] 
[ 61. 173.  96.  96. 169.] 
[0.471 0.026 1.024 0.994 0.34 ]
[1910.71517777   14.32353453  115.34532578  115.30964964   15.14007703] 
[[11 14 17 20]
[23 30 37 44]
[35 46 57 68]]

其余二元算数函数包括:

np.divmod(x, y) 		# 同时返回相除的整数部分和余数,形式为以两个
					# ndarray数组组成的元组,等于floor_divide和mod的合并
np.gcd(x_int, y_int) 	# 最大公约数,greatset common divisor
np.lcm(x_int, y_int)		# 最小公倍数,lowest common multiple
np.maximum(x, x1) 		# 两个数组中对应元素的较大值,另有np.fmax(x, x1)
np.minimum(x, x1) 		# 两个数组中对应元素的较小值,另有np.fmin(x, x1)

各自的运算结果如下:

array([ 61., 173.,  96.,  96., 169.]), array([0.471, 0.026, 1.024, 0.994, 0.34 ])) 
[ 1  5  3  4 11]
[ 91  25 120  80  66]
[104.15 100.02 100.   102.25 100.05]
[100.45  98.72 100.    99.97  99.65]

(3)二元逻辑运算

首先是**布尔运算中的逻辑运算符&|~^在这里插入代码片

x_bool & y_bool 			# np.logical_and(x_bool, y_bool) 
x_bool | y_bool 			# np.logical_or(x_bool, y_bool) 
~x_bool 					# np.logical_not(x_bool) 
x_bool ^ y_bool 			# np.logical_xor(x_bool, y_bool) 

各自的运算结果如下:

[False False  True False False] 
[ True False  True  True  True] 
[False  True False False  True] 
[ True False False  True  True]

接下来是二进制位的逻辑运算符&|~^,这些符号同样适用于二进制位。为便于展示,这里用二进制形式展示各二进制值:

[format(intvalue, '#b') for intvalue in (x_bit & y_bit)] 	# np.bitwise_and(x_bit, y_bit)
											# 位运算的与运算
[format(intvalue, '#b') for intvalue in (x_bit | y_bit)]	# np.bitwise_or(x_bit, y_bit)
											# 位运算的或运算
[format(intvalue, '#b') for intvalue in (~x_bit)] 		# np.invert(x_bit)
											# 位运算的非运算
											# 原码为补码取反后加1
[format(intvalue, '#b') for intvalue in (x_bit ^ y_bit)] 	# np.bitwise_xor(x_bit, y_bit)
											# 位运算的异或运算

各自的运算结果如下:

['0b1', '0b10', '0b101', '0b1000', '0b101']
['0b111', '0b1110', '0b101', '0b1111', '0b1111']
['-0b110', '-0b111', '-0b110', '-0b1011', '-0b1110']
['0b110', '0b1100', '0b0', '0b111', '0b1010']

二进制位的位运算符 <<>>,为便于展示,这里用二进制形式展示各二进制值:

[format(intvalue, '#b') for intvalue in (x_bit)] 
[format(intvalue, '#b') for intvalue in (x_bit << y_mv)] 	# np.left_shift(x_bit, y_mv)
									# 将x向左移y位。
[format(intvalue, '#b') for intvalue in (x_bit >> y_mv)] 	# np.right_shift(x_bit, y_mv)
											# 将x向右移y位

各自的运算结果如下:

['0b101', '0b110', '0b101', '0b1010', '0b1101']
['0b1010', '0b1100', '0b1010', '0b10100', '0b11010']
['0b10', '0b11', '0b10', '0b101', '0b110']

除了上述逻辑运算外,其他逻辑运算函数包括:

np.all(x_bool) 			# 是否所有的元素都为True
np.any(x_bool) 			# 是否至少有一个元素为True
np.isnan(w) 			# 原值是否为缺失值
np.isinf(w)				# 原值是否为无穷大
# 另有np.isposinf(w)和np.isneginf(w),判断正/负无穷大	
np.isfinite(w) 		 # 原值是否不为空值或异常值

各自的运算结果如下:

False
True
[False  True  True False False]
[ True False False False False]
[False False False  True  True]

(4)二元比较运算

比较运算符主要包括<<=>>===!=,各自符号的含义和本节最初所提到的各类二元比较符号的含义相同:

x > x1  				# np.greater(x, x1)
x >= x1 				# np.greater_equal(x, x1)
x < x1 				# np.less(x, x1)
x <= x1 				# np.less_equal(x, x1)
x == x1 				# np.equal(x, x1)
x != x1 				# np.not_equal(x, x1)

各自的运算结果如下:

[False  True False False  True]
[False  True  True False  True]
[ True False False  True False]
[ True False  True  True False]
[False False  True False False]
[ True  True False  True  True]

对于赋值和算术运算结合在一起的运算符+=*=等,除了 @ 之外的所有的二元算术运算符都适用:

 x_tmp1 = x.copy() 
 x_tmp2 = x.copy()
 x_tmp3 = x_bool.copy()
 x_tmp4 = x_bool.copy()

 x_tmp1 *= 2
 x_tmp2 *= y
 x_tmp3 ^= True
 x_tmp4 ^= y_bool

 x_tmp1
 x_tmp2
 x_tmp3
 x_tmp4

通过上述运算后,各自的结果如下:

[200.9  200.04 200.   199.94 200.1 ]
[164.63755  57.81156 103.1     103.06907  59.0295 ] 
[False  True False False  True]
[ True False False  True  True]

关于算术运算和赋值运算结合的操作,有一件事需要提醒一下,这就是运算结果的数据类型由整个运算过程的所有对象决定。

2、三角函数

三角函数的运算都是基于弧度的,角度的话需要运用函数库另外转换。在此,仍然建立我们的一些参数序列:

x_deg = np.array([0, 30, 45, 60, 90])
x_rad = np.array([0, np.pi/6, np.pi/4, np.pi/3, np.pi/2])

x_arcvalue1 = np.array([0, 0.5, np.sqrt(2)/2, np.sqrt(3)/2, 1])
x_arcvalue2 = np.array([0, np.sqrt(3)/3, 1, np.sqrt(3), 1e8])

x_side = np.array([2.5, 4.3, 1.0, 5.4, 3.3])
y_side = np.array([5.4, 1.8, 2.6, 3.3, 4.2])

我们以弧度的数组举例:

np.sin(x_rad)
np.cos(x_rad)
np.tan(x_rad)

输出结果如下:

array([0.        , 0.5       , 0.70710678, 0.8660254 , 1.        ])
array([1.00000000e+00, 8.66025404e-01, 7.07106781e-01, 5.00000000e-01,
       6.12323400e-17])
array([0.00000000e+00, 5.77350269e-01, 1.00000000e+00, 1.73205081e+00,
       1.63312394e+16])

反三角函数返回的值也是弧度,便于展示结果,在此将反三角函数的结果转成角度:

(np.arcsin(x_arcvalue1) / np.pi) * 180
(np.arccos(x_arcvalue1) / np.pi) * 180
(np.arctan(x_arcvalue2) / np.pi) * 180 		# 这里可以看到这里的arctan的精度不高,
							# 在1e8的情况下已经算成90度

输出结果如下:

array([ 0., 30., 45., 60., 90.])
array([90., 60., 45., 30.,  0.])
array([ 0.        , 30.        , 45.        , 60.        , 89.99999943])

配套的,弧度和角度的互转如下:

np.deg2rad(x_deg) 			# 角度转弧度,等同的函数还有np.radians(x_deg)
np.rad2deg(x_rad)			# 弧度转角度,等同的函数还有np.degrees(x_rad)

各自的运算结果如下:

array([0.        , 0.52359878, 0.78539816, 1.04719755, 1.57079633])
array([ 0., 30., 45., 60., 90.])

3、指对数工具

我们此前提到的的 ** 符号能支持我们的指数运算,但之前所提到的函数并没有支持取对数的运算。在此,我们逐一介绍一些以e、2、10位底数的指数和对数运算。我们先定义一些数据:

x = np.array([100.45, 100.02, 100.0,  99.97, 100.05])
y = np.array([1.639, 0.578, 1.031, 1.031, 0.59])
y1 = np.array([0.876, 1.452, 1.822, 0.932, 0.433])

x_int = np.array([13, 25, 24, 16, 33])
y_int = np.array([7, 5, 15, 20, 22])

(1)指数部分

np.exp(y_int) 			# 计算一个自然对数为底的指数值,等同于np.e ** y
np.expm1(y_int) 		# exp minus 1, 即计算一个自然对数为底的指数值减1
np.exp2(y_int)			# 计算一个2为底的指数值

运算结果如下:

[  2.71828183   7.3890561   20.08553692  54.59815003 148.4131591 ]
[  1.71828183   6.3890561   19.08553692  53.59815003 147.4131591 ]
[ 2.  4.  8. 16. 32.]

(2)对数部分

np.log(x_int) 			# 以自然对数为底的对数
np.log1p(x_int) 		# log 1 plus, 以自然对数为底的对数值加1 
np.log2(x_int) 		# 以2为底的对数
np.log10(x_int) 		# 以10为底的对数

各自的运算结果如下:

[2.56494936 3.21887582 3.17805383 2.77258872 3.49650756] 
[2.63905733 3.25809654 3.21887582 2.83321334 3.52636052] 
[3.70043972 4.64385619 4.5849625  4.         5.04439412] 
[1.11394335 1.39794001 1.38021124 1.20411998 1.51851394]

指对数结合的一些其他函数:

np.logaddexp(y, y1) 		# 对于x1,x2, 计算log(exp(x1) + exp(x2)).
np.logaddexp(y, y1) 		# 对于x1,x2, 计算log2(exp2(x1) + exp2(x2)).
np.ldexp(x_int, y_int) 		# 返回 x * (2 ** y)
np.frexp(x_int) 			# 将原值分解为尾数和以2为底的指数
						# 尾数 * ( 2 ** 指数) = 原值

各自的运算结果如下:

[2.02171871 1.8007389  2.19589957 1.67587181 1.20772515] 
[2.02171871 1.8007389  2.19589957 1.67587181 1.20772515] 
[1.664000e+03 8.000000e+02 7.864320e+05 1.677721e+07 1.384120e+08] 
(array([0.8125 , 0.78125 , 0.75, 0.5, 0.515625]), array([4, 5, 5, 5, 6], dtype=int32))

4、精度调整工具

精度调整的工具可能比我们想象的要多许多,我们这里做一个归纳。我们指出所需要的数据:

y = np.array([1.639, 0.578, 1.031, 1.031, 0.59])
z = np.array([-4.5, -3, 2.5, 0.76, -0.5])
z1 = np.array([-4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5])

在精度调整中,最重要的就是**np.round()函数**,四舍五入保存至个位,有一个等同的函数np.around()。这个函数可以补充一个数字指定保存到几位小数,默认是0也即整数,若为负数,则精度的小数点向左移动(即为-1时保存到10位、为-2时保存到百位):

np.round(y) 			# 等同的函数有np.around(y)
np.round(y, 1)
np.round(y, -1)

输出结果如下:

[2. 1. 1. 1. 1.] 
[1.6 0.6 1.  1.  0.6] 
[0. 0. 0. 0. 0.]

精度调整有一个需要注意的重要问题在于尾数是5(例如0.5)时候的情况,需要专门拿出来提示。它并不是我们默认的满5进1,而是在尾数为5时,前一位是奇数则进一,如果是偶数则舍去。而且,此时四舍五入计算所得的0是有符号的:

np.round(z1)

输出结果如下:

array([-4., -4., -2., -2., -0.,  0.,  2.,  2.,  4.,  4.])

还有一些取原值的整数部分、小数部分等等的函数,归纳如下:

np.sign(z) 		# 符号(正为1,负为-1,0为0)
np.rint(z) 		# 最接近的整数
np.floor(z) 		# 不大于原值的最大整数
np.ceil(z) 			# 不小于原值的最小整数
np.trunc(z) 		# 截断小数部分,只留下整数部分
np.modf(z) 		# 同时保留原值的整数和小数部分
				# 形式为以两个ndarray数组组成的元组

各自的运算结果如下:

[-1. -1.  1.  1. -1.]
[-4. -3.  2.  1. -0.] 
[-5. -3.  2.  0. -1.] 
[-4. -3.  3.  1. -0.] 
[-4. -3.  2.  0. -0.] 
(array([-0.5 , -0.  ,  0.5 ,  0.76, -0.5 ]), array([-4., -3.,  2.,  0., -0.]))

5、向量元素和、差分、乘积运算

这一块包含了向量内部元素的和、乘积与差分,同时也补充了二维和三维向量的叉积的计算方式。本节所需要的数据如下:

x = np.array([100.45, 100.02, 100.0,  99.97, 100.05])
y = np.array([1.639, 0.578, 1.031, 1.031, 0.59])
y_cross1 = np.array([1.639, 0.578, 1.031])
y_cross2 = np.array([0.578, 1.031, 1.031])

x_nan = np.array([100.45, 100.02, np.nan,  99.97, 100.05])
y_nan = np.array([1.639, np.nan, 1.031, np.nan, 0.59])

x_array = np.array([[1, 2],[3, 4],[5, 6]]) # 3 * 2矩阵
y_array = np.array([[1, 2, 3, 4],[5, 6, 7, 8]]) # 2 * 4矩阵

(1)求向量和、累积和

之前的函数,在处理nan值时由于nan的性质,返回结果会变成nan值,对于求和和乘积,有能够忽略nan值参与计算的方法,即在各种函数名之后加一个nan,例如这里的求和函数np.sum()忽略nan值的同等方法为np.nansum()。之后讲到的统计函数也有能够忽略nan值的统计函数,形式和这里的很类似:

np.sum(x) 			# 求向量各元素的和
np.sum(x_nan)			# 当向量中元素存在nan值时,会返回nan值
np.nansum(x_nan) 		# 求向量各非nan值元素的和,忽略nan值

np.cumsum(x) 			# 求向量各元素的累积和,返回一个向量,
					# 各元素的值是原向量内该位置元素及之前元素的和
np.cumsum(x_nan) 		# 但请注意这个函数并不忽略inf值,可以自行尝试一下
np.nancumsum(x_nan)	# 求向量各非nan值元素的累积和,当遇到nan值将其看做0

各自的运算结果如下:

500.49
nan
400.49

[100.45 200.47 300.47 400.44 500.49] 
[100.45 200.47    nan    nan    nan] 
[100.45 200.47 200.47 300.44 400.49]

(2)求向量积、累积向量积

计算方式与刚刚的np.sum()np.cumsum()相似。同样,也有忽略nan值的对应方法:

np.prod(y) 			# 求向量各元素的积
np.prod(y_nan)
np.nanprod(y_nan) 		# 求向量各非nan值元素的积

np.cumprod(y) 		# 求向量各元素的累积乘积,返回一个向量,
					# 各元素的值是原向量内该位置元素及之前元素的积
np.cumprod(y_nan)		# 同上,不忽略inf值
np.nancumprod(y_nan) 	# 求向量各非nan值元素的累积乘积,当遇到nan值将其看做1

各自的运算结果如下:

0.5941226838005798 
nan 
0.9969873099999998 

[1.639      0.947342   0.9767096  1.0069876  0.59412268] 
[1.639   nan   nan   nan   nan] 
[1.639      1.639      1.689809   1.689809   0.99698731]

(3)求差分

① np.diff()
np.diff()函数主求差分,默认求一阶差分,即前一个值减去后一个值。可传一个补充参数描述求第n阶差分,这时就是一阶差分的迭代调用。调用方法及运算结果如下:

np.diff(x)
np.diff(y, 2) 		# 等同于np.diff(np.diff(y))

输出结果如下:

[-0.43 -0.02 -0.03  0.08] 
[ 1.514 -0.453 -0.441]

② np.ediff1d()
np.ediff1d()只求一阶差分,但是允许补充to_endto_begin参数,作为返回序列的头和尾,头部和尾部的值并不参与到原始的求差分运算中。调用方法及运算结果如下:

np.ediff1d(y, 10, [15, 20]) # 位置上第二个参数是to_end,第三个参数是to_begin

输出结果如下:

array([ 15.   ,  20.   ,  -1.061,   0.453,   0.   ,  -0.441,  10.   ])

③ np.gradient()
np.gradient()函数求梯度,允许接收n维数组调用方法及运算结果如下:

np.gradient(y)
np.gradient(y_array)

输出结果如下:

[-1.061  -0.304   0.2265 -0.2205 -0.441 ]
[array([[4., 4., 4., 4.], [4., 4., 4., 4.]]), array([[1., 1., 1., 1.], [1., 1., 1., 1.]])]

④ np.cross()
np.cross(),求向量叉积( 一般对应相乘求的是内积,叉积是a * b * sin(θ),求的是向量a和b构成平面的法向量。在空间向量中,三维叉积的运算结果是a=[a1,a2,a3],b=[b1,b2,b3],则a×b=[a2b3-a3b2, a3b1-a1b3, a1b2-a2b1]),接收的数组必须是二维或是三维的(对于高维数组,最后一维的维度是2或3)。调用方法及运算结果如下:

np.cross(y_cross1, y_cross2)

输出结果如下:

[-0.467043, -1.093891,  1.355725]

二、排序与查找

1、排序

这里准备一些需要的数据:

x = np.array([100.45, 100.02, 100.0,  99.97, 100.05])
y = np.array([1.639, 0.578, 1.031, 1.031, 0.59])

x_nan = np.array([100.45, 100.02, np.nan,  99.97, 100.05])
y_nan = np.array([1.639, np.nan, 1.031, np.nan, 0.59])

xy_ary = np.array([x, y])				# 一个2 * 5的数组 
xy_ary_nan = np.array([x_nan, y_nan])

(1)列表排序

列表的排序使用函数.sort()sorted(),原排序结果是从小到大,比较规则按照各种元素(如数值、字符串、列表等)之间既有的比较规则.

① .sort()

.sort()函数是一个列表的成员函数。它可以接两个参数,keyreverse。这个函数没有返回值,是在原列表上进行的修改。常用的参数如下:

key:它是一个函数的名字,表示对列表中的元素,用一个函数处理后的值做排序。
reverse:它是表示是否需要再做一次反序,将结果从大到小输出。

具体示例如下:
先讲解**reverse参数**:

 a = [1, -3, 5, 6, -9]
 a.sort()
 print(a)

 a = [1, -3, 5, 6, -9]
 a.sort(reverse = True)
 print(a)

输出结果如下:

[-9, -3, 1, 5, 6]
[6, 5, 1, -3, -9]

再来讲解**key参数**。它可以用我们的一个函数名,也可以是一个匿名函数的定义。在此我们单独定义一个求平方的函数作为传递给key的参数:

def a2(a):
     return a ** 2
a = [1, -3, 5, 6, -9]
a.sort(key = a2, reverse = True) 		# 对a的平方值进行排序,同时将结果反序输出
								# 传入key参数后,不是对原始值,
								# 而是对列表元素在函数处理后的结果进行排序
print(a)

输出结果如下:

[-9, 6, 5, -3, 1]
② sorted()

.sort()不同,sorted()是一个内置的方法,它能接收的参数、具体的用法都是一样的,但是它有返回值,而且不会改变原列表。我们参考刚刚的例子,重新使用这两个参数以展示其效果,因为用法是相似的,所以不再重复讲解:

a = ['one', 'two', 'three', 'four']
print(sorted(a))					# 将a升序排序
print(sorted(a, reverse = True))		# 将a降序排序
print(sorted(a, key = len))			# 将a根据字符串长度进行升序排序
print(a)							# 验证这种修改并未影响原值

输出结果如下:

['four', 'one', 'three', 'two']
['two', 'three', 'one', 'four']
['one', 'two', 'four', 'three']
['one', 'two', 'three', 'four']

总之,.sort()是列表的成员函数,用待排序的列表加点 . 访问,而sorted()是内置方法,直接接一个待排序列表参数。sort会改变原列表的值,没有返回值,而sorted不会改变原值,返回值是排序完毕的列表。这些细节需要多多注意。

(2)numpy数组排序

① np.sort()

对值做排序最基础的排序工具是np.sort()。一般可以用得上的是axis参数,指示按照哪个维度排序,默认是最后一个维度,如果为None则将数组压成一维进行排序。
这个函数结果产生的排序结果是升序的,没有参数可以指示让输出结果降序排列。当然在前面的课程里,怎么将数组反序我们已经学过了(访问[::-1])。同时,排序的结果里面缺失值会被放在最后面,我们举如下例子观察效果:

np.sort(xy_ary)					# 对最末一个维度进行排序
np.sort(xy_ary, axis = 0)				# 对axis为0的轴进行排序
np.sort(xy_ary, axis = None)[::-1]		# 将所有数据压成一维,排序后进行反序
np.sort(xy_ary_nan)					# 对带有nan值的数组进行排序

各自的运算结果如下:

[[ 99.97  100.    100.02  100.05  100.45 ]
 [  0.578   0.59    1.031   1.031   1.639]]

[[  1.639   0.578   1.031   1.031   0.59 ]
 [100.45  100.02  100.     99.97  100.05 ]] 

[100.45  100.05  100.02  100.  99.97    1.639   1.031   1.031   0.59   0.578] 


[[ 99.97  100.02  100.05  100.45      nan]
 [  0.59    1.031   1.639     nan     nan]]

np.sort()有两个近似的函数,一个是numpy数组的.sort()方法,这是numpy数组的成员函数。这个函数会直接改变原值,无返回值。axis参数近似np.sort,但不能传递None

xy_ary_tmp = np.array([x, y]) 		# 重建一个为了防止xy_ary被修改
xy_ary_tmp.sort()
xy_ary_tmp

y_ary_tmp = np.array([x, y]) 		# 重建一个为了防止xy_ary被修改
xy_ary_tmp.sort(axis = 0)
xy_ary_tmp

输出结果如下:

[[ 99.97  100.    100.02  100.05  100.45 ]
 [  0.578   0.59    1.031   1.031   1.639]]

[[  1.639   0.578   1.031   1.031   0.59 ]
 [100.45  100.02  100.     99.97  100.05 ]] 
② np.msort()

其方法为对第一维数据进行排序,处理结果类似于np.sort(xy_ary, axis = 0)

np.msort(xy_ary)

输出结果如下:

array([[   1.639,    0.578,    1.031,    1.031,    0.59 ],
       [ 100.45 ,  100.02 ,  100.   ,   99.97 ,  100.05 ]])
③ np.argsort()

这个函数返回的是一个下标列表,下标列表的顺序是被自小到大重排的各个元素的下标。用原数组访问这个返回结果会得到原数组排序的结果,这就是它与数组排序的结果的关系。
np.sort()函数相同,缺失值的下标会被放在最后面;同时,axis参数同样适用。

np.argsort(xy_ary)
np.argsort(xy_ary_nan)
np.argsort(xy_ary, axis = 0)
np.argsort(xy_ary, axis = None)
ind = np.argsort(x)
x[ind]		# 用原数组访问返回结果,得到排序后的数组

各自的运算结果如下:

[[3 2 1 4 0]
 [1 4 2 3 0]]

[[3 1 4 0 2]
 [4 2 0 1 3]]

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

[6 9 7 8 5 3 2 1 4 0]
array([  99.97,  100.  ,  100.02,  100.05,  100.45])

(3)Series和DataFrame排序

sort_values().sort_index()都是Series和DataFrame的成员函数,我们围绕DataFrame进行讲解。

① .sort_values()

该函数是对值进行排序,我们会主要使用如下参数:

by:指示排序的面向对象列表,它是第一个参数、必须给出。可以传一个字符串或一个列/行索引值的列表,函数按给出排序列表的先后顺序排序。
axis:指示排序是按行还是按列排序(默认是0即对列样本排序)。
ascending:指示排序是升序或是降序(默认为True即升序),可以传递一个与排序列表等长的布尔列表。
na_position:指示排序列表中如果是空值放在最前还是最后,可选'first', 'last',默认为最后('last')。

各种参数的调用方式举例如下。因为输出的文本量较大,为图简洁,在此不展示程序执行的最终结果:

frame_nan.sort_values(['210X1']) 	# 基本的按单列排序
frame_nan.sort_values(['210X1'], na_position = 'first') # 将空值置于最前面
frame_nan.sort_values([0], axis = 1) 	# 对行样本进行排序														# 传入行索引

frame_ori.sort_values(['TOOL', 'Value']) 		# 对多列进行排序
												# 先排TOOL,再排Value
frame_ori.sort_values(['TOOL', 'Value'], ascending = False) 	# 以降序排列   
② .sort_index()

.sort_values()对应的是.sort_index()函数,它是对行或列的索引排序,因此它的参数没有by。但其他的参数axisascendingna_position都是意义一样的。我们将原来的列表按照打乱顺序,举两个简单的例子如下。同上,在此不展示程序执行的最终结果。

 frame_indexsorted = frame_ori.sort_values(['TOOL', 'Value'])# 打乱原DataFrame索引
 frame_indexsorted.sort_index() 					# 按行重排索引
 frame_indexsorted.sort_index(axis = 1)				# 按列重排索引
 frame_indexsorted.sort_index(ascending = False) 	# 将索引降序排列

至此,我们对我们的排序做一个总结:
列表做排序时,可以自由地定义排序的规则(函数),但一次只能对列表中的各个元素排序,即不能按选择按列排等等。
numpy的数组支持不同的维度排,也可以找到排序顺序对应的元素下标,但是它无法定义排序方式,而且排序只能从小到大排。
pandas dataframe支持按照行或者列的值或者索引排序,排序结果的展示也比较美观,但是它也无法定义排序方法,而且只能排二维的数据。
各种排序方法都有其优缺点,所幸的是我们这些数据结构之间的互转方便,我们能在我们需要的时候进行灵活的运用。

2、查找

这里所说的查找主要有两重意思,主要是面向下标的查找。一是找到最大值、最小值等等,它的值或是所在的下标,二是筛选出满足特定要求的值的元素下标。
我们仍旧建立本节中相应的元素如下:

x = np.array([100.45, 100.02, 100.0,  99.97, 100.05])
y = np.array([1.639, 0.578, 1.031, 1.031, 0.59])

x_nan = np.array([100.45, 100.02, np.nan,  99.97, 100.05])
y_nan = np.array([1.639, np.nan, 1.031, np.nan, 0.59])

x_0 = np.array([100.45, 100.02, 0,  99.97, 100.05])
y_0 = np.array([1.639, 0, 1.031, 0, 0.59])

xy_ary = np.array([x, y])
xy_ary_nan = np.array([x_nan, y_nan])
xy_ary_0 = np.array([x_0, y_0])

(1)查找最大值、最小值

首先是查找最大值、最小值以及所在的下标,我们用np.argmax()np.argmin()函数。类似于np.nansum()np.nanprod()函数,这里也有np.nanargmax()np.nanargmin()函数可以调用。
求最大值及其下标的函数整理如下:

np.max(xy_ary)							# 计算整个数组最大值
np.max(xy_ary, axis = 1) 					# 以轴1分别计算最大值

np.argmax(xy_ary)						# 数组转为1维后,最大值的下标
np.argmax(xy_ary, axis = 1)				# 以轴1分别计算最大值的下标

np.nanmax(xy_ary_nan, axis = 1)			# 以轴1分别计算最大值,忽略缺失值
np.nanargmax(xy_ary_nan, axis = 1) 		# 以轴1分别计算最大值下标,忽略缺失值

输出结果如下:

100.45
[100.45    1.639]
0 
[0 0]
[100.45    1.639]
[0 0]

与求最大值相对应,配套的求最小值及最小值下标的函数整理如下,其调用方式及参数与最大值时是相同的:

np.min(xy_ary)							# 计算整个数组最小值
np.min(xy_ary, axis = 1) 					# 以轴1分别计算最小值

np.argmin(xy_ary)						# 数组转为1维后,最小值的下标
np.argmin(xy_ary, axis = 1)				# 以轴1分别计算最小值的下标

np.nanmin(xy_ary_nan, axis = 1)			# 以轴1分别计算最小值,忽略缺失值
np.nanargmin(xy_ary_nan, axis = 1) 		# 以轴1分别计算最小值下标,忽略缺失值

输出结果如下:

0.578 
[99.97   0.578] 
6 
[3 1] 
[99.97  0.59] 
[3 4]

(2)查找满足特定条件的元素:np.where()

它接收的是一个表达式。我们曾经也学过基于布尔值的元素访问,下面用两个例子来比较np.where()函数和基于布尔数组的访问效果:

ind_100p = np.where(xy_ary > 100)		# 找出所有为0的元素
xy_ary[ind_100p] 						# 访问满足条件的所有元素

xy_ary[xy_ary > 100]  					# 基于布尔值筛选的表达式
									# 基于布尔值的访问

输出结果如下:

[ 100.45  100.02  100.05]
[ 100.45  100.02  100.05]

上面返回的结果相同。但相比基于布尔数组的访问,应用np.where()函数的一个好处是能够返回到数组的下标,因此可以根据一些具体的需求进行其他处理。
这里补充两个有两个判断元素不为0的函数,以及一个对不为0的函数的计数函数,它可以看成np.where()np.sum()函数的一个综合运用:

np.nonzero(xy_ary_0) 					# 即np.where(xy_ary_0 != 0)
np.flatnonzero(xy_ary_0)				# 即np.where(xy_ary_0.flatten() != 0)
np.count_nonzero(xy_ary_0, axis = 1) 		# 即np.sum(xy_ary_0 != 0, axis = 1)

各自的运算结果如下:

(array([0, 0, 0, 0, 1, 1, 1], dtype=int64), array([0, 1, 3, 4, 0, 2, 4], dtype=int64)) 
[0 1 3 4 5 7 9] 
[4 3]

(3)查找不重复的元素 :np.unique()

查找部分还有一项重要功能,即查找所有不重复的元素。我们此前学习过利用内置的方法set,将数据做成集合来实现查找不重复值的目的,但那种方法对于大数据集运算时间较长。对于numpy数组,我们用np.unique()方法;而对于Series或DataFrame,我们用其成员方法的.unique()。它们都支持对去重,但效果因为numpy和pandas本身的限制略有不同:

a = np.array([1, 2, 'alpha', 'alpha', 2])
s = pd.Series([1, 2, 'alpha', 'alpha', 2])
np.unique(a)
s.unique()

运算结果如下:

['1' '2' 'alpha'] 			# numpy数组所有元素都是同一类型,因此数字会转成字符串
[1 2 'alpha']			# Series则没有这一限制,这里它的数据类型为Object

这种查找所有不重复项的功能,可以和《Python学习笔记(8-2):数据分析——数据预处理》中的pd.drop_duplicates去重函数进行比较。


对于缺乏Python基础的同仁,可以通过免费专栏🔥《Python学习笔记(基础)》从零开始学习Python

结语

请始终相信“Done is better than perfect” ,不要过分追求完美,即刻行动就是最好的开始, 一个模块一个模块地积累和练习,必将有所收获。
还有其他的问题欢迎在评论区留言📝!


[版权申明] 非商业目的注明出处可自由转载,转载请标明出处!!!
博客:butterfly_701c

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值