What I'm trying to define is the following idea:
Consider whe have these tensors
a = tf.constant([1., 1.5, 1.2]) # tensor with shape [3,]
b = tf.constant([1., 2., 3.]) # ""
c = tf.constant([3., 0., 6.]) # ""
t = tf.constant([0.5, 0.6, 0.7, 2., 4., 5., 6.]) # tensor with shape [7,]
Now let's consider I want to compute a new tensor, working with each element of previous tensors, for example:
def new_tensor(a, b, c, t):
X = tf.constant([[tf.sin(a*t[1]), b*t[3], c+t[4]],
[tf.cos(b*t[5]), tf.atan2(t[5], c), a+t[2]+b],
[a+t[4], a+b, c*t[0]]])
return X
X should be a tensor with shape [3, 3, 3]. That is, I want to define a function that takes four tensors as input: three of them of same shape and the fourth with a different one. I want the function to compute a tensor (X) for each value of first three inputs (a, b, c).
With this code TensorFlow gives this error:
TypeError: List of Tensors when single Tensor expected
According to this post, this is beacuse tf.constant cannot take a tensor as input, and they recommend to use tf.Variable instead. But I don't think that fits for me because I have to work later with X and don't want to initializate it, etc. I have also read this other post, but couldn't find any answer to my problem.
Is there any way to do what I want? Does my code make sense for my purpose? Thank you in advance.
UPDATE: with jdehesa answer
Taking #jdehesa answer and making resulting tensor more simple:
def new_tensor(a, b, c, t):
# Could also use tf.convert_to_tensor
X = tf.stack([[a+t[1], b*t[1], c+t[1]],
[b*t[0], t[5]+ c, a+t[2]+b],
[a+t[4], a+b, c*t[0]]])
return X
And with tensors:
a = tf.constant([1., 1., 1.]) # tensor with shape [3,]
b = tf.constant([2., 2., 2.]) # ""
c = tf.constant([3., 3., 3.]) # ""
t = tf.constant([1., 1., 1., 1., 1., 1., 1.]) # tensor with shape [7,]
What I get is the following tensor:
# When evaluating x = new_tensor(a,b,c,t)
[[[2. 2. 2.]
[2. 2. 2.]
[4. 4. 4.]]
[[2. 2. 2.]
[4. 4. 4.]
[4. 4. 4.]]
[[2. 2. 2.]
[3. 3. 3.]
[3. 3. 3.]]]
But what I would expected is the following:
[[[2. 2. 4.]
[2. 4. 4.]
[2. 3. 3.]]
[[2. 2. 4.]
[2. 4. 4.]
[2. 3. 3.]]
[[2. 2. 4.]
[2. 4. 4.]
[2. 3. 3.]]]
As what I want it to evaluate to each element of input tensors.
That's correct, you can only pass Python or NumPy values to tf.constant, but you can build your tensor with tf.stack or, if you prefer, generally with tf.convert_to_tensor:
import tensorflow as tf
def new_tensor(a, b, c, t):
# Could also use tf.convert_to_tensor
X = tf.stack([[tf.sin(a*t[1]), b*t[3], c+t[4]],
[tf.cos(b*t[5]), tf.atan2(t[5], c), a+t[2]+b],
[ a+t[4], a+b, c*t[0]]])
return X
with tf.Graph().as_default(), tf.Session() as sess:
a = tf.constant([1., 1.5, 1.2]) # tensor with shape [3,]
b = tf.constant([1., 2., 3.]) # ""
c = tf.constant([3., 0., 6.]) # ""
t = tf.constant([0.5, 0.6, 0.7, 2., 4., 5., 6.]) # tensor with shape [7,]
x = new_tensor(a, b, c, t)
print(sess.run(x))
# [[[ 0.5646425 0.7833269 0.65938467]
# [ 2. 4. 6. ]
# [ 7. 4. 10. ]]
#
# [[ 0.2836622 -0.8390715 -0.7596879 ]
# [ 1.0303768 1.5707964 0.69473827]
# [ 2.7 4.2 4.9 ]]
#
# [[ 5. 5.5 5.2 ]
# [ 2. 3.5 4.2 ]
# [ 1.5 0. 3. ]]]
EDIT: For your second example, to get the result that you want you need to use tf.transpose to change the order of the dimensions of the tensor:
import tensorflow as tf
def new_tensor(a, b, c, t):
# Could also use tf.convert_to_tensor
X = tf.stack([[a+t[1], b*t[1], c+t[1]],
[b*t[0], t[5]+ c, a+t[2]+b],
[a+t[4], a+b, c*t[0]]])
X = tf.transpose(X, (2, 0, 1))
return X
with tf.Graph().as_default(), tf.Session() as sess:
a = tf.constant([1., 1., 1.]) # tensor with shape [3,]
b = tf.constant([2., 2., 2.]) # ""
c = tf.constant([3., 3., 3.]) # ""
t = tf.constant([1., 1., 1., 1., 1., 1., 1.]) # tensor with shape [7,]
x = new_tensor(a, b, c, t)
print(sess.run(x))
# [[[2. 2. 4.]
# [2. 4. 4.]
# [2. 3. 3.]]
#
# [[2. 2. 4.]
# [2. 4. 4.]
# [2. 3. 3.]]
#
# [[2. 2. 4.]
# [2. 4. 4.]
# [2. 3. 3.]]]
Related
I'm trying to recreate a numpy code snippet I wrote in tensorflow, but I'm struggling to find the correct/best tensorflow operations.
Consider the following numpy solution:
import numpy as np
# Initialize a random numpy array:
my_dummy = np.random.random((6, 2, 2, 10))
print(my_dummy)
> [[[[0.6715164 0.58915908 0.36607568 0.73404715 0.69455375 0.52177771
0.91810873 0.85010461 0.37485212 0.35634401]
[0.55885052 0.13041019 0.89774818 0.3363019 0.66634638 0.32054576
0.46174629 0.59975141 0.02283781 0.02997967]]
....
]]]]
# Create random floats, based on channel 0 of my dummy:
random_floats = np.random.random(my_dummy.shape[0])
print(random_floats)
> [0.89351759 0.76734892 0.36810602 0.08513434 0.65511941 0.61297472]
# Create a mask with ones and a shape based on my_dummy:
my_mask = np.ones((my_dummy.shape[0], 1, 1, my_dummy.shape[-1]))
print(my_mask)
> [[[[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]]]
[[[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]]]
[[[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]]]
[[[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]]]
[[[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]]]
[[[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]]]]
# Initialize a rate parameter:
my_rate = 0.5
# Based on my_rate, change the array accordingly:
my_mask[my_rate > random_floats] = [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
print(my_mask)
[[[[1. 0. 1. 0. 1. 0. 1. 0. 1. 0.]]]
[[[1. 0. 1. 0. 1. 0. 1. 0. 1. 0.]]]
[[[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]]]
[[[1. 0. 1. 0. 1. 0. 1. 0. 1. 0.]]]
[[[1. 0. 1. 0. 1. 0. 1. 0. 1. 0.]]]
[[[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]]]]
# Multiply my_dummy with the new mask:
np.multiply(my_dummy, my_mask)
array([[[[0.6715164 , 0.58915908, 0.36607568, 0.73404715, 0.69455375,
0.52177771, 0.91810873, 0.85010461, 0.37485212, 0.35634401],
[0.55885052, 0.13041019, 0.89774818, 0.3363019 , 0.66634638,
0.32054576, 0.46174629, 0.59975141, 0.02283781, 0.02997967]],
[[0.22358676, 0.74959561, 0.11109368, 0.56021714, 0.2767754 ,
0.55156506, 0.15488703, 0.25738564, 0.18588607, 0.57593545],
[0.15804289, 0.87858207, 0.12890992, 0.78828551, 0.52467083,
0.45117698, 0.2605117 , 0.46659721, 0.855278 , 0.29630581]]],
[[[0.381445 , 0. , 0.48308211, 0. , 0.5136352 ,
0. , 0.84428703, 0. , 0.20532641, 0. ],
[0.696645 , 0. , 0.84184568, 0. , 0.01369105,
0. , 0.27683334, 0. , 0.59356542, 0. ]],
[[0.5281193 , 0. , 0.82336821, 0. , 0.63435181,
0. , 0.12824084, 0. , 0.35045286, 0. ],
[0.02205884, 0. , 0.22927706, 0. , 0.45538199,
0. , 0.81220918, 0. , 0.46427429, 0. ]]],
.....
]]]])
In tensorflow, I did this (warning, many imports, I tried a lot of things and no longer sure whether all of them are necessary, just want to ensure you can reproduce immediately):
from keras.engine.base_layer import InputSpec
from tensorflow.python.util import deprecation
from tensorflow.python.framework import ops
from tensorflow.python.eager import context
from tensorflow.python.framework import tensor_shape
from tensorflow.python.framework import tensor_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import random_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.platform import tf_logging as logging
import numbers
import numpy as np
import tensorflow as tf
from tensorflow.python.framework import ops
from tensorflow.python.ops import random_ops
from tensorflow.python.ops import math_ops
from keras import backend as K
# Create my_dummy and convert to tensor object:
my_dummy = np.random.random((6, 2, 2, 4))
my_dummy = ops.convert_to_tensor(my_dummy)
my_dummy.get_shape()
> TensorShape([Dimension(6), Dimension(2), Dimension(2), Dimension(4)])
# Create random floats, like before and inspect tensor with Keras (instead of running a tf session):
random_floats = random_ops.random_uniform([my_dummy.get_shape().as_list()[0]], dtype=my_dummy.dtype)
K.eval(random_floats)
> array([0.74018297, 0.76996447, 0.52047441, 0.28215968, 0.91457724,
0.64637448])
# Like before, create a mask with ones, like before shape (almost completely) based on my_dummy:
my_mask = tf.ones([my_dummy.get_shape()[0], 1, 1, my_dummy.get_shape()[-1]], dtype=x.dtype)
K.eval(my_mask)
> array([[[[1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]]],
[[[1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]]],
[[[1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]]],
[[[1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]]],
[[[1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]]],
[[[1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]]]])
Unfortunately, this where I'm stuck. I did not find a way to alter the entries in the my_mask Tensor object, based on a rate value. One thing I tried was tf.where:
tf.where(rate > random_floats, my_mask, tf.constant([1, 0, 1, 0, 1, 0, 1, 0, 1, 0], dtype = my_dummy.dtype))
but get the error:
ValueError: Shapes must be equal rank, but are 4 and 1 for 'Select_1' (op: 'Select') with input shapes: [6], [6,1,1,10], [10].
Thankful for any advice/help :)
It is basically more or less the same in tensorflow. Showing with smaller shaped data for convenience:
import tensorflow as tf
value_to_assign = tf.constant([[1., 0., 1., 0., 1.]])
rate = tf.constant(.5)
dummy = tf.random_normal(shape=(4, 1, 1, 5))
# random_floats = tf.random_normal(shape=(tf.shape(dummy)[0], ))
random_floats = tf.constant([0.4, 0.6, .7, .2]) # <--using const values to illustrate
init_val = tf.ones((tf.shape(dummy)[0], 1, 1, tf.shape(dummy)[-1]))
mask = tf.Variable(init_val,
trainable=False)
indices = tf.where(tf.equal(True, rate > random_floats))
tiled = tf.tile(value_to_assign,
multiples=[tf.shape(indices)[0], 1])[:, tf.newaxis, tf.newaxis, :]
mask = tf.scatter_nd_update(mask,
indices=indices,
updates=tiled)
res = mask * dummy
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print('MASK')
print(sess.run(mask))
print('DUMMY')
print(sess.run(dummy))
print('RESULT')
print(sess.run(res))
MASK
[[[[1. 0. 1. 0. 1.]]]
[[[1. 1. 1. 1. 1.]]]
[[[1. 1. 1. 1. 1.]]]
[[[1. 0. 1. 0. 1.]]]]
DUMMY
[[[[-1.2031308 -1.6657363 -1.5552464 0.8540495 0.37618718]]]
[[[-0.4468031 0.46417323 -0.3764856 1.1906835 -1.4670093 ]]]
[[[ 1.2066191 -1.4767337 -0.9487017 -0.49180242 -0.33098853]]]
[[[-0.1621628 0.61168176 0.10006899 0.7585997 -0.23903783]]]]
RESULT
[[[[ 1.7753109 0. -0.5451439 -0. -0.53782284]]]
[[[ 0.08024058 -1.8178499 1.183356 1.0895957 -0.9272436 ]]]
[[[-0.5266396 -2.0316153 -1.0043124 -1.1657876 0.6106227 ]]]
[[[-0.46503183 0. 0.01983969 -0. 0.58563703]]]]
While looking up how to calculate pseudo-inverses in numpy (1.15.4) I noticed that numpy.linalg.pinv has a parameter rcond for which the description reads:
rcond : (…) array_like of float
Cutoff for small singular values. Singular values smaller (in
modulus) than rcond * largest_singular_value (again, in modulus)
are set to zero. Broadcasts against the stack of matrices
From my understanding if rcond is a scalar float, all entries
in the output of pinv which would have been smaller than rcond should be set to zero instead (which would be really useful) but this is not what happens, e.g.:
>>> A = np.array([[ 0., 0.3, 1., 0.],
[ 0., 0.4, -0.3, 0.],
[ 0., 1., -0.1, 0.]])
>>> np.linalg.pinv(A, rcond=1e-3)
array([[ 8.31963531e-17, -4.52584594e-17, -5.09901252e-17],
[ 1.82668420e-01, 3.39032588e-01, 8.09586439e-01],
[ 8.95805933e-01, -2.97384188e-01, -1.49788105e-01],
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00]])
What does this parameter actually do? And can I only get the behaviour I actually want by iterating over the whole output matrix again?
Under the hood, a pseudoinverse is calculated using a singular value decomposition. An initial matrix A=UDV^T is inverted as A^+=VD^+U^T, where D is a diagonal matrix with positive real values (singular values). rcond is used to zero out small entries in D. For example:
import numpy as np
# Initial matrix
a = np.array([[1, 0],
[0, 0.1]])
# SVD with diagonal entries in D = [1. , 0.1]
print(np.linalg.svd(a))
# (array([[1., 0.],
# [0., 1.]]),
# array([1. , 0.1]),
# array([[1., 0.],
# [0., 1.]]))
# Pseudoinverse
c = np.linalg.pinv(a)
print(c)
# [[ 1. 0.]
# [ 0. 10.]]
# Reconstruction is perfect
print(np.dot(a, np.dot(c, a)))
# [[1. 0. ]
# [0. 0.1]]
# Zero out all entries in D below rcond * largest_singular_value = 0.2 * 1
# Not entries of the initial or inverse matrices!
d = np.linalg.pinv(a, rcond=0.2)
print(d)
# [[1. 0.]
# [0. 0.]]
# Reconstruction is imperfect
print(np.dot(a, np.dot(d, a)))
# [[1. 0.]
# [0. 0.]]
To just zero out small values of a matrix:
a = np.array([[1, 2],
[3, 0.1]])
a[a < 0.5] = 0
print(a)
# [[1. 2.]
# [3. 0.]]
an issue in tensorflow.
there r two tensor
a=tf.placeholder(tf.float32, [None, 1])
b=tf.placeholder(tf.float32, [None, 1])
I can use
tf.matmul(a,b,False,True)
get a tensor like this
#[[a1b1 a1b2 a1b3 a1b4 …]
#[a2b1 a2b2 a2b3 a2b4 …]
#[a3b1 a3b2 a3b3 a3b4 …]
#…]
but now, if I want to get a tensor like this
#[[a1-b1 a1-b2 a1-b3 a1-b4 …]
#[a2-b1 a2-b2 a2-b3 a2-b4 …]
#[a3-b1 a3-b2 a3-b3 a3-b4 …]
#…]
or this
#[[tf.pow(a1,b1) tf.pow(a1,b2) tf.pow(a1,b3) tf.pow(a1,b4) …]
#[tf.pow(a2,b1) tf.pow(a2,b2) tf.pow(a2,b3) tf.pow(a2,b4) …]
#[tf.pow(a3,b1) tf.pow(a3,b2) tf.pow(a3,b3) tf.pow(a3,b4) …]
#…]
How can I do that?
You can achieve using tensorflow broadcasting feature:
#[[a1-b1 a1-b2 a1-b3 a1-b4 …]
#[a2-b1 a2-b2 a2-b3 a2-b4 …]
#[a3-b1 a3-b2 a3-b3 a3-b4 …]
#…]
# Subtract a and b transpose
c = a - tf.transpose(b)
Output:
#[[ 0. -1. -2. -3.]
#[ 1. 0. -1. -2.]
#[ 2. 1. 0. -1.]
#[ 3. 2. 1. 0.]]
# for input: a:[[1],[2],[3],[4]],b:[[1],[2],[3],[4]]
#[[tf.pow(a1,b1) tf.pow(a1,b2) tf.pow(a1,b3) tf.pow(a1,b4) …]
#[tf.pow(a2,b1) tf.pow(a2,b2) tf.pow(a2,b3) tf.pow(a2,b4) …]
#[tf.pow(a3,b1) tf.pow(a3,b2) tf.pow(a3,b3) tf.pow(a3,b4) …]
#…]
#form (i,j) index matrices and then apply tf.pow
i = tf.tile(a, [1, tf.shape(b)[0]])
j = tf.transpose(tf.tile(b, [1, tf.shape(a)[0]]))
power = tf.pow(i,j)
Output:
# index i index j
#[[1., 1., 1., 1.], [[1., 2., 3., 4.]
#[2., 2., 2., 2.], [1., 2., 3., 4.]
#[3., 3., 3., 3.], [1., 2., 3., 4.]
#[4., 4., 4., 4.] [1., 2., 3., 4.]]
#Output
#[[ 1. 1. 1. 1.]
#[ 2. 4. 8. 16.]
#[ 3. 9. 27. 81.]
#[ 4. 16. 64. 256.]]
I am using the following code and getting an output numpy ndarray of size (2,9) that I am then trying to reshape into size (3,3,2). My hope was that calling reshape using (3,3,2) as the dimensions of the new array would take each row of the 2x9 array and shape it into a 3x3 array and wrap these two 3x3 arrays into another array.
For instance, when I index the result I would like the following behavior:
input: print(result)
output: [[ 2. 2. 1. 0. 8. 5. 2. 4. 5.]
[ 4. 7. 5. 6. 4. 3. -3. 2. 1.]]
result = result.reshape((3,3,2))
DESIRED NEW BEHAVIOR
input: print(result[:,:,0])
output: [[2. 2. 1.]
[0. 8. 5.]
[2. 4. 5.]]
input: print(result[:,:,1])
output: [[ 4. 7. 5.]
[ 6. 4. 3.]
[-3. 2. 1.]]
ACTUAL NEW BEHAVIOR
input: print(result[:,:,0])
output: [[2. 1. 8.]
[2. 5. 7.]
[6. 3. 2.]]
input: print(result[:,:,1])
output: [[ 2. 0. 5.]
[ 4. 4. 5.]
[ 4. -3. 1.]]
Is there a way to specify to reshape that I would like to go row by row along the depth dimension? I'm very confused as to why numpy by default makes the choice it does for reshape.
Here is the code I am using to produce result matrix, this code may or may not be necessary to analyze my issue. I feel as if it will not be necessary but am including it for completeness:
import numpy as np
# im2col implementation assuming width/height dimensions of filter and input_vol
# are the same (i.e. input_vol_width is equal to input_vol_height and the same
# for the filter spatial dimensions, although input_vol_width need not equal
# filter_vol_width)
def im2col(input, filters, input_vol_dims, filter_size_dims, stride):
receptive_field_size = 1
for dim in filter_size_dims:
receptive_field_size *= dim
output_width = output_height = int((input_vol_dims[0]-filter_size_dims[0])/stride + 1)
X_col = np.zeros((receptive_field_size,output_width*output_height))
W_row = np.zeros((len(filters),receptive_field_size))
pos = 0
for i in range(0,input_vol_dims[0]-1,stride):
for j in range(0,input_vol_dims[1]-1,stride):
X_col[:,pos] = input[i:i+stride+1,j:j+stride+1,:].ravel()
pos += 1
for i in range(len(filters)):
W_row[i,:] = filters[i].ravel()
bias = np.array([[1], [0]])
result = np.dot(W_row, X_col) + bias
print(result)
if __name__ == '__main__':
x = np.zeros((7, 7, 3))
x[:,:,0] = np.array([[0,0,0,0,0,0,0],
[0,1,1,0,0,1,0],
[0,2,2,1,1,1,0],
[0,2,0,2,1,0,0],
[0,2,0,0,1,0,0],
[0,0,0,1,1,0,0],
[0,0,0,0,0,0,0]])
x[:,:,1] = np.array([[0,0,0,0,0,0,0],
[0,2,0,1,0,2,0],
[0,0,1,2,1,0,0],
[0,2,0,0,2,0,0],
[0,2,1,0,0,0,0],
[0,1,2,2,2,0,0],
[0,0,0,0,0,0,0]])
x[:,:,2] = np.array([[0,0,0,0,0,0,0],
[0,0,0,2,1,1,0],
[0,0,0,2,2,0,0],
[0,2,1,0,2,2,0],
[0,0,1,2,1,2,0],
[0,2,0,0,2,1,0],
[0,0,0,0,0,0,0]])
w0 = np.zeros((3,3,3))
w0[:,:,0] = np.array([[1,1,0],
[1,-1,1],
[-1,1,1]])
w0[:,:,1] = np.array([[-1,-1,0],
[1,-1,1],
[1,-1,-1]])
w0[:,:,2] = np.array([[0,0,0],
[0,0,1],
[1,0,1]]
w1 = np.zeros((3,3,3))
w1[:,:,0] = np.array([[0,-1,1],
[1,1,0],
[1,1,0]])
w1[:,:,1] = np.array([[-1,-1,1],
[1,0,1],
[0,1,1]])
w1[:,:,2] = np.array([[-1,-1,0],
[1,-1,0],
[1,1,0]])
filters = np.array([w0,w1])
im2col(x,np.array([w0,w1]),x.shape,w0.shape,2)
Let's reshape a bit differently and then do a depth-wise dstack:
arr = np.dstack(result.reshape((-1,3,3)))
arr[..., 0]
array([[2., 2., 1.],
[0., 8., 5.],
[2., 4., 5.]])
Reshape keeps the original order of the elements
In [215]: x=np.array(x)
In [216]: x.shape
Out[216]: (2, 9)
Reshaping the size 9 dimension into a 3x3 keeps the element order that you want:
In [217]: x.reshape(2,3,3)
Out[217]:
array([[[ 2., 2., 1.],
[ 0., 8., 5.],
[ 2., 4., 5.]],
[[ 4., 7., 5.],
[ 6., 4., 3.],
[-3., 2., 1.]]])
But you have to index it with [0,:,:] to see one of those blocks.
To see the same blocks with [:,:,0], you have to move that size 2 dimension to the end. COLDSPEED's dstack does that by iterating on the first dimension, and joining the 2 blocks (each 3x3) on a new third dimension). Another way is to use transpose to reorder the dimensions:
In [218]: x.reshape(2,3,3).transpose(1,2,0)
Out[218]:
array([[[ 2., 4.],
[ 2., 7.],
[ 1., 5.]],
[[ 0., 6.],
[ 8., 4.],
[ 5., 3.]],
[[ 2., -3.],
[ 4., 2.],
[ 5., 1.]]])
In [219]: y = _
In [220]: y.shape
Out[220]: (3, 3, 2)
In [221]: y[:,:,0]
Out[221]:
array([[2., 2., 1.],
[0., 8., 5.],
[2., 4., 5.]])
I want to multiply an array along it's first axis by some vector.
For instance, if a is 2D, b is 1D, and a.shape[0] == b.shape[0], we can do:
a *= b[:, np.newaxis]
What if a has an arbitrary shape? In numpy, the ellipsis "..." can be interpreted as "fill the remaining indices with ':'". Is there an equivalent for filling the remaining axes with None/np.newaxis?
The code below generates the desired result, but I would prefer a general vectorized way to accomplish this without falling back to a for loop.
from __future__ import print_function
import numpy as np
def foo(a, b):
"""
Multiply a along its first axis by b
"""
if len(a.shape) == 1:
a *= b
elif len(a.shape) == 2:
a *= b[:, np.newaxis]
elif len(a.shape) == 3:
a *= b[:, np.newaxis, np.newaxis]
else:
n = a.shape[0]
for i in range(n):
a[i, ...] *= b[i]
n = 10
b = np.arange(n)
a = np.ones((n, 3))
foo(a, b)
print(a)
a = np.ones((n, 3, 3))
foo(a, b)
print(a)
Just reverse the order of the axes:
transpose = a.T
transpose *= b
a.T is a transposed view of a, where "transposed" means reversing the order of the dimensions for arbitrary-dimensional a. We assign a.T to a separate variable so the *= doesn't try to set the a.T attribute; the results still apply to a, since the transpose is a view.
Demo:
In [55]: a = numpy.ones((2, 2, 3))
In [56]: a
Out[56]:
array([[[1., 1., 1.],
[1., 1., 1.]],
[[1., 1., 1.],
[1., 1., 1.]]])
In [57]: transpose = a.T
In [58]: transpose *= [2, 3]
In [59]: a
Out[59]:
array([[[2., 2., 2.],
[2., 2., 2.]],
[[3., 3., 3.],
[3., 3., 3.]]])
Following the idea of the accepted answer, you could skip the variable assignment to the transpose as follows:
arr = np.tile(np.arange(10, dtype=float), 3).reshape(3, 10)
print(arr)
factors = np.array([0.1, 1, 10])
arr.T[:, :] *= factors
print(arr)
Which would print
[[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]]
[[ 0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. ]
[ 0. 10. 20. 30. 40. 50. 60. 70. 80. 90. ]]