Alternative to arange with numpy arrays as boundaries - python

I have two numpy arrays acting as lower and upper boundaries of a range of vectors that I want to generate.
In the a similar way that arange() works, I would like to generate the intermediate members as in the example:
lower_boundary = np.array([1,1])
upper_boundary = np.array([3,3])
expected_result = [[1,1], [1,2], [1,3], [2,1], [2,2], [2,3], [3,1], [3,2], [3,3]]
The result can be a list or another numpy array. So far I have managed to workaround this scenario with nested loops, but the dimensions of 'lower_boundary' and 'upper_boundary' may vary, and my approach is not applicable.
In a typical scenario, both boundaries have at least 4 dimensions.

You can use np.indicies to get a range of index values of your desired range (upper_boundary - lower boundary + 1), reshape it to your needs (reshape(len(upper_boundary),-1)) and add your lower_boundry to values resulting in;
>>> np.indices(upper_boundary - lower_boundary + 1).reshape(len(upper_boundary),-1).T + lower_boundary
array([[1, 1],
[1, 2],
[1, 3],
[2, 1],
[2, 2],
[2, 3],
[3, 1],
[3, 2],
[3, 3]])
Edit: I forgot to correct the code before posting, it should be like this.
Thanks #Divakar for the fix.

Related

Taking rows of a matrix given a batch of indices - Python

How can we extract the rows of a matrix given a batch of indices (in Python)?
i = [[0,1],[1,2],[2,3]]
a = jnp.array([[1,2,3,4],[2,3,4,5]])
def extract(A,idx):
A = A[:,idx]
return A
B = extract(a,i)
I expect to get this result (where the matrices are stacked):
B = [[[1,2],
[2,3]],
[[2,3],
[3,4]],
[3,4],
[4,5]]]
And NOT:
B_ = [[1, 2],
[2, 3],
[3, 4]],
[[2, 3],
[3 ,4],
[4, 5]]]
In this case, the rows are stacked, but I want to stack the different matrices.
I tried using
jax.vmap(extract)(a,i),
but this gives me an error since a and i don't have the same dimension.... Is there an alternative, without using loops?
You can do this with vmap if you specify in_axes in the right way, and convert your index list into an index array:
vmap(extract, in_axes=(None, 0))(a, jnp.array(i))
# DeviceArray([[[1, 2],
# [2, 3]],
#
# [[2, 3],
# [3, 4]],
#
# [[3, 4],
# [4, 5]]], dtype=int32)
When you say in_axes=(None, 0), it specifies that you want the first argument to be unmapped, and you want the second argument to be mapped along its leading axis.
The reason you need to convert i from a list to an array is because JAX will only map over array arguments: if vmap encounters a collection like a list, tuple, dict, or a general pytree, it attempts to map over each array-like value within the collection.
You can use indexing right away on the matrix a transposed:
a.T[i,:]

Finding the Unique Arrays in an List of Arrays

I have a list of arrays, say
List = [A,B,C,D,E,...]
where each A,B,C etc. is an nxn array.
I wish to have the most efficient algorithm to find the unique nxn arrays in the list. That is, say if all entries of A and B are equal, then we discard one of them and generate the list
UniqueList = [A,C,D,E,...]
Not sure if there is a faster way, but I think this should be pretty fast (using the built-in unique function of numpy and choosing axis=0 to look for nxn unique arrays. More detail in the numpy doc):
[i for i in np.unique(np.array(List),axis=0)]
Example:
A = np.array([[1,1],[1,1]])
B = np.array([[1,1],[1,2]])
List = [A,B,A]
[array([[1, 1],
[1, 1]]),
array([[1, 1],
[1, 2]]),
array([[1, 1],
[1, 1]])]
Output:
[array([[1, 1],
[1, 1]]),
array([[1, 1],
[1, 2]])]

Set the last element equal to the first element in a multidimensional numpy array

For example I have an array:
[[[[1 2][3 4]]][[[1 2][3 4]]]]
How would I set 4 equal to 1? I used
array[-1][-1][-1][-1] = array[0][0][0][0]
but I got an error because of it later on. Is there a more general way of doing this?
You can "cheat" by updating the flattened array:
a = np.array([[[1,2],[3,4]],[[1,2],[3,4]]])
a.flat[-1] = a.flat[0]
a
array([[[1, 2],
[3, 4]],
[[1, 2],
[3, 1]]])

Converting values of Existing Numpy ndarray to tuples

Let's say I have a numpy.ndarray with shape (2,3,2) as below,
arr = np.array([[[1,3], [2,5], [1,2]],[[3,3], [6,5], [5,2]]])
I want to reshape it in such a way that:
arr.shape == (2,3)
arr == [[(1,3), (2,5), (1,2)],[(3,3), (6,5), (5,2)]]
and
each value of arr is a size 2 tuple
The reason I want to do this is that I want to take the minimum along axis 0 of the 3dimensional array, but I want to preserve the value that the min of the rows in paired with.
arr = np.array(
[[[1, 4],
[2, 1],
[5, 2]],
[[3, 3],
[6, 5],
[1, 7]]])
print(np.min(arr, axis=0))
>>> [[1,3],
[2,1],
[1,2]]
>>>Should be
[[1,4],
[2,1],
[1,7]]
If the array contained tuples, it would be 2 dimensional, and the comparison operator for minimize would still function correctly,
so I would get the correct result. But I haven't found any way to do this besides iterating over the arrays, which is inefficient and obvious in implementation.
Is it possible to perform this conversion efficiently in numpy?
Don't use tuples at all - just view it as a structured array, which supports the lexical comparison you're after:
a = np.array([[[1,3], [2,5], [1,2]],[[3,3], [6,5], [5,2]]])
a_pairs = a.view([('f0', a.dtype), ('f1', a.dtype)]).squeeze(axis=-1)
min_pair = np.partition(a_pairs, 0, axis=0)[0] # min doesn't work on structured types :(
array([(1, 4), (2, 1), (1, 7)],
dtype=[('f0', '<i4'), ('f1', '<i4')])
First, let's find out which pairs to take:
first_eq = arr[0,:,0] == arr[1,:,0]
which_compare = np.where(first_eq, 1, 0)[0]
winner = arr[:,:,which_compare].argmin(axis=0)
Here, first_eq is True where the first elements match, so we would need to compare the second elements. It's [False, False, False] in your example. which_compare then is [0, 0, 0] (because the first element of each pair is what we will compare). Finally, winner tells us which of the two pairs to choose along the second axis. It is [0, 0, 1].
The last step is to extract the winners:
arr[winner, np.arange(arr.shape[1])]
That is, take the winner (0 or 1) at each point along the second axis.
Here's one way -
# Get each row being fused with scaling based on scale being decided
# based off the max values from the second col. Get argmin indices.
idx = (arr[...,1] + arr[...,0]*(arr[...,1].max()+1)).argmin(0)
# Finally use advanced-indexing to get those rows off array
out = arr[idx, np.arange(arr.shape[1])]
Sample run -
In [692]: arr
Out[692]:
array([[[3, 4],
[2, 1],
[5, 2]],
[[3, 3],
[6, 5],
[5, 1]]])
In [693]: out
Out[693]:
array([[3, 3],
[2, 1],
[5, 1]])

Generalizing matrix transpose in numpy

Let a be a list in python.
a = [1,2,3]
When matrix transpose is applied to a, we get:
np.matrix(a).transpose()
matrix([[1],
[2],
[3]])
I am looking to generalize this functionality and will next illustrate what I am looking to do with the help of an example. Let b be another list.
b = [[1, 2], [2, 3], [3, 4]]
In a, the list items are 1, 2, and 3. I would like to consider each of [1,2], [2,3], and [3,4] as list items in b, only for the purpose of performing a transpose. I would like the output to be as follows:
array([[[1,2]],
[[2,3]],
[[3,4]]])
In general, I would like to be able to specify what a list item would look like, and perform a matrix transpose based on that.
I could just write a few lines of code to do the above, but my purpose of asking this question is to find out if there is an inbuilt numpy functionality or a pythonic way, to do this.
EDIT: unutbu's output below matches the output that I have above. However, I wanted a solution that would work for a more general case. I have posted another input/output below. My initial example wasn't descriptive enough to convey what I wanted to say. Let items in b be [1,2], [2,3], [3,4], and [5,6]. Then the output given below would be of doing a matrix transpose on higher dimension elements. More generally, once I describe what an 'item' would look like, I would like to know if there is a way to do something like a transpose.
Input: b = [[[1, 2], [2, 3]], [[3, 4], [5,6]]]
Output: array([[[1,2], [3,4]],
[[2,3], [5,6]]])
Your desired array has shape (3,1,2). b has shape (3,2). To stick an extra axis in the middle, use b[:,None,:], or (equivalently) b[:, np.newaxis, :]. Look for "newaxis" in the section on Basic Slicing.
In [178]: b = np.array([[1, 2], [2, 3], [3, 4]])
In [179]: b
Out[179]:
array([[1, 2],
[2, 3],
[3, 4]])
In [202]: b[:,None,:]
Out[202]:
array([[[1, 2]],
[[2, 3]],
[[3, 4]]])
Another userful tool is np.swapaxes:
In [222]: b = np.array([[[1, 2], [2, 3]], [[3, 4], [5,6]]])
In [223]: b.swapaxes(0,1)
Out[223]:
array([[[1, 2],
[3, 4]],
[[2, 3],
[5, 6]]])
The transpose, b.T is the same as swapping the first and last axes, b.swapaxes(0,-1):
In [226]: b.T
Out[226]:
array([[[1, 3],
[2, 5]],
[[2, 4],
[3, 6]]])
In [227]: b.swapaxes(0,-1)
Out[227]:
array([[[1, 3],
[2, 5]],
[[2, 4],
[3, 6]]])
Summary:
Use np.newaxis (or None) to add new axes. (Thus, increasing the dimension of the array)
Use np.swapaxes to swap any two axes.
Use np.transpose to permute all the axes at once. (Thanks to #jorgeca for pointing this out.)
Use np.rollaxis to "rotate" the axes.

Categories