【问题标题】:Python lazy evaluation numpy ndarrayPython惰性求值numpy ndarray
【发布时间】:2023-03-29 18:19:01
【问题描述】:

我有一个大型 2D 数组,我想声明一次,并且偶尔只根据参数更改 一些 值,而不遍历整个数组。

为了构建这个数组,我使用dtype=object 对 numpy ndarray 类进行了子类化,并分配给我想要更改函数的元素,例如:

def f(parameter):
     return parameter**2

for i in range(np.shape(A)[0]):
    A[i,i]=f
    for j in range(np.shape(A)[0]):
        A[i,j]=1.

然后我重写了__getitem__ 方法,以便它返回具有给定参数的函数的求值,如果它是可调用的,否则返回值本身。

    def __getitem__(self, key):
        value = super(numpy.ndarray, self).__getitem__(key)
        if callable(value):
            return value(*self.args)
        else:
            return value

self.args 以前被赋予 myclass 的实例。

但是,最后我需要使用浮点数组,而且我不能使用这种技术简单地将这个数组转换为dtype=float 数组。我还尝试使用 numpy 视图,这对于 dtype=object 也不起作用。

你有更好的选择吗?我应该覆盖视图方法而不是 getitem 吗?

编辑我将来可能不得不使用 Cython,所以如果你有一个解决方案涉及例如C指针,我有兴趣。

【问题讨论】:

  • 这是一种有趣的方法,但我不确定 numpy 数组是否适合它。通常,当您使用 numpy 时,您将使用使用完整数组或切片的矢量化操作,而不是逐个元素访问。以您的方式对 ndarray 进行子类化,您基本上失去了快速 numpy 操作的所有优势。您最好从零创建自己的类并将所有内容保存为纯 python 结构(列表等)。在性能方面,它将具有可比性。为什么你真的需要惰性评估?您可以使用精美的索引有效地更改某些元素。
  • 你只有一个函数f吗?有常量参数?
  • 你熟悉scipy.sparse吗? dok 格式是一个字典,(i,j) 元组作为键。那和lil(列表列表)是访问/更改所选项目的两种最快方式。
  • @hpaulj : dok 很有趣。但是,我不能将它与 dtype=object 一起使用,就像我上面展示的示例一样:github.com/scipy/scipy/issues/2528
  • @rth:我需要惰性求值而不是使用键(甚至有效)访问数组的原因是,每种影响都可能与不同类型的索引有关。对于上面的示例,我只将对角线设置为可变的。例如,我也可以将一行(或更复杂的)影响到另一个函数 g。

标签: python arrays numpy multidimensional-array lazy-evaluation


【解决方案1】:

在这种情况下,将转换函数绑定到数组的每个索引是没有意义的。

相反,一种更有效的方法是将转换定义为一个函数,以及它所应用的数组的一个子集。这是一个基本的实现,

import numpy as np

class LazyEvaluation(object):
    def __init__(self):
        self.transforms = []

    def add_transform(self, function, selection=slice(None), args={}):
        self.transforms.append( (function, selection, args))

    def __call__(self, x):
        y = x.copy() 
        for function, selection, args in self.transforms:
            y[selection] = function(y[selection], **args)
        return y

可以如下使用:

x = np.ones((6, 6))*2

le = LazyEvaluation()
le.add_transform(lambda x: 0, [[3], [0]]) # equivalent to x[3,0]
le.add_transform(lambda x: x**2, (slice(4), slice(4,6)))  # equivalent to x[4,4:6]
le.add_transform(lambda x: -1,  np.diag_indices(x.shape[0], x.ndim), ) # setting the diagonal 
result =  le(x)
print(result)

哪个打印,

array([[-1.,  2.,  2.,  2.,  4.,  4.],
       [ 2., -1.,  2.,  2.,  4.,  4.],
       [ 2.,  2., -1.,  2.,  4.,  4.],
       [ 0.,  2.,  2., -1.,  4.,  4.],
       [ 2.,  2.,  2.,  2., -1.,  2.],
       [ 2.,  2.,  2.,  2.,  2., -1.]])

通过这种方式,您可以轻松支持所有高级 Numpy 索引(逐个元素访问、切片、精美索引等),同时将数据保存在具有本机数据类型的数组中(float、@987654325 @ 等),这比使用dtype='object' 更有效。

【讨论】:

  • 谢谢,我基本上已经将它实现为 dict 的子类,并且它几乎可以按照我想要的方式工作。但是,出于好奇,我想知道是否有可能在 C/C++ 中以更优雅的方式实现类似的指针?例如,可以声明一个(浮点)指针表,并且在声明时每个指针将指向零或函数的结果,这样我就可以通过调用一个(或多个)函数来更新矩阵。
  • 但要做到这一点,函数的到达调用必须绑定到一个特定的指针,我不确定它是否可行。我希望我已经足够清楚了。
猜你喜欢
  • 2011-07-14
  • 2013-12-30
  • 1970-01-01
  • 2011-05-02
  • 2011-03-24
  • 2016-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多