暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Numpy函数ufunc的高级应用

漫谈大数据与数据分析 2020-04-26
955


通用函数在提供的快速的元素级运算的同时,还有一些高级用法能使我们丢开循环而编写出更为简洁的代码。



ufunc实例方法

NumPy中有一些用于执行特定矢量化运算的二元ufunc:


reduce接受一个数组参数,并通过一系列的二元运算对其值进行聚合(可指明轴向)。

用np.add.reduce可对数组中各个元素进行聚合求和:

In [115]: arr = np.arange(10)
In [116]: np.add.reduce(arr)
Out[116]: 45


In [117]: arr.sum()
Out[117]: 45

起始值取决于ufunc的实现,对于add函数是0。设置轴号使我们可以更简洁的解决一些问题,比如检查数组各行中的值是否是有序,可以用np.logical_and实现。

首先准备测试数据:

In [118]: np.random.seed(12346)
In [119]: arr = np.random.randn(5, 5)


In [120]: arr[::2].sort(1) # 排序一些行

对于每一行,将每一个元素与后面相邻的元素进行比较:

In [121]: arr[:, :-1] < arr[:, 1:]
Out[121]:
array([[ True, True, True, True],
[False, True, False, False],
[ True, True, True, True],
[ True, False, True, True],
[ True, True, True, True]], dtype=bool)

使用logical_and.reduce函数将结果合并起来:

In [122]: np.logical_and.reduce(arr[:, :-1] < arr[:, 1:], axis=1)
Out[122]: array([ True, False, True, False, True], dtype=bool)

其实logical_and.reduce跟all方法效果是等价的:

(arr[:, :-1] < arr[:, 1:]).all(axis=1)


ccumulate跟reduce的关系就像cumsum跟sum的关系那样。

accumulate可用于产生一个跟原数组大小相同的中间“累计”值数组:

In [123]: arr = np.arange(15).reshape((3, 5))
In [124]: np.add.accumulate(arr, axis=1)
Out[124]:
array([[ 0, 1, 3, 6, 10],
[ 5, 11, 18, 26, 35],
[10, 21, 33, 46, 60]])

outer用于计算两个数组的叉积:

In [125]: arr = np.arange(3).repeat([1, 2, 2])
In [126]: arr
Out[126]: array([0, 1, 1, 2, 2])


In [127]: np.multiply.outer(arr, np.arange(5))
Out[127]:
array([[0, 0, 0, 0, 0],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 2, 4, 6, 8],
[0, 2, 4, 6, 8]])

outer输出结果的维度是两个输入数据的维度的高维合并:

In [128]: x, y = np.random.randn(3, 4), np.random.randn(5)
In [129]: result = np.subtract.outer(x, y)


In [130]: result.shape
Out[130]: (3, 4, 5)



reduceat用于计算局部聚合,可以看成是一个对数据各切片进行聚合的groupby运算。它接受一个列表作为对值进行拆分的边界:

In [131]: arr = np.arange(10)
In [132]: np.add.reduceat(arr, [0, 5, 8])
Out[132]: array([10, 18, 17])

最终结果是在arr[0:5]、arr[5:8]以及arr[8:]上执行的聚合。


reduceat也可以传入一个axis参数:

In [133]: arr = np.multiply.outer(np.arange(4), np.arange(5))
In [134]: arr
Out[134]:
array([[ 0, 0, 0, 0, 0],
[ 0, 1, 2, 3, 4],
[ 0, 2, 4, 6, 8],
[ 0, 3, 6, 9, 12]])


In [135]: np.add.reduceat(arr, [0, 2, 4], axis=1)
Out[135]:
array([[ 0, 0, 0],
[ 1, 5, 4],
[ 2, 10, 8],
[ 3, 15, 12]])





编写自己的NumPy ufuncs

比较常见的是使用NumPy C API,但本文仅介绍纯粹的Python ufunc。

使用numpy.frompyfunc即可,它需要传入一个函数,以及该函数输入输出参数的数量。

下面创建一个实现元素级加法的简单函数,函数传入两个参数返回一个值,所以frompyfunc后面传入(2, 1):

In [136]: def add_elements(x, y):
.....: return x + y


In [137]: add_them = np.frompyfunc(add_elements, 2, 1)


In [138]: add_them(np.arange(8), np.arange(8))
Out[138]: array([0, 2, 4, 6, 8, 10, 12, 14], dtype=object)

但是用frompyfunc创建的函数总是返回Python对象数组,不方便。

numpy.vectorize功能虽少,但可以指定输出类型:

In [139]: add_them = np.vectorize(add_elements, otypes=[np.float64])
In [140]: add_them(np.arange(8), np.arange(8))
Out[140]: array([ 0., 2., 4., 6., 8., 10., 12., 14.])

frompyfunc和vectorize这两个函数提供了一种创建ufunc型函数的手段,但它们在计算每个元素时都要执行一次Python函数调用,性能较差低会比NumPy自带的基于C的ufunc慢很多,所以一般都使用NumPy C API编写高性能的NumPy ufuncs。


文章转载自漫谈大数据与数据分析,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论