Numpy: trouble obtaining a 2D slice of 3D array - python

Similar questions have been asked on SO, but never in the form that I needed.
I seem to be having trouble understanding NumPy slicing behavior.
Lets say I have an Numpy Array of shape 512x512x120
vol1=some_numpy_array
print(x.shape) #result: (512,512,120)
I want to take a "z-slice" of this array, however I end up with a 512x120 array instead of a 512x512 one
I try the following code for instance
zSlice=(vol1[:][:][2]).squeeze()
print(zSlice.shape()) #result: (512,120)
Why is the resulting array shape (512,120) and not (512,512)? How can I fix this problem?

You have to slice at once:
vol1[:, :, 2].squeeze()
>>> vol1[:, :, 2].squeeze().shape
(512, 512)
Because doing [:] repeatedly doesn't do anything:
>>> (vol1[:][:] == vol1).all()
True
>>>
It's because [:] gets the whole of the list... doing it multiple times does not change anything.

The problem with vol1[:][:][2] is:
vol1[:] will give the entire array, then again [:] gives the entire array, and finally [2] gives the array at index 2 to in the outer axis, eventually vol1[:][:][2] is nothing but vol1[2] which is not what you want.
You need to take numpy array slice:
vol1[:, :, 2].
Now, it will take all the items from outermost, and outer axes, but only item at index 2 for the inner most axis.

Related

Numpy slice of first arbitrary dimensions

There is this great Question/Answer about slicing the last dimension:
Numpy slice of arbitrary dimensions: for slicing a numpy array to obtain the i-th index in the last dimension, one can use ... or Ellipsis,
slice = myarray[...,i]
What if the first N dimensions are needed ?
For 3D myarray, N=2:
slice = myarray[:,:,0]
For 4D myarray, N=2:
slice = myarray[:,:,0,0]
Does this can be generalized to an arbitrary dimension?
I don't think there's any built-in syntactic sugar for that, but slices are just objects like anything else. The slice(None) object is what is created from :, and otherwise just picking the index 0 works fine.
myarray[(slice(None),)*N+(0,)*(myarray.ndim-N)]
Note the comma in (slice(None),). Python doesn't create tuples from parentheses by default unless the parentheses are empty. The comma signifies that don't just want to compute whatever's on the inside.
Slices are nice because they give you a view into the object instead of a copy of the object. You can use the same idea to, e.g., iterate over everything except the N-th dimension on the N-th dimension. There have been some stackoverflow questions about that, and they've almost unanimously resorted to rolling the indices and other things that I think are hard to reason about in high-dimensional spaces. Slice tuples are your friend.
From the comments, #PaulPanzer points out another technique that I rather like.
myarray.T[(myarray.ndim-N)*(0,)].T
First, transposes in numpy are view-operations instead of copy-operations. This isn't inefficient in the slightest. Here's how it works:
Start with myarray with dimensions (0,...,k)
The transpose myarray.T reorders those to (k,...,0)
The whole goal is to fix the last myarray.ndim-N dimensions from the original array, so we select those with [(myarray.ndim-N)*(0,)], which grabs the first myarray.ndim-N dimensions from this array.
They're in the wrong order. We have dimensions (N-1,...,0). Use another transpose with .T to get the ordering (0,...,N-1) instead.

Numpy-like slicing in Julia

In Python/Numpy I can slice arrays in this form:
arr = np.ones((3,4,5))
arr[2]
and the shape will be maintained:
(arr[2]).shape # prints (4, 5)
Which means that, if I want to keep the shape of the array, the following code works for N-dimensional arrays
arr = np.ones((3,4,5,2,2))
(arr[2]).shape # prints (4, 5, 2, 2)
This is great if I want to write functions that work for N-dim arrays preserving their output.
In Julia, however, the same action does not preserve the structure:
arr = ones(3,4,5)
size(arr[3]) # prints () (0-dimensinoal)
size(arr[3,:]) # prints (20,)
because of partial linear indexing. So if want to keep the original dimensions I need to write arr[3,:,:], which only works for 3D arrays. If I want a 4D array I would have to use arr[3,:,:,:] and so on. The code isn't general.
Furthermore, when you get to array that are 5 dimensions or more (which is the case I'm working with now) this notation gets extremely cumbersome.
Is there any way I can write code like I do in Python and make it general? I couldn't even think of a nice clean way with reshape, let alone a way that's as clean as Python.
Notice that in Python the shape is only preserved if you slice the first dimension of the array. In Julia you can use slicedim(A, d, i) to slice dimension d of array A at index i.

Python: Creating multi dimensional array of multidimensional zero array

Hello I have the following question. I create zero arrays of dimension (40,30,80). Now I need 7*7*7 of these zero arrays in an array. How can I do this?
One of my matrices is created like this:
import numpy as np
zeroMatrix = np.zeros((40,30,80))
My first method was to put the zero matrices in a 7*7*7 list. But i want to have it all in a numpy array. I know that there is a way with structured arrays I think, but i dont know how. If i copy my 7*7*7 list with np.copy() it creates a numpy array with the given shape, but there must be a way to do this instantly, isnt there?
EDIT
Maybe I have to make my question clearer. I have a 7*7 list of my zero matrices. In a for loop all of that arrays will be modified. In another step, this tempory list is appended to an empty list which will have a length of 7 in the end ( So i append the 7*7 list 7 times to the empty list. In the end I have a 7*7*7 List of those matrices. But I think this will be better If I have a numpy array of these zero matrices from the beginning.
Building an array of same-shaped arrays is not well supported by numpy which prefers to create a maximum depth array of minimum depth elements instead.
It turns out that numpy.frompyfunc is quite useful in circumventing this tendency where it is unwanted.
In your specific case one could do:
result = np.frompyfunc(zeroMatrix.copy, 0, 1)(np.empty((7, 7, 7), object))
Indeed:
>>> result.shape
(7, 7, 7)
>>> result.dtype
dtype('O')
>>> result[0, 0, 0].shape
(40, 30, 80)

Accessing second index in 3-d numpy array

I have a 2-d numpy array that I convert into a 3-d array by using:
trainSet = numpy.reshape(trainSet, (trainSet.shape[0], 1, trainSet.shape[1]))
My problem is I'm trying to adjust values in the 1st axis (the new one). However, no matter what I try I can not actually figure out how to access the 1's in the new axis. I've tried a multitude of print statements such as
print(trainSet[0][0][0])
print(trainSet[1][0][1])
but no matter what I try I can not print out any 1's, just the contents of the 2-d array. I know it's a 3d array because when I use
print(trainSet.shape)
I get
(12, 1, 793)
Any help would be immensely appreciated!
The one refers to the size of that particular dimension, not the content of the array. The array [[5 5]] has shape (1, 2) but, still, you don't really expect its values must include 1 for that reason, do you?
What inserting a dimension of length one does is increasing the nesting level by one, so a 2d array [[a b] [c d]] will become [[[a b] [c d]]] or [[[a b]] [[c d]]] or [[[a] [b]] [[c] [d]]] depending on where you insert the new axis.
In particular, there are no new elements.
If I didn't mistake your question, what you want is
trainSet[:,0,:]
the amount of values remains the same, you're just creating a 3D structure instead of the 2D structure.

Interpreting numpy.where results

I'm confused by what the results of numpy.where mean, and how to use it to index into an array.
Have a look at the code sample below:
import numpy as np
a = np.random.randn(10,10,2)
indices = np.where(a[:,:,0] > 0.5)
I expect the indices array to be 2-dim and contain the indices where the condition is true. We can see that by
indices = np.array(indices)
indices.shape # (2,120)
So it looks like indices is acting on the flattened array of some sort, but I'm not able to figure out exactly how. More confusingly,
a.shape # (20,20,2)
a[indices].shape # (2,120,20,2)
Question:
How does indexing my array with the output of np.where actually grow the size of the array? What is going on here?
You are basing your indexing on a wrong assumption: np.where returns something that can be immediatly used for advanced indexing (it's a tuple of np.ndarrays). But you convert it to a numpy array (so it's now a np.ndarray of np.ndarrays).
So
import numpy as np
a = np.random.randn(10,10,2)
indices = np.where(a[:,:,0] > 0.5)
a[:,:,0][indices]
# If you do a[indices] the result would be different, I'm not sure what
# you intended.
gives you the elements that are found by np.where. If you convert indices to a np.array it triggers another form of indexing (see this section of the numpy docs) and the warning message in the docs gets very important. That's the reason why it increases the total size of your array.
Some additional information about what np.where means: You get a tuple containing n arrays. n is the number of dimensions of the input array. So the first element that satisfies the condition has index [0][0], [1][0], ... [n][0] and not [0][0], [0][1], ... [0][n]. So in your case you have (2, 120) meaning you have 2 dimensions and 120 found points.

Categories

Resources