python科学计算之numpy——ufunc函数用法


Posted in Python onNovember 25, 2019

写在前面

ufunc是universal function的缩写,意思是这些函数能够作用于narray对象的每一个元素上,而不是针对narray对象操作,numpy提供了大量的ufunc的函数。这些函数在对narray进行运算的速度比使用循环或者列表推导式要快很多,但请注意,在对单个数值进行运算时,python提供的运算要比numpy效率高。

四则运算

numpy提供的四则ufunc有如下一些:

python科学计算之numpy——ufunc函数用法

numpy提供的四则运算unfunc能够大大的提高计算效率,但如果运算式复杂,且参与运算的narray过大,会产生大量的中间结果,从而降低计算效率。例如:计算x=a*b+c时,实际上会按照如下方式计算:

t = a*b 
x = t+c 
del t

这会产生两次内存分配,一次时x,一次时t,所以按照

x = a*b 
x = x+c

会节省一次内存的分配,从而提高效率。

比较运算 & bool运算

numpy同时提供了=、<、>等这些比较运算符,这些运算符的结果是bool值或者bool数组。

python科学计算之numpy——ufunc函数用法

np.array([1,2,3]) < np.array([0,3,4])
array([False, True, True], dtype=bool)

逻辑运算and、or、not、xor等由于python提供了,所以numpy的这些逻辑运算符是以logical_开头的函数

a = np.arange(5)
b= np.arange(4,-1,-1)
## print a==b
[False False True False False]
## print a>b
[False False False True True]
## print np.logical_or(a == b, a > b)
[False False True True True]

对两个bool数组进行逻辑运算时,将发生ValueError异常,因为bool值也是True和False,numpy无法确定运算目的,可以使用numpy.any()和numpy.all()函数,他们的使用方法和python的any()、all()函数用法相同。以bitwise_开头的函数时用于位运算,如(bitwise_and、bitwise_or)等,也可以使用&、|、~和^来进行运算。

除了numpy提供的内置ufunc函数,用户也可以编写自定义的ufunc函数,方式是:

1. 编写对单个数值计算的目的函数;

2. 利用np.frompyfunc(func, nin, nout)将其转换为ufunc函数,其中func是上面编写的目的函数,nin是输入的参数个数,nout是返回值的个数。

## 基本形式
u_func = np.frompyfunc(func,nin,nout)
ret = u_func(narray_obj,param1,param2..)

这里返回的ret是object类型,所以实际上需要用astype()转换为目的类型。numpy.vectorize()也实现了和numpy.frompyfunc()一样的功能,区别是前者可以t通过otypes指定返回值的类型,不用再用astype()进行转换。

## 基本形式
u_func = np.frompyfunc(func,otypes=[dtype1,dtype2..]
ret = u_func(narray_object,param1,param2..)

广播

先看个例子:

a = np.arange(0,60,10).reshape(-1,1)
b = np.arange(0,5)
#a
array([[ 0],
  [10],
  [20],
  [30],
  [40],
  [50]])
#b
array([0, 1, 2, 3, 4])

ok,现在计算a+b,不过现在有一个问题,a和b的维度不一样,那应该怎么加?先看看结果吧

# a+b
array([[ 0, 1, 2, 3, 4],
  [10, 11, 12, 13, 14],
  [20, 21, 22, 23, 24],
  [30, 31, 32, 33, 34],
  [40, 41, 42, 43, 44],
  [50, 51, 52, 53, 54]])

结果来看,是用a的每一行的元素(这里a为列向量,每一行只有一个元素)与b的每一个元素相加,相当于:

a=array([[ 0, 0, 0, 0, 0],
  [10, 10, 10, 10, 10],
  [20, 20, 20, 20, 20],
  [30, 30, 30, 30, 30],
  [40, 40, 40, 40, 40],
  [50, 50, 50, 50, 50]])

而b是一个行向量,现在我们将这一个行向量重复6次,和a的第0轴长度相同,构成一个二维数组,相当于:

b=array([[0, 1, 2, 3, 4],
  [0, 1, 2, 3, 4],
  [0, 1, 2, 3, 4],
  [0, 1, 2, 3, 4],
  [0, 1, 2, 3, 4],
  [0, 1, 2, 3, 4]])

现在,再进行相加,自然就是对用元素相加了,也就是上面的结果,这就是numpy中的广播,对进行运算的两个narray对象shape不一样时,进行的维度补齐。总的来说,numpy的广播规则基于下面4个规则:

让所有输入数姐都向其中维数最多的数组看齐,shape属性中不足的部分都通过在前面加1补齐; 如上面的输入中,a.shape=(6,1),b.shape=(,5),a的维数是2,b的维数是1,所以b向a看齐,并且用1补齐,那么b.shape=(1,5)。

输出数组的shape属性是输入数组的shape属性的各个轴上的最大值 输出是各轴上最大值,所以a+b的输出的shape应该是(6,5);

如果输入数组的某个轴的长度 为 1或与输出数组的对应轴的长度相同,这个数组能够用来计算,否则出错

当输入数组的某个轴的长度为1?迹?刈糯酥嵩怂闶倍加么酥嵘系牡谝蛔橹?/p>

由于广播在numpy计算中比较常见,所以numpy提供了ogrid和mgrid来创建广播计算的数组,前者返回的是两个向量,后者返回的是进行广播运算的数组。

x,y = np.ogrid[:5,:5]
# x
array([[0],
  [1],
  [2],
  [3],
  [4]])
# y
array([[0, 1, 2, 3, 4]])
x,y=np.mgrid[:5,:5]
# x
[[0, 0, 0, 0, 0],
[1, 1, 1, 1, 1],
[2, 2, 2, 2, 2],
[3, 3, 3, 3, 3],
[4, 4, 4, 4, 4]]
#y
[[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4]]]

ogrid[]参数是有两种形式(1):[start:end:step]即起点、终点和步长;(2):[start:end:len]即起点、终点和数组长度,这里的长度为了和步长区分,在传入时时以虚数方式传入的,例如[1,5,4j]将产生[1,2,3,4]这样的数组。

另外广播支持特殊的下标None,意义时在对应位置上产生1的新轴,例如a[None,:]等效于a.reshape((1,-1)),a[:,None]等效于a.reshape((-1,1)),另外,np.ix_()能将一维数组转换为能进行广播的二维数组:

x=np.array([0,1,4,10])
y=np.array([2,3,8])
gy,gx=np.ix_(y,x)
#print gy
[[2]
[3]
[8]]
#print gx
[[ 0 1 4 10]]
# gx+gy
array([[ 2, 3, 6, 12],
  [ 3, 4, 7, 13],
  [ 8, 9, 12, 18]])

np.ix_()支持多个参数,从而支持n维空间的广播计算。

ufunc方法

ufunc函数对象本身还有一些方法函数,这些方法只对两个输入、一个输出的ufunc 函数有效,其他的ufunc对象调用这些方法时会抛出ValueError异常。

(1). reduce(),沿着指定轴对数组进行操作,相当于将相应的操作放到该轴元素之间。

np.add.reduce([1,2,3]) #1+2+3=6
np.add.reduce([[1,2,3],[4,5,6]]) #[1+4,2+5,3+6]=[5,7,9]
np.add.reduce([[1,2,3],[4,5,6]],axis=1) #[1+2+3,4+5+6]=[6,15]

(2). accumulate()和reduce()类似,区别时是前者会保留中间结果:

np.add.accumulate([1,2,3]) #[1,1+2,1+2+3]=[1,3,6]
np.add.accumulate([[1,2,3],[4,5,6]],axis=1) 
#
array([[ 1, 3, 6],
  [ 4, 9, 15]])

(3). reduceat()方法计算多纽reduce()的结果,通 过 indices参数指定一系列的起始和终止位置。它的计算有些特别,,计算的方法如下:

if indices[i] < indices[i+1]:
 result[i] = <op>.reduce(a[indices[i]:indices[i+1]])
else:
 result[i] = a[indices[i]]
#result[-1]的计算如下:
<op>.reduce(a[indices[-1]:])

例:

a = np.array([1,2,3,4])
result = np.add.reduceat(a, indices=[0,1,0,2,0,3,0])
## result
array([1,2,3,3,6,4,10])

## 计算过程如下:
 : a[0] -> 1
 : a[1] -> 2
 : a[0] + a[1] -> 1 + 2
 : a[2] -> 3
 : a[0] + a[1] + a[2] -> 1 + 2 + 3 = 6
 : a[3] -> 4
 :a[0] + a[1] + a[2] + a[4] - > 1 + 2 + 3 + 4 = 1 0

再看多维数组

在前一篇文章中我们提到过多维数组,现在我们回头再看看多维数组的下标取数据。首先,多维数组的下标应该是一个长度和数组的维数相同的元组。如栗下标元组的长度比数组的维数大,就会出错;如果小,就 会 在 下 标 元 组 的 后 而 补 ,使得它的长度与数组维数相同。如果下标对象不是元组,则 numpy会首先把它转换为元组。这种转换可能会和用户所希望的不一致,因此为了避免出现问题,请 “显式”地使用元组作为下标。

a = np.arange(3*4*5).reshape(3,4,5)
array([[[ 0, 1, 2, 3, 4],
  [ 5, 6, 7, 8, 9],
  [10, 11, 12, 13, 14],
  [15, 16, 17, 18, 19]],

  [[20, 21, 22, 23, 24],
  [25, 26, 27, 28, 29],
  [30, 31, 32, 33, 34],
  [35, 36, 37, 38, 39]],

  [[40, 41, 42, 43, 44],
  [45, 46, 47, 48, 49],
  [50, 51, 52, 53, 54],
  [55, 56, 57, 58, 59]]])

lidx=[[0],[1]]
#a[lidx]

aidx = np.array(lidx)
#a[aidx]
array([[[[ 0, 1, 2, 3, 4],
   [ 5, 6, 7, 8, 9],
   [10, 11, 12, 13, 14],
   [15, 16, 17, 18, 19]]],


  [[[20, 21, 22, 23, 24],
   [25, 26, 27, 28, 29],
   [30, 31, 32, 33, 34],
   [35, 36, 37, 38, 39]]]])

可以看到,numpy把列表[[0],[1]]转换成元组([0],[1]),而把数组对象转换成(aidx,:,:)。

整数数组和切片做为下标

如果利用数组作为下标,但数组的维度都不一样,那么这个时候这些数组将会应用广播规则把这些数组转换成一样,转换的规则是上面说到的先用1补足然后取各个维度的最大值。

i0 = np.array([[1,2,1],[0,1,0]])
i1 = np.array([[[0]],[[1]]])
i2 = np.array([[[2,3,2]]])
#print i0.shape
(2, 3)
#print i1.shape
(2, 1, 1)
#print i2.shape
(1, 1, 3)
# i0补齐之后为 (1,2,3),然后取最大值为
(2,2,3)

这里将所有的数组进行广播,把所有的数组转换成shape=(2,2,3),利用numpy.broadcast_arrays()可以查看广播后的数组:

id0,id1,id2=np.broadcast_arrays(i0,i1,i2)
#id0
[[[1 2 1]
 [0 1 0]]

 [[1 2 1]
 [0 1 0]]
#id1
[[[0 0 0]
 [0 0 0]]

 [[1 1 1]
 [1 1 1]]]
#id2
[[[2 3 2]
 [2 3 2]]

 [[2 3 2]
 [2 3 2]]]

然后利用下标取元素时,例如i,j,k=1,1,1时,取得的元素应该是a[id0[i,j,k],id1[i,j,k],id2[i,j,k]]=a[1,1,3]=28。下标中含有切片时,首先来看第一种,就是整数数组时连续的,也就是数组之间没有切片,例如a[1:3,i0,i1],这种情况下,首先会把整数数组(i0,i1)广播,然后将切片的长度放到相应的位置构成一个维度,i0,i1广播后的shape=(2,2,3),切片的长度为2,所以最后的shape=(2,2,2,3)。最后的结果是a[1:3,id0[i,j,k],id1[i,j,k]]。再者,如果切片是再整数数组之间,那么同样会将数组广播,然后把切片位置替换为数组的维度或者切片的长度添加到索引的最后面,如a[i0,:,i1],i0,i1广播后的shape=(2,2,3),数组a的第二个轴的长度为4,所以最后的shape=(2,2,3,4)。

bool数组做为下标

用bool数组作为下标时,会将bool数组中为True的索引组成一个整数数组,然后以此数组作为下标。相当于用numpy.nonzero(bool_array)的结果,例如:

b2 = np.array([[True,False,True],[True,False,False]])
np.nonzero(b2)
##
(array([0, 0, 1]), array([0, 2, 0]))
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
## 相当于取[0,0],[0,2],[1,0]

除此之外,如果bool数组中有切片,那么相当于将bool值转换成nonzero()后的整数数组,切片位置不变。

以上这篇python科学计算之numpy——ufunc函数用法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python多进程编程技术实例分析
Sep 16 Python
在Python的gevent框架下执行异步的Solr查询的教程
Apr 16 Python
Python制作数据导入导出工具
Jul 31 Python
Django中Model的使用方法教程
Mar 07 Python
python实现数据写入excel表格
Mar 25 Python
PyQt实现界面翻转切换效果
Apr 20 Python
解决Python pandas df 写入excel 出现的问题
Jul 04 Python
python之验证码生成(gvcode与captcha)
Jan 02 Python
python调用c++ ctype list传数组或者返回数组的方法
Feb 13 Python
Python如何处理大数据?3个技巧效率提升攻略(推荐)
Apr 15 Python
对pytorch中的梯度更新方法详解
Aug 20 Python
python如何输出反斜杠
Jun 18 Python
OpenCV里的imshow()和Matplotlib.pyplot的imshow()的实现
Nov 25 #Python
Python解析json代码实例解析
Nov 25 #Python
python实现差分隐私Laplace机制详解
Nov 25 #Python
python3实现弹弹球小游戏
Nov 25 #Python
python数据化运营的重要意义
Nov 25 #Python
python实现拉普拉斯特征图降维示例
Nov 25 #Python
python模块hashlib(加密服务)知识点讲解
Nov 25 #Python
You might like
PHP编程函数安全篇
2013/01/08 PHP
PHP文件去掉PHP注释空格的函数分析(PHP代码压缩)
2013/07/02 PHP
PHP通过API获取手机号码归属地
2015/05/28 PHP
PHP面向对象程序设计实例分析
2016/01/26 PHP
Laravel 中使用 Vue.js 实现基于 Ajax 的表单提交错误验证操作
2017/06/30 PHP
jquery实现的超出屏幕时把固定层变为定位层的代码
2010/02/23 Javascript
将中国标准时间转换成标准格式的代码
2014/03/20 Javascript
angularJS 中input示例分享
2015/02/09 Javascript
详解Bootstrap按钮
2016/01/04 Javascript
javascript简单判断输入内容是否合法的方法
2016/05/11 Javascript
进阶之初探nodeJS
2017/01/24 NodeJs
JavaScript无缝滚动效果的实例代码
2017/03/27 Javascript
基于javascript的异步编程实例详解
2017/04/10 Javascript
利用nvm管理多个版本的node.js与npm详解
2017/11/02 Javascript
通过fastclick源码分析彻底解决tap“点透”
2017/12/24 Javascript
老生常谈JS中的继承及实现代码
2018/07/06 Javascript
ES6中Set和Map数据结构,Map与其它数据结构互相转换操作实例详解
2019/02/28 Javascript
Vue使用axios出现options请求方法
2019/05/30 Javascript
教你完全理解ReentrantLock重入锁
2019/06/03 Javascript
vue+django实现一对一聊天功能的实例代码
2019/07/17 Javascript
VUE实现移动端列表筛选功能
2019/08/23 Javascript
NodeJs crypto加密制作token的实现代码
2019/11/15 NodeJs
ES6如何用一句代码实现函数的柯里化
2020/01/18 Javascript
vue实现井字棋游戏
2020/09/29 Javascript
[46:14]完美世界DOTA2联赛PWL S3 Magma vs INK ICE 第一场 12.11
2020/12/16 DOTA
Python编程之黑板上排列组合,你舍得解开吗
2017/10/30 Python
Python实现二维曲线拟合的方法
2018/12/29 Python
Python里字典的基本用法(包括嵌套字典)
2019/02/27 Python
Python如何爬取实时变化的WebSocket数据的方法
2019/03/09 Python
python 将字符串完成特定的向右移动方法
2019/06/11 Python
浅谈keras中的batch_dot,dot方法和TensorFlow的matmul
2020/06/18 Python
小学三八妇女节活动方案
2014/03/16 职场文书
市场调查策划方案
2014/06/10 职场文书
简易离婚协议书范本2014
2014/10/15 职场文书
敲诈同学钱财检讨书范文
2014/11/18 职场文书
php解析非标准json、非规范json的方式实例
2022/05/10 PHP