将调用numpy数组的方法委托给其元素

时间:2016-04-12 15:32:50

标签: python numpy

假设我有以下numpy数组:

arr = np.array(["a", "b", "c"], dtype=object)

现在我想对每个元素执行任意函数或调用任意方法。例如,假设我想获得所有这些元素的大写。 “逻辑上”说,我想做这样的事情:

upp_arr = arr.upper()

upp_arr = str.upper(arr)

简短版本:我可以完成以上任何一种吗?

长版: 这显然不会起作用,考虑到数组没有upper方法(即使各个元素都有),str内置也不知道如何处理numpy数组。我不想构建一个知道如何处理数组的str.upper自定义版本,因为这需要我事先知道我的数组用户可能想要使用哪些函数。

我可以做类似

的事情
upp_arr = np.array([x.upper() for x in arr])

我不喜欢这样:现在我需要向我的代码的用户公开arr实际上是一个numpy数组。我希望得到类似上述两种非工作解决方案的东西。

我试图通过对np.ndarray进行子类化来覆盖__getattr__(self, name)方法以返回名称name的元素属性数组,如果name不是np.ndarray的属性,而是覆盖其__call__方法以返回元素数组 调用`结果:

import numpy as np

class MyArr(np.ndarray):
    def __getattr__(self, name):
        if hasattr(np.ndarray, name):
            return getattr(np.ndarray, name)
        arr = MyArr(self.shape, dtype=object)
        arr[:] = [getattr(elem, name) for elem in self]
        return arr
    def __call__(self):
        arr = MyArr(self.shape, dtype=self.dtype)
        arr[:] = [elem() for elem in self]
        return arr

arr = MyArr((3,), dtype=object)
arr[:] = ["a", "b", "c"]
arr.upper()

这有两个问题:构造数组是一个痛苦(必须首先实例化它,然后用值填充它),我必须假设函数调用的结果与dtype相同原始数组(dtype=self.dtype部分)。如果我坚持使用object类型的数组,那么后者不是问题,但通常会这样。

如何解决?

1 个答案:

答案 0 :(得分:-1)

有一个函数库可以将str操作应用于字符串数组的元素,例如

In [199]: np.char.upper(np.array(['a','b','c']))
Out[199]: 
array(['A', 'B', 'C'], 
      dtype='|S1')

但它不适用于dtype对象的数组:

In [200]: np.char.upper(np.array(['a','b','c'],dtype=object))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-200-17a9e3cc581f> in <module>()
----> 1 np.char.upper(np.array(['a','b','c'],dtype=object))
...\numpy\core\defchararray.pyc in upper(a)
   1571     """
   1572     a_arr = numpy.asarray(a)
-> 1573     return _vec_string(a_arr, a_arr.dtype, 'upper')
TypeError: string operation on non-string array 

但是这个char函数并不比相应的列表理解快。它仍然必须在每个元素上调用str操作。但是如果数组是n-d将会更方便。

In [209]: timeit np.char.upper(x1)   # large x1
1000 loops, best of 3: 277 µs per loop

In [210]: timeit np.array([i.upper() for i in x1])
1000 loops, best of 3: 278 µs per loop

关于这个问题的一些新想法。

np.frompyfunc将一个函数应用于数组的所有元素,返回一个新对象dtype数组。

In [49]: vupper = np.frompyfunc(lambda astr: astr.upper(), 1, 1)
In [50]: vupper(np.array(['a','b','c']))
Out[50]: array(['A', 'B', 'C'], dtype=object)
In [51]: vupper(np.array(['a','b','c'], dtype=object))
Out[51]: array(['A', 'B', 'C'], dtype=object)

np.vectorize使用np.frompyfunc,但添加了一些dtype次转化。在我的测试np.frompyfunc中有点快;在某些情况下,比同等的列表理解版本快2倍。

查看numpy/core/records.py,了解np.recarray如何让我们将结构化数组字段作为属性进行访问。它包裹__getattribute__

Masked数组是具有大量自定义方法的数组子类的一个很好的例子。 /numpy/ma/__init__.py