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多线程编程(八):使用Event实现线程间通信
Apr 05 Python
python语言使用技巧分享
May 31 Python
Python生成随机密码的方法
Jun 16 Python
Python 实现一行输入多个值的方法
Apr 21 Python
详谈python在windows中的文件路径问题
Apr 28 Python
Django项目中用JS实现加载子页面并传值的方法
May 28 Python
很酷的python表白工具 你喜欢我吗
Apr 11 Python
python多进程读图提取特征存npy
May 21 Python
python程序 创建多线程过程详解
Sep 23 Python
使用Python FastAPI构建Web服务的实现
Jun 08 Python
django haystack实现全文检索的示例代码
Jun 24 Python
matplotlib之多边形选区(PolygonSelector)的使用
Feb 24 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模板,主要想体现一下思路
2006/12/25 PHP
phpwind中的数据库操作类
2007/01/02 PHP
探讨PHP中OO之静态关键字以及类常量的详解
2013/06/07 PHP
PHP static局部静态变量和全局静态变量总结
2014/03/02 PHP
Codeigniter实现智能裁剪图片的方法
2014/06/12 PHP
YII中Ueditor富文本编辑器文件和图片上传的配置图文教程
2017/03/15 PHP
jquery pagination插件实现无刷新分页代码
2009/10/13 Javascript
js替换字符串的所有示例代码
2013/07/23 Javascript
JavaScript中switch判断容易犯错的一个细节
2014/08/27 Javascript
JavaScript原生对象之Date对象的属性和方法详解
2015/03/13 Javascript
使用jQuery实现图片遮罩半透明坠落遮挡
2015/03/16 Javascript
javascript实现汉字转拼音代码分享
2015/04/20 Javascript
JS实现环形进度条(从0到100%)效果
2016/07/05 Javascript
canvas实现手机端用来上传用户头像的代码
2016/10/20 Javascript
Bootstrap popover用法详解
2016/12/22 Javascript
Angular-Ui-Router+ocLazyLoad动态加载脚本示例
2017/03/02 Javascript
bootstrap常用组件之头部导航实现代码
2017/04/20 Javascript
利用prop-types第三方库对组件的props中的变量进行类型检测
2017/05/02 Javascript
AngularJS入门教程一:路由用法初探
2017/05/27 Javascript
Vue生命周期activated之返回上一页不重新请求数据操作
2020/07/26 Javascript
python自动化测试之从命令行运行测试用例with verbosity
2014/09/28 Python
Python import用法以及与from...import的区别
2015/05/28 Python
解决Django删除migrations文件夹中的文件后出现的异常问题
2019/08/31 Python
pytorch之inception_v3的实现案例
2020/01/06 Python
python+opencv实现移动侦测(帧差法)
2020/03/20 Python
python requests库的使用
2021/01/06 Python
HTML5 File API改善网页上传功能
2009/08/19 HTML / CSS
Html5饼图绘制实现统计图的方法
2020/08/05 HTML / CSS
高中的自我鉴定
2013/12/16 职场文书
欢送退休感言
2014/02/08 职场文书
廉政教育的心得体会
2014/09/01 职场文书
合作意向协议书
2015/01/29 职场文书
浅谈如何提高PHP代码质量之单元测试
2021/05/28 PHP
详细聊一聊mysql的树形结构存储以及查询
2022/04/05 MySQL
vue中的可拖拽宽度div的实现示例
2022/04/08 Vue.js
Redis实现短信验证码登录的示例代码
2022/06/14 Redis