【问题标题】:How to vectorize a class instantiation to allow NumPy arrays as input?如何向量化类实例化以允许 NumPy 数组作为输入?
【发布时间】:2019-03-04 05:08:04
【问题描述】:

我编写的类看起来像这样:

import numpy as np

class blank():
    def __init__(self,a,b,c):
        self.a=a
        self.b=b
        self.c=c

        n=5
        c=a/b*8

        if (a>b):
            y=c+a*b
        else:
            y=c-a*b

        p = np.empty([1,1])
        k = np.empty([1,1])
        l = np.empty([1,1])

        p[0]=b
        k[0]=b*(c-1)
        l[0]=p+k

        for i in range(1, n, 1):

            p=np.append(p,l[i-1])
            k=np.append(k,(p[i]*(c+1)))
            l=np.append(l,p[i]+k[i])

        komp = np.zeros(shape=(n, 1))
        for i in range(0, n):
            pl_avg = (p[i] + l[i]) / 2
            h=pl_avg*3
            komp[i]=pl_avg*h/4

        self.tot=komp+l

当我这样称呼它时:

from ex1 import blank
import numpy as np

res=blank(1,2,3)


print(res.tot)

一切正常。

但我想这样称呼它:

res = blank(np.array([1,2,3]), np.array([3,4,5]), 3)

有没有一种简单的方法可以在不编辑类代码的情况下为这两个数组的每个 i 元素调用它?

【问题讨论】:

  • 有点不清楚您期望发生什么-您有错误吗?如果是在哪里?目前tot 应该是什么?此外,如果您必须经常附加到数组,您可能需要一个列表。
  • 你的blank 类看起来像it shouldn't be a class。考虑改变你解决这个问题的方法。无论如何,也许你会发现这很有帮助:Is it possible to numpy.vectorize an instance method?

标签: python class numpy vectorization


【解决方案1】:

如果不更改类代码,您将无法以 NumPy 数组作为输入来实例化类。 @PabloAlvarez@NagaKiran 已经提供了替代方案:使用 zip 遍历数组并为每对元素实例化类。虽然这是一个非常简单的解决方案,但它破坏了使用 NumPy 及其高效矢量化操作的目的。

以下是我建议您重写代码的方法:

from typing import Union

import numpy as np


def total(a: Union[float, np.ndarray],
          b: Union[float, np.ndarray],
          n: int = 5) -> np.array:
    """Calculates what your self.tot was"""
    bc = 8 * a
    c = bc / b

    vectorized_geometric_progression = np.vectorize(geometric_progression,
                                                    otypes=[np.ndarray])
    l = np.stack(vectorized_geometric_progression(bc, c, n))
    l = np.atleast_2d(l)
    p = np.insert(l[:, :-1], 0, b, axis=1)

    l = np.squeeze(l)
    p = np.squeeze(p)

    pl_avg = (p + l) / 2
    komp = np.array([0.75 * pl_avg ** 2]).T

    return komp + l


def geometric_progression(bc, c, n):
    """Calculates array l"""
    return bc * np.logspace(start=0,
                            stop=n - 1,
                            num=n,
                            base=c + 2)

您可以将它称为唯一数字和 NumPy 数组,如下所示:

>>> print(total(1, 2))
[[2.6750000e+01 6.6750000e+01 3.0675000e+02 1.7467500e+03 1.0386750e+04]
 [5.9600000e+02 6.3600000e+02 8.7600000e+02 2.3160000e+03 1.0956000e+04]
 [2.1176000e+04 2.1216000e+04 2.1456000e+04 2.2896000e+04 3.1536000e+04]
 [7.6205600e+05 7.6209600e+05 7.6233600e+05 7.6377600e+05 7.7241600e+05]
 [2.7433736e+07 2.7433776e+07 2.7434016e+07 2.7435456e+07 2.7444096e+07]]

>>> print(total(3, 4))
[[1.71000000e+02 3.39000000e+02 1.68300000e+03 1.24350000e+04 9.84510000e+04]
 [8.77200000e+03 8.94000000e+03 1.02840000e+04 2.10360000e+04 1.07052000e+05]
 [5.59896000e+05 5.60064000e+05 5.61408000e+05 5.72160000e+05 6.58176000e+05]
 [3.58318320e+07 3.58320000e+07 3.58333440e+07 3.58440960e+07 3.59301120e+07]
 [2.29323574e+09 2.29323590e+09 2.29323725e+09 2.29324800e+09 2.29333402e+09]]

>>> print(total(np.array([1, 3]), np.array([2, 4])))
[[[2.67500000e+01 6.67500000e+01 3.06750000e+02 1.74675000e+03 1.03867500e+04]
  [1.71000000e+02 3.39000000e+02 1.68300000e+03 1.24350000e+04 9.84510000e+04]]

 [[5.96000000e+02 6.36000000e+02 8.76000000e+02 2.31600000e+03 1.09560000e+04]
  [8.77200000e+03 8.94000000e+03 1.02840000e+04 2.10360000e+04 1.07052000e+05]]

 [[2.11760000e+04 2.12160000e+04 2.14560000e+04 2.28960000e+04 3.15360000e+04]
  [5.59896000e+05 5.60064000e+05 5.61408000e+05 5.72160000e+05 6.58176000e+05]]

 [[7.62056000e+05 7.62096000e+05 7.62336000e+05 7.63776000e+05 7.72416000e+05]
  [3.58318320e+07 3.58320000e+07 3.58333440e+07 3.58440960e+07 3.59301120e+07]]

 [[2.74337360e+07 2.74337760e+07 2.74340160e+07 2.74354560e+07 2.74440960e+07]
  [2.29323574e+09 2.29323590e+09 2.29323725e+09 2.29324800e+09 2.29333402e+09]]]

可以看到结果合规。


说明:
首先,我想指出您对pkl 的计算不必在循环中。此外,不需要计算k。如果你仔细看,pl 的元素是如何计算的,它们只是几何级数(p 的第一个元素除外):

p = [b, b*c, b*c*(c+2), b*c*(c+2)**2, b*c*(c+2)**3, b*c*(c+2)**4, ...]
l = [b*c, b*c*(c+2), b*c*(c+2)**2, b*c*(c+2)**3, b*c*(c+2)**4, b*c*(c+2)**5, ...]

因此,您可以使用np.logspace 而不是那个循环。不幸的是,np.logspace 不支持将base 参数作为数组,所以我们别无选择,只能使用np.vectorize,这只是一个循环......
komp 的计算很容易矢量化。您可以在我的示例中看到它。那里不需要循环。

另外,正如我已经在评论中指出的那样,您的类不必是类,所以我冒昧地将其更改为函数。

接下来,注意输入参数c被覆盖了,所以我把它去掉了。变量y 从未使用过。 (另外,你可以像y = c + a * b * np.sign(a - b)一样计算它)

最后,我想指出的是,使用 np.append 创建 NumPy 数组的效率非常低(正如 @kabanus 所指出的那样),因此您应该始终尝试一次创建它们 - 没有循环,没有附加.

P.S.:我在代码中使用了 np.atleast_2dnp.squeeze,可能不清楚我为什么这样做。它们对于避免使用 if-else 子句来检查数组 l 的维度是必要的。你可以print中间结果来看看那里到底发生了什么。没什么难的。

【讨论】:

    【解决方案2】:

    如果只是调用具有两个不同列表元素的类,循环可以很好地满足

    res = [blank(i,j,3) for i,j in zip(np.array([1,2,3]),np.array([3,4,5]))]
    

    你可以看到 res 变量的值列表

    【讨论】:

      【解决方案3】:

      我能想到迭代数组列表的唯一方法是在主程序上使用一个函数进行迭代,然后在循环内执行您需要执行的操作。

      此解决方案适用于两个数组的每个元素(注意使用 zip 函数在这两个列表中进行迭代,如果它们具有此答案here 中列出的小尺寸):

      for n,x in zip(np.array([1,2,3]),np.array([3,4,5])):
          res=blank(n,x,3)
          print(res.tot)
      

      希望这是您所需要的!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-04-27
        • 2013-05-05
        • 1970-01-01
        • 2020-08-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多