如何向量化类实例化以允许NumPy数组作为输入?

2024-04-25 03:51:10 发布

您现在位置:Python中文网/ 问答频道 /正文

我编写了一个类似这样的类:

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元素,有没有一种简单的方法来调用它而不必编辑类代码?你知道吗


Tags: inimportselfnumpyforasnprange
3条回答

迭代数组列表的唯一方法是在主程序上使用一个函数进行迭代,然后在循环中执行需要执行的操作。你知道吗

此解决方案适用于两个数组的每个元素(注意,如果两个列表的大小都很小,请使用^{}函数在这两个列表中进行迭代,如本答案here中所列):

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

希望这是你需要的!你知道吗

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

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

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

在不更改类代码的情况下,您将无法实例化以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不支持将base参数作为数组,因此我们别无选择,只能使用^{},它只是一个隐藏的循环…
尽管komp的计算很容易矢量化。你可以在我的例子中看到。那里不需要环路。你知道吗

另外,正如我在评论中提到的,你的类不一定是类,所以我冒昧地把它改成了函数。你知道吗

接下来,注意输入参数c被覆盖,所以我去掉了它。变量y从不使用。(同样,您可以按y = c + a * b * np.sign(a - b)计算)

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

另外:我在代码中使用了np.atleast_2dnp.squeeze,可能不清楚我为什么这么做。它们是避免if else子句的必要条件,在这些子句中我们将检查数组的维数l。你可以print中间结果来看看那里到底发生了什么。没什么困难。你知道吗

相关问题 更多 >