简体   繁体   English

是否可以numpy.vectorize实例方法?

[英]Is it possible to numpy.vectorize an instance method?

I've found that the numpy.vectorize allows one to convert 'ordinary' functions which expect a single number as input to a function which can also convert a list of inputs into a list in which the function has been mapped to each input. 我发现numpy.vectorize允许转换'普通'函数,这些函数期望单个数字作为函数的输入,该函数也可以将输入列表转换为函数已映射到每个输入的列表。 For example, the following tests pass: 例如,以下测试通过:

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__])

However, I've not been able to get this to work for an instance method which makes use of an instance attribute. 但是,我无法让这个用于使用实例属性的实例方法。 For example: 例如:

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]

This test fails: 此测试失败:

=================================== 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'

Is it possible to apply numpy.vectorize to an instance method? 是否可以将numpy.vectorize应用于实例方法?

Simple solution without modifying the class 简单的解决方案,无需修改类

You can use np.vectorize directly on the method on the instance: 您可以直接在实例上的方法上使用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()

You can also create a vectorized function vec_f in your __init__ : 您还可以在__init__创建矢量化函数vec_f

Adding a vectorized version to the instance 向实例添加矢量化版本

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]

or with a different naming scheme: 或者使用不同的命名方案:

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()

Remembering a technique I saw in the memoized decorator, I managed to get the decorator to also work for instance methods by subclassing numpy.vectorize as follows: 记住我在memoized装饰器中看到的一种技术,我设法让装饰器也通过子类化numpy.vectorize来为实例方法工作,如下所示:

import numpy as np
import functools


class vectorize(np.vectorize):
    def __get__(self, obj, objtype):
        return functools.partial(self.__call__, obj)

Now if I decorate the Dummy class' f method with vectorize instead of np.vectorize , the test passes: 现在,如果我使用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__])

with output 与输出

test_numpy_vectorize.py .

=========================== 1 passed in 0.01 seconds ===========================
[Finished in 0.7s]

Here's a generic decorator that works with instance methods as well as functions (refer to Numpy's documentation for otypes and signature ): 这是一个使用实例方法和函数的通用装饰器(请参阅Numpy的 otypessignature 文档 ):

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

You may use it to vectorize your method as follows: 您可以使用它来按如下方式对方法进行矢量化:

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]

The key is to make use of the signature kwarg. 关键是要使用signature kwarg。 Parenthesized values to the left of -> specify input parameters and values to the right specify output values. 左侧的带括号的值->指定输入参数,右侧的值指定输出值。 () represents a scalar (0-dimensional vector); ()表示标量(0维向量); (n) represents a 1-dimensional vector; (n)代表一维向量; (m,n) represents a 2-dimensional vector; (m,n)表示二维矢量; (m,n,p) represents a 3-dimensional vector; (m,n,p)表示三维矢量; etc. Here, signature="(),()->()" specifies to Numpy that the first parameter ( self ) is a scalar, the second ( x ) is also a scalar, and the method returns a scalar (either self.val or 2 , depending on x ). 这里, signature="(),()->()"指定Numpy第一个参数( self )是标量,第二个( x )也是标量,并且该方法返回一个标量( self.val2 ,取决于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 ======================

If you want to use vectorized implementation of your method you can use excluded parameter like following: 如果要使用方法的矢量化实现,可以使用如下的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)

With this, you can use your method like the non-vectorized one: 有了这个,你可以使用你的方法,如非矢量化的方法:

 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])

From the docs : 来自文档

The data type of the output of vectorized is determined by calling the function with the first element of the input. 向量化输出的数据类型是通过使用输入的第一个元素调用函数来确定的。 This can be avoided by specifying the otypes argument. 通过指定otypes参数可以避免这种情况。

The first input in your function f(self, x) is self . 函数f(self, x)的第一个输入是self Maybe you can make that function a wrapper around a staticmethod function? 也许你可以使该函数成为staticmethod函数的包装器?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM