是否可以 numpy.vectorize 实例方法?
Posted
技术标签:
【中文标题】是否可以 numpy.vectorize 实例方法?【英文标题】:Is it possible to numpy.vectorize an instance method? 【发布时间】:2018-08-05 11:54:23 【问题描述】:我发现numpy.vectorize
允许将期望单个数字作为输入的“普通”函数转换为函数,该函数还可以将输入列表转换为函数已映射到每个函数的列表输入。例如,以下测试通过:
import numpy as np
import pytest
@np.vectorize
def f(x):
if x == 0:
return 1
else:
return 2
def test_1():
assert list(f([0, 1, 2])) == [1, 2, 2]
def test_2():
assert f(0) == 1
if __name__ == "__main__":
pytest.main([__file__])
但是,对于使用实例属性的实例方法,我无法让它工作。例如:
class Dummy(object):
def __init__(self, val=1):
self.val = val
@np.vectorize
def f(self, x):
if x == 0:
return self.val
else:
return 2
def test_3():
assert list(Dummy().f([0, 1, 2])) == [1, 2, 2]
此测试失败:
=================================== FAILURES ===================================
____________________________________ test_3 ____________________________________
def test_3():
> assert list(Dummy().f([0, 1, 2])) == [1, 2, 2]
test_numpy_vectorize.py:31:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/numpy/lib/function_base.py:2739: in __call__
return self._vectorize_call(func=func, args=vargs)
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/numpy/lib/function_base.py:2809: in _vectorize_call
ufunc, otypes = self._get_ufunc_and_otypes(func=func, args=args)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <numpy.lib.function_base.vectorize object at 0x106546470>
func = <function Dummy.f at 0x10653a2f0>, args = [array([0, 1, 2])]
def _get_ufunc_and_otypes(self, func, args):
"""Return (ufunc, otypes)."""
# frompyfunc will fail if args is empty
if not args:
raise ValueError('args can not be empty')
if self.otypes is not None:
otypes = self.otypes
nout = len(otypes)
# Note logic here: We only *use* self._ufunc if func is self.pyfunc
# even though we set self._ufunc regardless.
if func is self.pyfunc and self._ufunc is not None:
ufunc = self._ufunc
else:
ufunc = self._ufunc = frompyfunc(func, len(args), nout)
else:
# Get number of outputs and output types by calling the function on
# the first entries of args. We also cache the result to prevent
# the subsequent call when the ufunc is evaluated.
# Assumes that ufunc first evaluates the 0th elements in the input
# arrays (the input values are not checked to ensure this)
args = [asarray(arg) for arg in args]
if builtins.any(arg.size == 0 for arg in args):
raise ValueError('cannot call `vectorize` on size 0 inputs '
'unless `otypes` is set')
inputs = [arg.flat[0] for arg in args]
> outputs = func(*inputs)
E TypeError: f() missing 1 required positional argument: 'x'
是否可以将numpy.vectorize
应用于实例方法?
【问题讨论】:
装饰器版本@vectorize
比函数版本更难应用。您的方法f
有两个参数,self
和x
。 vectorize
不够聪明,无法将 Dummy()
分配给 self
,并在 [0, 1, 2]
上进行迭代。
类似问题:numpy-discussion.10968.n7.nabble.com/…
如果您打算对np.vectorize
做任何严肃的事情,我建议您研究它的代码。和np.frompyfunc
一起玩。
【参考方案1】:
不修改类的简单解决方案
您可以直接在实例上的方法上使用np.vectorize
:
class Dummy(object):
def __init__(self, val=1):
self.val = val
def f(self, x):
if x == 0:
return self.val
else:
return 2
vec_f = np.vectorize(Dummy().f)
def test_3():
assert list(vec_f([0, 1, 2])) == [1, 2, 2]
test_3()
您还可以在__init__
中创建矢量化函数vec_f
:
向实例添加矢量化版本
class Dummy(object):
def __init__(self, val=1):
self.val = val
self.vec_f = np.vectorize(self.f)
def f(self, x):
if x == 0:
return self.val
else:
return 2
def test_3():
assert list(Dummy().vec_f([0, 1, 2])) == [1, 2, 2]
或使用不同的命名方案:
class Dummy(object):
def __init__(self, val=1):
self.val = val
self.f = np.vectorize(self.scalar_f)
def scalar_f(self, x):
if x == 0:
return self.val
else:
return 2
def test_3():
assert list(Dummy().f([0, 1, 2])) == [1, 2, 2]
test_3()
test_3()
【讨论】:
是的。这也可以,但每次调用f
时都会调用np.vectorize
。在我的例子中,向量化只在实例化时完成一次。【参考方案2】:
如果您想使用方法的矢量化实现,可以使用excluded
参数,如下所示:
class MyClass:
def __init__(self, data):
self.data = data
self.my_vectorized_func = np.vectorize(self.my_func, excluded='self')
def my_func(self, x):
return pow(x, self.data)
有了这个,你可以像非矢量化的那样使用你的方法:
In[1]: myclass = MyClass(3) # '3' will be the power factor of our function
In[2]: myclass.my_vectorized_func([1, 2, 3, 4, 5])
Out[3]: array([ 1, 8, 27, 64, 125])
【讨论】:
【参考方案3】:想起我在memoized
装饰器中看到的一种技术,我设法通过子类化numpy.vectorize
使装饰器也适用于实例方法,如下所示:
import numpy as np
import functools
class vectorize(np.vectorize):
def __get__(self, obj, objtype):
return functools.partial(self.__call__, obj)
现在,如果我用vectorize
而不是np.vectorize
来装饰Dummy
类的f
方法,则测试通过:
class Dummy(object):
def __init__(self, val=1):
self.val = val
@vectorize
def f(self, x):
if x == 0:
return self.val
else:
return 2
def test_3():
assert list(Dummy().f([0, 1, 2])) == [1, 2, 2]
if __name__ == "__main__":
pytest.main([__file__])
有输出
test_numpy_vectorize.py .
=========================== 1 passed in 0.01 seconds ===========================
[Finished in 0.7s]
【讨论】:
np.vectorize
用作装饰器只是因为它将函数作为参数并返回一个函数。但是这种vectorize
的简单使用是有限的。人们经常遇到vectorize
的问题,因为他们没有指定otypes
甚至signature
(在最近的版本中)。【参考方案4】:
这是一个适用于实例方法和函数的通用装饰器(请参阅 Numpy's documentation 了解 otypes
和 signature
):
from functools import wraps
import numpy as np
def vectorize(otypes=None, signature=None):
"""Numpy vectorization wrapper that works with instance methods."""
def decorator(fn):
vectorized = np.vectorize(fn, otypes=otypes, signature=signature)
@wraps(fn)
def wrapper(*args):
return vectorized(*args)
return wrapper
return decorator
您可以使用它来向量化您的方法,如下所示:
class Dummy(object):
def __init__(self, val=1):
self.val = val
@vectorize(signature="(),()->()")
def f(self, x):
if x == 0:
return self.val
else:
return 2
def test_3():
assert list(Dummy().f([0, 1, 2])) == [1, 2, 2]
关键是使用signature
kwarg。 ->
左边的括号值指定输入参数,右边的值指定输出值。 ()
代表一个标量(0维向量); (n)
代表一维向量; (m,n)
代表一个二维向量; (m,n,p)
代表一个3维向量;在这里,signature="(),()->()"
向 Numpy 指定第一个参数 (self
) 是一个标量,第二个 (x
) 也是一个标量,并且该方法返回一个标量(self.val
或 2
,取决于x
)。
$ pytest /tmp/instance_vectorize.py
======================= test session starts ========================
platform linux -- Python 3.6.5, pytest-3.5.1, py-1.5.3, pluggy-0.6.0
rootdir: /tmp, inifile:
collected 1 item
../../tmp/instance_vectorize.py . [100%]
==================== 1 passed in 0.08 seconds ======================
【讨论】:
【参考方案5】:来自docs:
vectorized 输出的数据类型是通过调用输入的第一个元素的函数来确定的。这可以通过指定 otypes 参数来避免。
函数f(self, x)
中的第一个输入是self
。也许您可以将该函数作为staticmethod
函数的包装器?
【讨论】:
以上是关于是否可以 numpy.vectorize 实例方法?的主要内容,如果未能解决你的问题,请参考以下文章
Jax 矢量化:vmap 和/或 numpy.vectorize?
python Python:numpy.vectorize(a,b)