1

I programmed class which looks something like this:

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

And when I call it like this:

from ex1 import blank
import numpy as np

res=blank(1,2,3)


print(res.tot)

everything works well.

BUT I want to call it like this:

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

Is there an easy way to call it for each i element of this two arrays without editing class code?

Georgy
  • 12,464
  • 7
  • 65
  • 73
  • It's a bit unclear what you are expecting to happen - do you have an error? if so where? what is currently `tot` supposed to be? Also, if you have to append to an array often you probably want a list. – kabanus Sep 28 '18 at 08:35
  • Your `blank` class looks like [it shouldn't be a class](https://www.youtube.com/watch?v=o9pEzgHorH0) in the first place. Consider changing your approach to this problem. Anyway, maybe you can find this helpful: [Is it possible to numpy.vectorize an instance method?](https://stackoverflow.com/questions/48981501/is-it-possible-to-numpy-vectorize-an-instance-method) – Georgy Sep 28 '18 at 08:38

3 Answers3

1

You won't be able to instantiate a class with NumPy arrays as inputs without changing the class code. @PabloAlvarez and @NagaKiran already provided alternative: iterate with zip over arrays and instantiate class for each pair of elements. While this is pretty simple solution, it defeats the purpose of using NumPy with its efficient vectorized operations.

Here is how I suggest you to rewrite the code:

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)

And you can call it both for sole numbers and NumPy arrays like that:

>>> 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]]]

You can see that results are in compliance.


Explanation:
First of all I'd like to note that your calculation of p, k, and l doesn't have to be in the loop. Moreover, calculating k is unnecessary. If you see carefully, how elements of p and l are calculated, they are just geometric progressions (except the 1st element of 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, ...]

So, instead of that loop, you can use np.logspace. Unfortunately, np.logspace doesn't support base parameter as an array, so we have no other choice but to use np.vectorize which is just a loop under the hood...
Calculating of komp though is easily vectorized. You can see it in my example. No need for loops there.

Also, as I already noted in a comment, your class doesn't have to be a class, so I took a liberty of changing it to a function.

Next, note that input parameter c is overwritten, so I got rid of it. Variable y is never used. (Also, you could calculate it just as y = c + a * b * np.sign(a - b))

And finally, I'd like to remark that creating NumPy arrays with np.append is very inefficient (as it was pointed out by @kabanus), so you should always try to create them at once - no loops, no appending.

P.S.: I used np.atleast_2d and np.squeeze in my code and it could be unclear why I did it. They are necessary to avoid if-else clauses where we would check dimensions of array l. You can print intermediate results to see what is really going on there. Nothing difficult.

Georgy
  • 12,464
  • 7
  • 65
  • 73
0

The only way I can think of iterating lists of arrays is by using a function on the main program for iteration and then do the operations you need to do inside the loop.

This solution works for each element of both arrays (note to use zip function for making the iteration in both lists if they have a small size as listed in this answer here):

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

Hope it is what you need!

Georgy
  • 12,464
  • 7
  • 65
  • 73
Pablo Alvarez
  • 106
  • 1
  • 7
0

if it is just calling class with two different list elements, loop can satisfies well

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

You can see list of values for res variable

Naga kiran
  • 4,528
  • 1
  • 17
  • 31