3

Suppose I have a NumPy 1d-array a:

a = np.array([1, 2, 3])

and I have a function foo:

def foo(x, p):
    ...
    return y

I want to apply foo on a with, say, p from 1 to 3 to make a 2d-array.

Mehdi Abbassi
  • 627
  • 1
  • 7
  • 24
  • What is the problem with a simple `for` loop? – Dani Mesejo Sep 19 '21 at 10:42
  • I want to avoid `for` loops, due to the computational cost. In fact, my question is the simplified version of my main problem where I have millions of rows and arguments. – Mehdi Abbassi Sep 19 '21 at 10:47
  • Is the foo function just the power function? – Dani Mesejo Sep 19 '21 at 10:51
  • @DaniMesejo not necessarily. – Mehdi Abbassi Sep 19 '21 at 11:07
  • 2
    I should identify a proper `duplicate` for this kind of question since it comes up so often. If `foo` can only be called with specific dimensions, e.g. 1d for `x` and scalar for `p`, it has to be called once for each such combination. Calling that many times is what takes up the time, not the details of the iteration mechanism. To reduce time in `numpy` you need to use `numpy` compiled methods without iteration, for example `**` power with (n,) and (1,m) shaped arrays to produce a (n,m) result. **Learn `numpy` before trying to "avoid loops".** – hpaulj Sep 19 '21 at 15:29
  • Thank you sir @hpaulj definitely I am not a NumPy specialist. This is the very reason I ask here and people are here to help each other and provide useful hints and resources. – Mehdi Abbassi Sep 19 '21 at 17:30

3 Answers3

2

Or just:

>>> a[:, None] ** np.arange(1, 4)
array([[ 1,  1,  1],
       [ 2,  4,  8],
       [ 3,  9, 27]], dtype=int32)
>>> 

With a function:

def foo(x, p):
    return x ** p

np.apply_along_axis(lambda x: foo(x, np.arange(1, 4)), 1, a[:, None])

array([[ 1,  1,  1],
       [ 2,  4,  8],
       [ 3,  9, 27]], dtype=int32)
U13-Forward
  • 69,221
  • 14
  • 89
  • 114
  • 1
    Thanks, @U12-Forward but my question is in general. I represent the general question here in a simple fashion just for the sake of simplicity. – Mehdi Abbassi Sep 19 '21 at 10:55
  • 1
    @MehdiAbbassi Edited my answer with working code with functions – U13-Forward Sep 19 '21 at 10:55
  • 1
    @MehdiAbbassi - Asking *in general* can lead to wrong assumptions. This solution is correct but can be as [slow](https://stackoverflow.com/a/23849233/14277722) as a `python` loop. Most functions can be vectorized for fast computation with `numpy`. – Michael Szczesny Sep 19 '21 at 11:00
  • 2
    This doesn't avoid a loop. `apply_along_axis` hides the loop, and is actually slower. It has to perform a trial call just to determine the return value. Please do not recommend it, especially if the OP wants to improve performance. – hpaulj Sep 19 '21 at 15:23
1

In you comment you say you want to give both arguments to function For this purpose you can use map and functools like below:

from functools import partial

a = np.array([1, 2, 3])

def foo(x,y,z):
    return list(z ** y + x)

list(map(partial(foo, z=a), range(1,4), range(1,4)))

Output:

[
 [3, 4, 5],  # [1,2,3]**1+1
 [3, 6, 11], # [1,2,3]**2+2
 [3, 10, 29] # [1,2,3]**3+3
]
I'mahdi
  • 23,382
  • 5
  • 22
  • 30
  • Thanks for your answer. But actually, I have to give both arguments `a` and `p` to my function `foo`. Also, the function is not actually a simple power function, I just put `foo` here like this to simplify the problem, – Mehdi Abbassi Sep 19 '21 at 10:52
0

Firstly, numpy module support math function .So, if you want to apply mathematic function on an array you only have to write it as normal function or a lambda function, then apply it on your array .For example:

def foo(x,p):
    return numpy.power(x,p) 

Note : these much more matematical function in the numpy module . Try to take a look on them :) .

Ayyoub ESSADEQ
  • 776
  • 2
  • 14