常量 | 含义 |
---|---|
np.pi |
圆周率 |
np.e |
自然常数 |
np.euler_gamma |
欧拉常数 |
np.nan |
非数字 |
np.inf |
无穷大 |
两个np.nan
不相等,但两个np.inf
是相等的,判断一个数组元素是否是np.nan
或np.inf
,需要使用np.isnan()
和np.isinf()
这两个相应的函数,而不是使用两个等号的逻辑表达式
import numpy as np
a = np.array([1, 2 ,np.nan, np.inf])
print(a.dtype)
a[0] = np.nan
a[1] = np.inf
print(a)
# 两个np.nan不相等
print(a[0] == a[2])
# 两个np.inf
print(a[1] == a[3])
# 判断一个数组元素是否是np.nan
print(np.isnan(a[0]))
# 判断一个数组元素是否是np.inf
print(np.isinf(a[1]))
import numpy as np
a = np.array([9, 3, np.nan, 5, 3])
a = np.repeat(a, 2)[:-1]
a[1::2] += (a[2::2]-a[1::2])/2
print(a)
对于NumPy
函数的使用,可能会有这样的困惑:实现同样的功能,一个函数却有两种写法;有时以为某个函数可以有两种写法,但用起来却会出错,归纳起来,这些困惑有以下三种类型
都是求和、求极值,下面两种写法有什么区别么?
import numpy as np
a = np.random.random(10)
print(a.max(), np.max(a))
print(a.sum(), np.sum(a))
同样是赋值,为什么深复制copy()
两种写法都行,而浅复制view()
则只有数组的方法?
import numpy as np
a = np.random.random(5)
print(a.copy())
print(np.copy(a))
print(a.view())
# 会报错
print(np.view(a))
为什么where()
不能作为数组ndarray
的函数,必须作为NumPy
的函数
import numpy as np
a = np.random.random(10)
print(np.where(a>0.5))
# 会报错
print(a.where(a>0.5))
以上这些差异取决于函数在不同的命名空间是否有映射。数组的大部分函数在顶层命名空间有映射,因此可以有两种写法。但数组的一小部分函数没有映射到顶层命名空间,所以只能有一种写法。而顶层命名空间的大部分函数,也都只有一种写法。
类别 | NumPy函数 | Math模块函数 | 功能 |
---|---|---|---|
数学常数 | np.e |
math.e |
自然常数 |
数学常数 | np.pi |
math.pi |
圆周率 |
舍入函数 | np.ceil() |
math.ceil() |
进尾取整 |
舍入函数 | np.floor() |
math.floor() |
去尾取整 |
舍入函数 | np.around() |
四舍五入到指定精度 | |
舍入函数 | np.rint() |
四舍五入到最近整数 | |
快速转换函数 | np.deg2rad() 、np.radians() |
np.radians() |
度转弧度 |
快速转换函数 | np.rad2deg() 、np.degrees() |
math.degrees() |
弧度转度 |
幂指数对数函数 | np.hypot() |
math.hypot() |
计算直角三角形的斜边 |
幂指数对数函数 | np.square() |
平方 | |
幂指数对数函数 | np.sqrt() |
math.sqrt() |
开平方 |
幂指数对数函数 | np.power() |
math.pow() |
幂 |
幂指数对数函数 | np.exp() |
math.exp() |
指数 |
幂指数对数函数 | np.log() 、np.log10() 、np.log2() |
math.log() 、math.log10() 、math.log2() |
对数 |
三角函数 | np.sin()/arcsin() |
math.sin()/asin() |
正弦/反正弦 |
三角函数 | np.cos()/arccos() |
math.cos()/acos() |
余弦/反余弦 |
三角函数 | np.tan()/arctan() |
math.tan()/atan() |
正切/反正切 |
import numpy as np
import math
# 两个模块的自然常数相等
print(math.e == np.e)
# 两个模块的圆周率相等
print(math.pi == np.pi)
print(np.ceil(5.3), np.ceil(-5.3))
print(np.floor(5.8), np.floor(-5.8))
print(np.around(5.87, 1))
print(np.rint(5.87))
print(np.degrees(np.pi/2))
print(np.radians(180))
# 求平面上任意两点的距离
print(np.hypot(3,4))
print(np.power(3, 1/2))
print(np.log2(1024))
print(np.exp(1))
# 正弦、余弦函数的周期是2π
print(np.sin(np.radians(30)))
print(np.sin(np.radians(150)))
# 反正弦、反余弦函数的周期则是π
print(np.degrees(np.arcsin(0.5)))
上述代码中使用的函数都是单一的数值,实际上,这些函数也都可以用到NumPy
数组上。例如,平面直角坐标系中有1000万个点,他们的x坐标和y坐标都分布在[0,1)
区间,哪一个点距离点(0.5,0.5)
最近呢?使用Numpy
数组计算的代码如下
import numpy as np
p = np.random.random((10000000,2))
# 分离每一个点的x和y坐标
x, y = np.hsplit(p, 2)
# 计算每一个点距离点(0.5,0.5)的距离
d = np.hypot(x-0.5, y-0.5)
# 返回最短距离的点的索引号
i = np.argmin(d)
print('距离点(0.5,0.5)最近的点的坐标是(%f,%f),距离为%f' % (*p[i], d[i]))
类别 | 函数 | 功能 |
---|---|---|
查找特殊值 | np.max/min(a,axis=None) |
返回最大值/最小值 |
查找特殊值 | np.nanmax/nanmin(a, axis=None) |
忽略nan返回最大值/最小值 |
查找特殊值 | np.argmax/argmin(a, axis=None) |
返回最大值和最小值的索引号 |
查找特殊值 | np.nanargmax/nanargmin(a, axis=None) |
忽略nan返回最大值和最小值索引号 |
查找特殊值 | np.median(a, axis=None) |
返回中位数 |
查找特殊值 | np.nanmedian(a, axis=None) |
忽略nan返回中位数 |
求和差积 | np.ptp(a, axis=None) |
返回元素最大值与最小值的差 |
求和差积 | np.sum(a, axis=None) |
按指定轴求和 |
求和差积 | np.nansum(a, axis=None) |
忽略nan按指定轴求和 |
求和差积 | np.cumsum(a, axis=None) |
按指定周求累计和 |
求和差积 | np.nancumsum(a, axis=None) |
忽略nan按指定轴求累计和 |
求和差积 | np.diff(a, axis=None) |
按指定轴返回相邻元素的差 |
求和差积 | np.prod(a, axis=None) |
按指定轴求积 |
求和差积 | np.nanprod(a, axis=None) |
忽略nan按指定轴返回算数平均值 |
均值和方差 | np.mean(a, axis=None) |
按指定轴返回算数平均值 |
均值和方差 | np.nanmean(a, axis=None) |
忽略nan按指定轴返回算数平均值 |
均值和方差 | np.average() |
返回所有元素的加权平均值 |
均值和方差 | np.var(a) |
返回数组方差 |
均值和方差 | np.nanvar(a) |
忽略nan返回数组方差 |
均值和方差 | np.std() |
返回数组标准差 |
均值和方差 | np.nanstd() |
忽略nan返回数组标准差 |
相关系数 | np.corrcoef(a, b) |
返回a和b的皮尔逊关系数 |
下面以求最大值和最小值为例,演示忽略np.nan
的必要性
import numpy as np
import math
a = np.random.random(10)
print(np.max(a), np.min(a))
a[1::2] = np.nan # 将索引1,3,5,7,9的元素设置为nan
print(a)
print(np.max(a), np.min(a)) # 此时,min()和max()失效了
print(np.nanmax(a), np.nanmin(a)) # 必须使用nanmax()和nanmin()
方差和标准差
import numpy as np
import math
a = np.random.randint(0,50,(3,4))
print(np.sum(np.square(a-a.mean()))/a.size) # 用方差定义求方差
print(np.var(a)) # 直接用方差函数求方差,结果相同
print(np.sqrt(a.var())) # 对方差开方,即是标准差
print(a.std()) # 直接用标准差函数求标准差,结果相同
pa和pb是两只股票连续30个交易日的股价数组。每日股价收益定义为当日股价与前一个交易日股价之差再除以最后一个交易日的股价
import numpy as np
import math
# 综合运用统计函数,分析两只股票的关联关系和收益率
pa = np.array([
79.66, 81.29, 80.37, 79.31, 79.84, 78.53, 78.29, 78.51, 77.99, 79.82,
80.41, 79.27, 80.26, 81.61, 81.39, 80.29, 80.18, 78.38, 75.06, 76.15,
75.66, 73.90, 72.14, 74.27, 75.27, 76.15, 75.40, 76.51, 77.57, 77.06
])
pb = np.array([
30.93, 31.61, 31.62, 31.77, 32.01, 31.52, 30.09, 30.54, 30.78, 30.84,
30.80, 30.38, 30.88, 31.38, 31.05, 29.90, 29.96, 29.59, 28.71, 28.95,
29.19, 28.71, 27.93, 28.35, 28.92, 29.17, 29.02, 29.43, 29.12, 29.11
])
print(np.corrcoef(pa, pb)) # 两只股票的相关系数为0.867,关联比较密切
pa_re = np.diff(pa)/pa[:-1] # 股价收益率
pb_re = np.diff(pb)/pb[:-1] # 股价与前一个交易日股价差除以最后一个交易日的股价
import matplotlib.pyplot as plt
plt.plot(pa_re)
plt.plot(pb_re)
plt.show()
数据插值是数据处理过程中经常用到的技术,常用的插值有一维插值、二维插值、高阶插值等,常见的算法有线性插值、B样条插值、临近插值等。不过,Numpy只提供了一个简单地一维线性插值函数np.interp()
,其他更加复杂的插值功能放到了Scipy
中。
下面用一个实例来演示NumPy一维线性插值函数的使用方法。假定_x
和_y
是原始的样本点x坐标和y坐标构成的数组,总数只有11个点。如果想在_x
的值域范围内插值更多的点,如增加到33个点,就需要在_x
的值域范围内生成33个点的x坐标构成的数组x,再利用插值函数np.interp()
得到对应的33个点的y坐标构成的数组y。
import numpy as np
import matplotlib.pyplot as plt
_x = np.linspace(0, 2*np.pi, 11)
_y = np.sin(_x)
x = np.linspace(0, 2*np.pi, 33)
y = np.interp(x, _x, _y)
plt.plot(x, y, 'o')
plt.plot(_x, _y, 'o')
plt.show()
拟合又称回归,是指已知某函数的若干离散函数值,通过调整该函数中特定若干待定系数,使得该函数与已知离散函数值的误差达到最小。
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['FangSong'] # 指定字体以保证汉字正常显示
plt.rcParams['axes.unicode_minus'] = False # 正确显示连字符
_x = np.linspace(-1, 1, 201)
_y = ((_x**2-1)**3 + 0.5)*np.sin(2*_x) + np.random.random(201)/10 - 0.1
plt.plot(_x, _y, ls='', marker='o', label="原始数据")
for k in range(4, 8):
g = np.poly1d(np.polyfit(_x, _y, k)) # g是k次多项式
loss = np.sum(np.square(g(_x)-_y)) # g(x)和f(x)的误差
plt.plot(_x, g(_x), label="%d次多项式,误差:%0.3f"%(k,loss))
plt.legend()
plt.show()
np.frompyfunc()
定义广播函数使用np.frompyfunc()
将数值函数转换成数组函数需要提供三个参数:数值函数、输入函数的个数和返回值的个数。另外,np.frompyfunc()
返回的广播函数,其返回值是object类型,最终需要根据实际情况显式地转换数据类型
import numpy as np
def func_demo(x, y):
if x == 0 or y == 0 or x == y:
return 0
elif x&(x-1) == 0 and y&(y-1) == 0: # x和y都是2的整数次幂
return max(x, y)
elif x&(x-1) == 0: # 仅有x等于2的整数次幂
return x
elif y&(y-1) == 0: # 仅有y等于2的整数次幂
return y
else:
return max(x, y)
uf = np.frompyfunc(func_demo, 2, 1)
a = np.random.randint(0, 256, (2,5), dtype=np.uint8)
b = np.random.randint(0, 256, (2,5), dtype=np.uint8)
c = uf(a, b)
print(c.dtype) # 此时c的数据类型为object
c = c.astype(np.uint8) # 改变类型
print(c)
np.vectorize()
定义广播函数np.frompyfunc()
适用于多个返回值的函数。如果返回值只有一个,使用np.vectorize()
定义广播函数更方便,并且还可以通过otypes
参数指定返回数组的元素类型
import numpy as np
def func_demo(x, y):
if x == 0 or y == 0 or x == y:
return 0
elif x&(x-1) == 0 and y&(y-1) == 0: # x和y都是2的整数次幂
return max(x, y)
elif x&(x-1) == 0: # 仅有x等于2的整数次幂
return x
elif y&(y-1) == 0: # 仅有y等于2的整数次幂
return y
else:
return max(x, y)
uf = np.vectorize(func_demo, otypes=[np.uint8])
c = uf(a, b)
print(c) # 此时c的数据类型为uint8
自定义广播函数并不是真正的广播函数,其运行效率和循环遍历几乎没有差别,因此除非确实必要,否则不应该滥用自定义广播函数。