Concatenate Numpy arrays of different shape - python

I would like to create a numpy array by concatenating two or more numpy arrays with shape (1, x, 1) where x is variable.
Here is the problem in detail.
x1 = #numpy array with shape (x,)
x2 = #numpy array with shape (y,)
#create batch
x1 = np.expand_dims(x1, 0) #shape (1, x)
x2 = np.expand_dims(x2, 0) #shape (1, y)
#add channel dimension
x1 = np.expand_dims(x1, -1) #shape (1, x, 1)
x2 = np.expand_dims(x2, -1) #shape (1, y, 1)
#merge the two arrays
x = np.concatenate((x1, x2), axis=0)
#expected shape (2, ??, 1)
Note the expected shape (2, ??, 1). I am wondering if what I am trying to do is doable.
Executing this code raises a ValueError:
ValueError: all the input array dimensions for the concatenation axis must match exactly, but along dimension 1, the array at index 0 has size 138241 and the array at index 1 has size 104321

Related

Howw to reshape X and Y from (300, 1000,50) to (300000, 50)?

I have a dataset:
X.shape = (300, 1000, 50)
Y.shape = (300,)
Y is the true values (4 options: [0..3])
I want to reshape X to: (300000, 50) and Y to (300000,)
The new X.shape will be [X.shape[0]*X.shape[1], X.shape[2]]
The new Y.shape will be [X.shape[0]*X.shape[1],] and it will contain the right duplicate values of Y (according to the new shape).
How can I do it?
You can do
X = X.reshape(-1, Y.shape[-1])
However, it becomes unclear how to broadcast the arrays then, since broadcasting aligns to the right (implicitly prepends unit dimensions, not appends).
You could repeat the elements of Y:
Y = np.repeat(Y, 1000)
or the entire array itself:
Y = np.tile(Y, 4)
Both are overkill.
Just use proper broadcasting instead:
# X untouched
Y = Y[:, None, None]
Or equivalently
Y = Y.reshape(-1, 1, 1)

How to make dot.product of 2D and 3D matrices (taking it for each dimension separately)

I need to calculate dot product of two matrices. Probably tensordot would do the job, however I am struggling to figure out exact solution.
The simple option
res = np.dot(x, fullkernel[:, :-1].transpose())
works fine, where x is of shape (9999,), fullkernel of shape (980,10000), and res is of shape (1, 980).
Now I need to do similar thing with 2 dimensions. Thus my x now has shape (9999, 2), fullkernel (2, 980, 10000).
Literally I want my result "res" to be of 2 dimensions, where each one is dot.product of one column of x and one dimension of fullkernel.
You can do that like this:
res = np.einsum('ki,ijk->ij', x, fullkernel[:, :, :-1])
print(res.shape)
# (2, 980)
If you want to have the additional singleton dimension in the middle just do:
res = np.expand_dims(res, 1)
An equivalent solution with # / np.matmul would be:
res = np.expand_dims(x.T, 1) # np.moveaxis(fullkernel[:, :, :-1], 2, 1)
print(res.shape)
# (2, 1, 980)

numpy pad with zeros creates 2d array instead of desired 1d

I am trying to pad a 1d numpy array with zeros.
Here is my code
v = np.random.rand(100, 1)
pad_size = 100
v = np.pad(v, (pad_size, 0), 'constant')
result is 200x101 array, whose last column is [0,0,0,... <v>], (leading 100 zeros),
and all 1st 100 columns are zeros.
How to get my desired array
[0,0,0,..0,<v>]
of size (len(v)+pad_size, 1)?
The pad output is 2D because the pad input was 2D. You made a 2D array with rand for some reason:
v = np.random.rand(100, 1)
If you wanted a 1D array, you should have made a 1D array:
v = np.random.rand(100)
If you wanted a 1-column 2D array, then you're using pad incorrectly. The second argument should be ((100, 0), (0, 0)): padding 100 elements before in the first axis, 0 elements after in the first axis, 0 elements before in the second axis, 0 elements after in the second axis:
v = np.random.rand(100, 1)
pad_size = 100
v = np.pad(v, ((pad_size, 0), (0, 0)), 'constant')
For a 1-row 2D array, you would need to adjust both the rand call and the pad call:
v = np.random.rand(1, 100)
pad_size = 100
v = np.pad(v, ((0, 0), (pad_size, 0)), 'constant')
np.hstack((np.zeros((200, 100)), your v))
np.concatenate((np.zeros((200, 100)), your v), axis=1)
may be your desire this:

How to use Keras Conv2D layer with variably shaped input

I've got a NP array called X_train with the following properties:
X_train.shape = (139,)
X_train[0].shape = (210, 224, 3)
X_train[1].shape = (220,180, 3)
In other words, there are 139 observations. Each image has a different width and height, but they all have 3 channels. So the dimension should be (139, None, None, 3) where None = variable.
Since you don't include the dimension for the number of observations in the layer, for the Conv2D layer I used input_shape=(None,None,3). But that gives me the error:
expected conv2d_1_input to have 4 dimensions, but got array with shape
(139, 1)
My guess is that the problem is that the input shape is (139,) instead of (139, None, None, 3). I'm not sure how to convert to that however.
One possible solution to your problem is to fill the arrays with zeros so that they all have a similar size. Afterwards, your input shape will be something like (139, max_x_dimension, max_y_dimension, 3).
The following functions will do the job:
import numpy as np
def fillwithzeros(inputarray, outputshape):
"""
Fills input array with dtype 'object' so that all arrays have the same shape as 'outputshape'
inputarray: input numpy array
outputshape: max dimensions in inputarray (obtained with the function 'findmaxshape')
output: inputarray filled with zeros
"""
length = len(inputarray)
output = np.zeros((length,)+outputshape, dtype=np.uint8)
for i in range(length):
output[i][:inputarray[i].shape[0],:inputarray[i].shape[1],:] = inputarray[i]
return output
def findmaxshape(inputarray):
"""
Finds maximum x and y in an inputarray with dtype 'object' and 3 dimensions
inputarray: input numpy array
output: detected maximum shape
"""
max_x, max_y, max_z = 0, 0, 0
for array in inputarray:
x, y, z = array.shape
if x > max_x:
max_x = x
if y > max_y:
max_y = y
if z > max_z:
max_z = z
return(max_x, max_y, max_z)
#Create random data similar to your data
random_data1 = np.random.randint(0,255, 210*224*3).reshape((210, 224, 3))
random_data2 = np.random.randint(0,255, 220*180*3).reshape((220, 180, 3))
X_train = np.array([random_data1, random_data2])
#Convert X_train so that all images have the same shape
new_shape = findmaxshape(X_train)
new_X_train = fillwithzeros(X_train, new_shape)

Batched 4D tensor Tensorflow indexing

Given
batch_images: 4D tensor of shape (B, H, W, C)
x: 3D tensor of shape (B, H, W)
y: 3D tensor of shape (B, H, W)
Goal
How can I index into batch_images using the x and y coordinates to obtain a 4D tensor of shape B, H, W, C. That is, I want to obtain for each batch, and for each pair (x, y) a tensor of shape C.
In numpy, this would be achieved using input_img[np.arange(B)[:,None,None], y, x] for example but I can't seem to make it work in tensorflow.
My attempt so far
def get_pixel_value(img, x, y):
"""
Utility function to get pixel value for
coordinate vectors x and y from a 4D tensor image.
"""
H = tf.shape(img)[1]
W = tf.shape(img)[2]
C = tf.shape(img)[3]
# flatten image
img_flat = tf.reshape(img, [-1, C])
# flatten idx
idx_flat = (x*W) + y
return tf.gather(img_flat, idx_flat)
which is returning an incorrect tensor of shape (B, H, W).
It should be possible to do it by flattening the tensor as you've done, but the batch dimension has to be taken into account in the index calculation.
In order to do this, you'll have to make an additional dummy batch index tensor with the same shape as x and y that always contains the index of the current batch.
This is basically the np.arange(B) from your numpy example, which is missing from your TensorFlow code.
You can also simplify things a bit by using tf.gather_nd, which does the index calculations for you.
Here's an example:
import numpy as np
import tensorflow as tf
# Example tensors
M = np.random.uniform(size=(3, 4, 5, 6))
x = np.random.randint(0, 5, size=(3, 4, 5))
y = np.random.randint(0, 4, size=(3, 4, 5))
def get_pixel_value(img, x, y):
"""
Utility function that composes a new image, with pixels taken
from the coordinates given in x and y.
The shapes of x and y have to match.
The batch order is preserved.
"""
# We assume that x and y have the same shape.
shape = tf.shape(x)
batch_size = shape[0]
height = shape[1]
width = shape[2]
# Create a tensor that indexes into the same batch.
# This is needed for gather_nd to work.
batch_idx = tf.range(0, batch_size)
batch_idx = tf.reshape(batch_idx, (batch_size, 1, 1))
b = tf.tile(batch_idx, (1, height, width))
indices = tf.pack([b, y, x], 3)
return tf.gather_nd(img, indices)
s = tf.Session()
print(s.run(get_pixel_value(M, x, y)).shape)
# Should print (3, 4, 5, 6).
# We've composed a new image of the same size from randomly picked x and y
# coordinates of each original image.

Categories

Resources