1

I have a numpy question

I have a 2D array of values

vals=np.array([[1.0, 2.0, 3.0],[4.0, 5.0, 6.0],[7.0, 8.0, 9.0]], np.float32)

And a 2D array of scale factors

factors=np.array([[1.0, 4.0], [2.0, 5.0], [3.0, 6.0]])

I want to multiply every row in vals with every row in factors to end up with a "scaled_vals" array like the one below NOTE : This output has been corrected form my original post - my apologies for this goof up

[[ 1.  2.  3.]
 [ 8. 10. 12.]
 [21. 24. 27.]]
[[ 4.  8. 12.]
 [20. 25. 30.]
 [42. 48. 54.]]

I'm showing factors with just two columns of data but in actuality it is 'n'.

Any help will be gratefully received.

Doug

===

copied from the comment:

for step in range(2): 
    scaled_vals = np.multiply(vals, factors[0:,step:step+1])
6
  • can you explain how you get the output? I cannot make sense of it with any combination of multiplication. is this only the first element of output? Commented Apr 25, 2020 at 2:00
  • Using a loop to produce the output for step in range(2): scaled_vals = np.multiply(vals, factors[0:,step:step+1]) Commented Apr 25, 2020 at 2:36
  • Now I understand. Thank you for your clarification. I have an easy one liner solution that I post and indices in that makes it very readable for you. Commented Apr 25, 2020 at 15:33
  • All - I completely goofed up my question - my apologies for wasting everyones time. The output that I am ACTUALLY trying to achieve is, [[ 1. 2. 3.] [ 8. 10. 12.] [21. 24. 27.]] [[ 4. 8. 12.] [20. 25. 30.] [42. 48. 54.]] This is what the loop actually produces as noted by hpaulj Commented Apr 25, 2020 at 18:09
  • You can easily modify my post to your setting. I will edit it later to this comment requirement. I think it will be ‘ij,ik->kij’ Commented Apr 25, 2020 at 18:40

4 Answers 4

1

Playing with broadcasting:

vals[:, None, None, :] * factors.T[None, :, :, None]

Output:

array([[[[ 1.,  2.,  3.],
         [ 2.,  4.,  6.],
         [ 3.,  6.,  9.]],

        [[ 4.,  8., 12.],
         [ 5., 10., 15.],
         [ 6., 12., 18.]]],


       [[[ 4.,  5.,  6.],
         [ 8., 10., 12.],
         [12., 15., 18.]],

        [[16., 20., 24.],
         [20., 25., 30.],
         [24., 30., 36.]]],


       [[[ 7.,  8.,  9.],
         [14., 16., 18.],
         [21., 24., 27.]],

        [[28., 32., 36.],
         [35., 40., 45.],
         [42., 48., 54.]]]])
Sign up to request clarification or add additional context in comments.

Comments

1

The (2,3,3) block that you show can be produced with:

In [267]: vals[0,:]*factors.T[:,:,None]                                                                
Out[267]: 
array([[[ 1.,  2.,  3.],
        [ 2.,  4.,  6.],
        [ 3.,  6.,  9.]],

       [[ 4.,  8., 12.],
        [ 5., 10., 15.],
        [ 6., 12., 18.]]])

but the comment loop is;

In [268]: for step in range(2):  
     ...:     print(np.multiply(vals, factors[0:,step:step+1])) 
     ...:                                                                                              
[[ 1.  2.  3.]
 [ 8. 10. 12.]
 [21. 24. 27.]]
[[ 4.  8. 12.]
 [20. 25. 30.]
 [42. 48. 54.]]
In [269]: vals*factors.T[:,:,None]                                                                     
Out[269]: 
array([[[ 1.,  2.,  3.],
        [ 8., 10., 12.],
        [21., 24., 27.]],

       [[ 4.,  8., 12.],
        [20., 25., 30.],
        [42., 48., 54.]]])

Comments

0

You can use einsum one liner (without loop) to multiply your indices in any shape you desire:

output = np.einsum('ij,kl->ilkj',vals,factors)

this multiplies [i,j] from vals into [k,l] from factors and put it at [i,l,k,j] in output. Note @Quan Hoang's answer (upvoted) does the same without looping. However, this is somewhat more readable I think and you can extend it to any other combination you desire.

output:

[[[[ 1.  2.  3.]
   [ 2.  4.  6.]
   [ 3.  6.  9.]]

  [[ 4.  8. 12.]
   [ 5. 10. 15.]
   [ 6. 12. 18.]]]


 [[[ 4.  5.  6.]
   [ 8. 10. 12.]
   [12. 15. 18.]]

  [[16. 20. 24.]
   [20. 25. 30.]
   [24. 30. 36.]]]


 [[[ 7.  8.  9.]
   [14. 16. 18.]
   [21. 24. 27.]]

  [[28. 32. 36.]
   [35. 40. 45.]
   [42. 48. 54.]]]]

UPDATE: Per new multiplication mentioned in comments, solution would be:

output = np.einsum('ij,ik->kij',vals,factors)

this multiplies [i,j] from vals into [i,k] from factors and put it at [k,i,j] in output.

output:

[[[ 1.  2.  3.]
  [ 8. 10. 12.]
  [21. 24. 27.]]

 [[ 4.  8. 12.]
  [20. 25. 30.]
  [42. 48. 54.]]]

2 Comments

Ehsan - Thanks for this. I've never used einsum before - very useful!!!! I wasn't sure whether to mark this answer as the best solution or the one from Quang since both give the result I need and both run in practically the same time. I decided on this one because it's just a shade faster and introduced me to a new capability......
I am glad it helped. einsum is powerful beyond this example. Knowing its powers, you would enjoy using it.
0

The only I can make sense of the inputs and outputs is if you multiply each row of vals with the corresponding index of all factors but you need to match the dimensions of factors and vals.

import numpy as np

vals=np.array([[1.0, 2.0, 3.0],[4.0, 5.0, 6.0],[7.0, 8.0, 9.0]], np.float32) # shape = mxn

print(vals.shape)

factors=np.array([[1.0, 4.0], [2.0, 5.0], [3.0, 6.0]]) # shape = nxk increasing dim to match with vals.shape

m = vals.shape[0]
n = vals.shape[1]
k = factors.shape[1]

print(factors.shape)



result = np.array([np.array([vals[j]*np.repeat(factors[:,i], n).reshape(n,n) for i in range(k)]) for j in range(m)])

print(result[0])

(3, 3)
(3, 2)
[[[ 1.  2.  3.]
  [ 2.  4.  6.]
  [ 3.  6.  9.]]

 [[ 4.  8. 12.]
  [ 5. 10. 15.]
  [ 6. 12. 18.]]]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.