目录
Numpy数组的计算:通用函数
前面的章节中,讲解了Numpy的基础知识.包括Numpy的核心概念,Numpy数组的一些基本操作,例如数组的创建,数组的属性,对值的引用,切片操作,变形分裂和拼接操作.
在接下来的几个章节中,我们将会深入学习Numpy中数据科学计算内容.更准确的说,Numpy提供了简单灵活的接口来优化数组的计算.而我们在接下来的几章中就将学习这些内容.
本章将介绍Numpy的通用函数.
1.缓慢的循环
原生的Python的实现(被称作CPython)在处理某些操作时候非常慢.
一个原因就是因为Python语言的动态性和解释性,每一个数据其实都是一个对象.因此这种灵活性决定了对于序列操作无法像C语言一样被编译成有效地机器码.
虽然目前有一些项目尝试解决这一个缺点,例如:Pypi,Cython和Numba这三个项目.
Pypi的思路是对写下的代码进行实时编译,进而做到在写完程序之后,实际上已经运行了大部分的代码,从而在"表象上"提高速度.
Cython的思路就是将Python代码写成可编译的C语言代码.
Numba项目的思路是将Python代码片段转换成快速的LLVM字节码
但是无论是哪一个项目,都没有原生的Python实现受欢迎.
Python原生代码的相对缓慢通常是因为在进行某些处理的时候,一些小操作不断地重复,进而导致浪费了较多的时间.
例如:我们在对数组中的每一个元素进行循环操作的时候,都要查询每个元素的对象类型,然后找到这个储存这个元素值的地址.对每个元素都要进行这样的操作,所以就浪费了大量的时间.
所以为了解决这个问题,提高Python在进行大量数据处理时缓慢速度,Numpy就进行了一系列的修改.
首先第一个就是确定Numpy中的数组每个元素就是元素值,而非对象.
其次就是使用了通用函数
2.通用函数介绍
通过上面的引入,我们明白了Numpy中的通用函数的目标就是提高Python处理大量数据速度.
Python原生的一些运算符只能对单个的元素值进行,例如:
list_1=[1,2,3,4,5]
for num in list:
num+=1
print(list1)
>>>[2,3,4,5,6]
这样就导致了我们为了进行一个简单的加法运算,就必须要遍历数组.而一旦遍历,就会降低效率.
所以Numpy就重写了Python中的运算符号,避免了遍历
import numpy as np
array_1=np.arange(1,6)
print(array_1+1)
>>>[2,3,4,5,6]
具体实现的方法就是将同一类型的数据(即数组)视为向量.然后Numpy中为向量预留了方便的,静态类型的,可编译的接口.当向量传递到接口后就被推送到了Numpy之下的编译层.从而避免了Python的解释器对数组的遍历.
通用函数就是Numpy中重新将Python的一些原生函数重写了一遍得到的速度更快的函数.
当数组很大的时候,我们就不要考虑使用遍历了,直接使用这些通用函数效率更高
3.基本通用函数
通用函数有两种存在方式:一元通用函数和二元通用函数
有很多通用函数的来源是scipy.special这个Numpy的子模块
下面就将介绍Numpy中常用的通用函数
数组的运算
首先需要明确的是,Python中加减乘除(+ - * / // ** %)等等并不是简单的仅仅是个符号.实际上他们都是函数
只不过我们可以通过符号的形式来使用.
即这些符号都是某个函数的封装器,例如==+==就是add函数的封装器
为了便于对数组的运算,Numpy重写了这些运算符,达到能够对数组进行运算,直接向底层传递向量.
首先是一般计算
import numpy as np
array=np.arang(1,6)
print(array)
print(array+5)
print(array-5)
print(array*2)
print(array/2)
print(array//2)
print(array**2)
print(array%3)
>>>
[1 2 3 4 5]
[ 6 7 8 9 10]
[-4 -3 -2 -1 0]
[ 2 4 6 8 10]
[0.5 1. 1.5 2. 2.5]
[0 1 1 2 2]
[ 1 4 9 16 25]
[1 2 0 1 2]
其次对于算数运算符间的混合运算也成立
下面给出符号和对应通用函数间的关系
| 运算符 | 对应的通用函数 | 描述 |
|---|---|---|
| + | np.add | 加法运算 |
| - | np.subtract | 减法运算 |
| - | np.negative | 负数运算 |
| * | np.multiply | 乘法运算 |
| / | np.divide | 除法运算 |
| // | np.floor_divide | 地板除法运算(向下取整除法) |
| ** | np.power | 指数运算 |
| % | np.mod | 取余/取模运算 |
绝对值函数numpy.absolute/numpy.abs.
Numpy中的求绝对值函数为numpy.absolute(),也可以用别名numpy.abs()来调用
np.abs(数组名)
np.absolute(数组名)
注意:
- 当元素为复数数时,得到复数的模
例如:
array=np.array([-2,1,0,10,2])
print(np.abs(array))
print(np,absolute(array))
>>>
[ 2 1 0 10 2]
[ 2 1 0 10 2]
此外,Numpy的ndarray对象支持原生的abs对象,但是会更慢
三角函数
numpy提供了支持向量的内置三角函数
np.sin(角度)
np.cos(角度)
np.tan(角度)
np.arcsin(角度)
np.arccos(角度)
np.arctan(角度)
注意:
我们可以用Numpy内置的数据pi来参与运算
np.pi
例如:
theta_1=np.linspace(0,np.pi,3);
theta_2=[-1,0,1]
print(np.sin(theta_1))
print(np.cos(theta_1))
print(np.tan(theta_1))
print(np.arcsin(theta_2))
print(np.arccos(theta_2))
print(np.arctan(theta_2))
>>>
[0.0000000e+00 1.0000000e+00 1.2246468e-16]
[ 1.000000e+00 6.123234e-17 -1.000000e+00]
[ 0.00000000e+00 1.63312394e+16 -1.22464680e-16]
[-1.57079633 0. 1.57079633]
[3.14159265 1.57079633 0. ]
[-0.78539816 0. 0.78539816]
指数和对数
Numpy中提供了有关指对数的通用函数
np.exp(数组) #y=e^x
np.exp2(数组) #y=2^x
np.power(数组,数组) #y=x1^x2 两个数组必须同维
np.ln(数组) #y=ln(x)
np.log2(数组) #y=log2(x)
np.log10(数组) #y=log10(x)
举例:
略
4.通用函数的特性
下面将介绍一些通用函数的特性,使用这些通信将极大的便利我们的使用
指定输出
有的时候,当我们需要计算的数组非常大的时候,希望将这些数组计算后的结果直接写入到指定的空数组中,这样就避免了当计算的数组都在十多个G级别的时候,还要把计算得到的数组赋值给新的数组,节省了计算机开销.
指定输出的实现主要是用了out参数
x=np.arange(10)
y=np.empty(10)
np.multiply(x,10,out=y)
print(y)
>>>
[ 0. 10. 20. 30. 40. 50. 60. 70. 80. 90.]
我们也可以把结果写入视图中
x=np.arange(10)
y=np.zeros(20)
np.multiply(x,10,out=y[::2])
print(y)
>>>
[ 0. 0. 10. 0. 20. 0. 30. 0. 40. 0. 50. 0. 60. 0. 70. 0. 80. 0.
90. 0.]
聚合
一个数组本身就具有多个信息.例如:数组的最大值,最小值,均值,方差,等等.
我们称提取一个数组的信息称为聚合.
下面将讲解Numpy中数据聚合的一些操作,更多的在第五节讲解
下面讲解的方法在后面将会讲解的专用函数中有其他的函数可以实现(np.sum,np.prod,np.cumsum,np.cumprod)
numpy.add.reduce方法
numpy.add.reduce方法用于求数组元素的和
np.add.reduce(数组)
其中:
- 当数组为一维数组时候,返回所有元素的的和,当数组为二维数组的是哦户,返回每一列所有元素的和,得到一个一维数组,数组为三维数组时候,返回第三维元素的和,得到一个二维数组
例如:
array_1=np.arange(1,10)
array_2=np.arange(1,10).reshape(3,3)
array_2=np.arange(1,28),reshape(3,3,3)
print(np.add.reduce(array_1))
print(np.add.reduce(array_2))
print(np.add.reduce(array_3))
>>>
45
[12 15 18]
[[30 33 36]
[39 42 45]
[48 51 54]]
numpy.multiply.reduce方法
同样,numpy.multiply.reuce方法用于求数组中元素的乘积
np.multiply.reduce(数组)
和numpy.add.reduce方法需要说明的点一样
array_1=np.arange(1,10)
array_2=np.arange(1,10).reshape(3,3)
array_2=np.arange(1,28),reshape(3,3,3)
print(np.multiply.reduce(array_1))
print(np.multiply.reduce(array_2))
print(np.multiply.reduce(array_3))
>>>
362880
[ 28 80 162]
[[ 190 440 756]
[1144 1610 2160]
[2800 3536 4374]]
accumulate方法
如果我们想要储存上面每一步运算的结果,可以对add和multiply两个通用函数使用accumulate方法
np.add.accumulate(数组)
np.multiply.accumulate(数组)
其中:
- 对应位置上的数就是所有数组加/乘到当前位置上的和
- 返回的数组和原来一样
例如:
print(np.add.accumulate(np.arange(1,10)))
print(np.add.accumulate(np.arange(1,10).reshape(3,3)))
print(np.add.accumulate(np.arange(1,28).reshape(3,3,3)))
>>>
[ 1 3 6 10 15 21 28 36 45]
[[ 1 2 3]
[ 5 7 9]
[12 15 18]]
[[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]]
[[11 13 15]
[17 19 21]
[23 25 27]]
[[30 33 36]
[39 42 45]
[48 51 54]]]
print(np.multiply.accumulate(np.arange(1,10)))
print(np.multiply.accumulate(np.arange(1,10).reshape(3,3)))
print(np.multiply.accumulate(np.arange(1,28).reshape(3,3,3)))
>>>
[ 1 2 6 24 120 720 5040 40320 362880]
[[ 1 2 3]
[ 4 10 18]
[ 28 80 162]]
[[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]]
[[ 10 22 36]
[ 52 70 90]
[ 112 136 162]]
[[ 190 440 756]
[1144 1610 2160]
[2800 3536 4374]]]
外积
最后,任何函数都可以通过outer方法获得两个不同输入数组所有元素对的函数运算结果.
np.multiply.outer(数组1,数组2)
其中:
- 当数组为同维数组时,将升维.例如有列向量左乘行向量
- 当不是同维数组时,低维数组将会进行广播,以完成外积.(什么是广播将在后面介绍)
例如;
print(np.multiply.outer(array_1,array_1))
print(np.multiply.outer(array_2,array_2))
#print(np.multiply.outer(array_3,array_3)) #太长了,略
>>>
[[ 1 7 1 1 9 9]
[ 7 49 7 7 63 63]
[ 1 7 1 1 9 9]
[ 1 7 1 1 9 9]
[ 9 63 9 9 81 81]
[ 9 63 9 9 81 81]]
[[[[ 0 0 0 0]
[ 0 0 0 0]
[ 0 0 0 0]]
[[ 0 4 6 10]
[ 6 6 18 2]
[ 2 12 18 0]]
[[ 0 6 9 15]
[ 9 9 27 3]
[ 3 18 27 0]]
[[ 0 10 15 25]
[15 15 45 5]
[ 5 30 45 0]]]
[[[ 0 6 9 15]
[ 9 9 27 3]
[ 3 18 27 0]]
[[ 0 6 9 15]
[ 9 9 27 3]
[ 3 18 27 0]]
[[ 0 18 27 45]
[27 27 81 9]
[ 9 54 81 0]]
[[ 0 2 3 5]
[ 3 3 9 1]
[ 1 6 9 0]]]
[[[ 0 2 3 5]
[ 3 3 9 1]
[ 1 6 9 0]]
[[ 0 12 18 30]
[18 18 54 6]
[ 6 36 54 0]]
[[ 0 18 27 45]
[27 27 81 9]
[ 9 54 81 0]]
[[ 0 0 0 0]
[ 0 0 0 0]
[ 0 0 0 0]]]]