Как векторизовать создание экземпляров класса, чтобы разрешить массивы NumPy в качестве входных данных? - PullRequest
0 голосов
/ 28 сентября 2018

Я запрограммировал класс, который выглядит примерно так:

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 элемента этих двух массивов без редактирования кода класса?

Ответы [ 3 ]

0 голосов
/ 28 сентября 2018

если это просто вызов класса с двумя различными элементами списка, цикл может хорошо удовлетворить

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

Вы можете увидеть список значений для переменной res

0 голосов
/ 28 сентября 2018

Вы не сможете создать экземпляр класса с массивами 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]]]

Вы можете видеть, что результаты соответствуют.


Объяснение:
Прежде всего я хотел бы отметить, что ваш расчет p, kl не должно быть в цикле.Более того, вычислять k не нужно.Если вы внимательно посмотрите, как рассчитываются элементы p и l, это просто геометрические прогрессии (кроме 1-го элемента 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))

И, наконец, я хотел бы отметить, что создание массивов NumPy с np.append очень неэффективно (как было отмечено @kabanus),поэтому вы всегда должны пытаться создать их сразу - без циклов, без добавления.

PS: я использовал np.atleast_2d и np.squeeze в своем коде, и может быть неясно, почему я это сделал.Они необходимы, чтобы избежать предложений if-else, где мы проверяли бы размеры массива l.Вы можете print промежуточные результаты, чтобы увидеть, что на самом деле там происходит.Ничего сложного.

0 голосов
/ 28 сентября 2018

Единственный способ, которым я могу думать об итерации списков массивов, - это использовать функцию в главной программе для итерации, а затем выполнять операции, которые необходимо выполнить внутри цикла.

Это решение работает для каждого элемента обоих массивов (обратите внимание на использование zip функции для выполнения итерации в обоих списках, если они имеют небольшой размер, как указано в этом ответе здесь ):

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

Надеюсь, это то, что вам нужно!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...