1

I am fairly new to numpy. I want to apply a custom function to 1, 2 or more rows (or columns). How can I do this? Before this is marked as duplicate, I want to point out that the only thread I found that does this is how to apply a generic function over numpy rows? and how to apply a generic function over numpy rows?. There are two issues with this post:

a) As a beginner, I am not quite sure what operation like A[:,None,:] does.

b) That operation doesn't work in my case. Please see below.

Let's assume that Matrix M is:

import numpy as np
M = np.array([[8, 3, 2],
              [6, 1, 2],
              [1, 2, 4]])

Now, I would want to calculate product of combination of all three rows. For this, I have created a custom function. Actual operation of the function could be different from multiplication. Multiplication is just an example.

def myf(a,b): return(a*b) 

I have taken numpy array product as an example. Actual custom function could be different, but no matter what the operation is, the function will always return a numpy array. i.e. it will take two equally-sized numpy 1-D array and return 1-D array. In myf I am assuming that a and b are each np.array.

I want to be able to apply custom function to any two rows or columns, or even three rows (recursively applying function).

Expected output after multiplying two rows recursively:

If I apply pairwise row-operation:

[[48,3,4],
 [6,2,8],
 [8,6,8]]

OR ( The order of application of custom function doesn't matter. Hence, the actual position of rows in the output matrix won't matter. Below matrix will be fine as well.)

[[6,2,8],
 [48,3,4],  #row1 and 2 are swapped
 [8,6,8]]

Similarly, if I apply pairwise operation on columns, I would get

[[24, 6, 16]
 [6,  2, 12] 
 [2,  8, 4]]

Similarly, if I apply custom function to all three rows, I would get:

[48,6,16] #row-wise

OR

[48,12,8] #column-wise

I tried a few approaches after reading SO:

1:

vf=np.vectorize(myf)
vf(M,M)

However, above function applies custom function element-wise rather than row-wise or columnwise.

2:

I also tried:

M[:,None,:].dot(M) #dot mimics multiplication. Python wouldn't accept `*`

There are two problems with this:

a) I don't know what the output is.

b) I cannot apply custom function.

Can someone please help me? I'd appreciate any help.

I am open to numpy and scipy.


Some experts have requested desired output. Let's assume that the desired output is [[48,3,4], [6,2,8], [8,6,8]].

However, I'd appreciate some guidance on customizing the solution for 2 or more columns and 2 or more rows.

watchtower
  • 4,140
  • 14
  • 50
  • 92
  • What is the desired output? – Nils Werner Dec 06 '18 at 08:46
  • 1
    In your example, you are "rolling" your function over (wrapped) `numpy` array rather than what the title seems to indicate: apply some multivariate function over more than one (but specific) rows. This actually makes things much more complicated – ZisIsNotZis Dec 06 '18 at 08:47
  • @Nils Werner. Thanks. Can we apply custom function to rows? `[[48,3,4], [6,2,8], [8,6,8]]` Also, I'd appreciate if the solution to be customizable for 2 or more columns and 2 or more rows. – watchtower Dec 06 '18 at 08:48
  • Maybe you are looking for [`ufunc`](https://docs.scipy.org/doc/numpy/reference/ufuncs.html)? – suvayu Dec 06 '18 at 08:50
  • @ZizisNot...Do you want me to change the title? If so, I'd appreciate if you could suggest something. I am not sure about it. – watchtower Dec 06 '18 at 08:51
  • @Nils Werner: I think I gave sample output with row multiplication. Is anything missing? I have also provided sample input. – watchtower Dec 06 '18 at 08:52
  • I would say something like "*roll custom multivariate function over `numpy` array with wrapped boundary*". The "row" here isn't the problem. – ZisIsNotZis Dec 06 '18 at 09:23

1 Answers1

1

You can simply roll your axis along the 0th axis

np.roll(M, -1, axis=0)
# array([[6, 1, 2],
#        [1, 2, 4],
#        [8, 3, 2]])

And multiply the result with your original array

M * np.roll(M, -1, axis=0)
# array([[48,  3,  4],
#        [ 6,  2,  8],
#        [ 8,  6,  8]])

If you want to incorporate more than two rows, you can roll it more than once:

M * np.roll(M, -1, axis=0) * np.roll(M, -2, axis=0)
# array([[48,  6, 16],
#        [48,  6, 16],
#        [48,  6, 16]])
Nils Werner
  • 34,832
  • 7
  • 76
  • 98
  • Thanks. Is there any way to use the custom function? – watchtower Dec 06 '18 at 08:57
  • That heavily depends on what your custom function does. – Nils Werner Dec 06 '18 at 08:59
  • Thanks. It would *always* accept two 1-D `numpy` array of same dimension and return 1-D `numpy` array. I believe I have clarified this in the question. – watchtower Dec 06 '18 at 09:00
  • Then no. It needs to accept 2D arrays. But in many cases, a function that accepts 1D arrays can be made to accept 2D arrays. – Nils Werner Dec 06 '18 at 09:01
  • Thanks. Do you mind explaining why it must accept 2D arrays. I am curious. I didn't follow the logic. – watchtower Dec 06 '18 at 09:02
  • 1
    Because both arrays are 2D. You can iterate over it rowwise, but that will make things slow. Just open a new question with your actual function and ask how you can vectorize it to 2D operations. – Nils Werner Dec 06 '18 at 09:03