This question is based on this older question:
Given an array:
In [122]: arr = np.array([[1, 3, 7], [4, 9, 8]]); arr Out[122]: array([[1, 3, 7], [4, 9, 8]])
And given its indices:
In [127]: np.indices(arr.shape) Out[127]: array([[[0, 0, 0], [1, 1, 1]], [[0, 1, 2], [0, 1, 2]]])
How would I be able to stack them neatly one against the other to form a new 2D array? This is what I'd like:
array([[0, 0, 1], [0, 1, 3], [0, 2, 7], [1, 0, 4], [1, 1, 9], [1, 2, 8]])
This solution by Divakar is what I currently use for 2D arrays:
def indices_merged_arr(arr):
m,n = arr.shape
I,J = np.ogrid[:m,:n]
out = np.empty((m,n,3), dtype=arr.dtype)
out[...,0] = I
out[...,1] = J
out[...,2] = arr
out.shape = (-1,3)
return out
Now, if I wanted to pass a 3D array, I need to modify this function:
def indices_merged_arr(arr):
m,n,k = arr.shape # here
I,J,K = np.ogrid[:m,:n,:k] # here
out = np.empty((m,n,k,4), dtype=arr.dtype) # here
out[...,0] = I
out[...,1] = J
out[...,2] = K # here
out[...,3] = arr
out.shape = (-1,4) # here
return out
But this function now works for 3D arrays only - I can't pass a 2D array to it.
Is there some sort of way I can generalise this to work for any dimension? Here's my attempt:
def indices_merged_arr_general(arr):
tup = arr.shape
idx = np.ogrid[????] # not sure what to do here....
out = np.empty(tup + (len(tup) + 1, ), dtype=arr.dtype)
for i, j in enumerate(idx):
out[...,i] = j
out[...,len(tup) - 1] = arr
out.shape = (-1, len(tup)
return out
I'm having trouble with this line:
idx = np.ogrid[????]
How can I get this working?
Slicing arrays Slicing in python means taking elements from one given index to another given index. We pass slice instead of index like this: [start:end] . We can also define the step, like this: [start:end:step] .
Slice Two-dimensional Numpy Arrays To slice elements from two-dimensional arrays, you need to specify both a row index and a column index as [row_index, column_index] . For example, you can use the index [1,2] to query the element at the second row, third column in precip_2002_2013 .
Python supports the slicing of arrays. It is the creation of a new sub-array from the given array on the basis of the user-defined starting and ending indices. We can slice arrays by either of the following ways. Array slicing can be easily done following the Python slicing method.
Numpy with Python Three types of indexing methods are available − field access, basic slicing and advanced indexing. Basic slicing is an extension of Python's basic concept of slicing to n dimensions. A Python slice object is constructed by giving start, stop, and step parameters to the built-in slice function.
Here's the extension to handle generic ndarrays -
def indices_merged_arr_generic(arr, arr_pos="last"):
n = arr.ndim
grid = np.ogrid[tuple(map(slice, arr.shape))]
out = np.empty(arr.shape + (n+1,), dtype=np.result_type(arr.dtype, int))
if arr_pos=="first":
offset = 1
elif arr_pos=="last":
offset = 0
else:
raise Exception("Invalid arr_pos")
for i in range(n):
out[...,i+offset] = grid[i]
out[...,-1+offset] = arr
out.shape = (-1,n+1)
return out
Sample runs
2D case :
In [252]: arr
Out[252]:
array([[37, 32, 73],
[95, 80, 97]])
In [253]: indices_merged_arr_generic(arr)
Out[253]:
array([[ 0, 0, 37],
[ 0, 1, 32],
[ 0, 2, 73],
[ 1, 0, 95],
[ 1, 1, 80],
[ 1, 2, 97]])
In [254]: indices_merged_arr_generic(arr, arr_pos='first')
Out[254]:
array([[37, 0, 0],
[32, 0, 1],
[73, 0, 2],
[95, 1, 0],
[80, 1, 1],
[97, 1, 2]])
3D case :
In [226]: arr
Out[226]:
array([[[35, 45, 33],
[48, 38, 20],
[69, 31, 90]],
[[73, 65, 73],
[27, 51, 45],
[89, 50, 74]]])
In [227]: indices_merged_arr_generic(arr)
Out[227]:
array([[ 0, 0, 0, 35],
[ 0, 0, 1, 45],
[ 0, 0, 2, 33],
[ 0, 1, 0, 48],
[ 0, 1, 1, 38],
[ 0, 1, 2, 20],
[ 0, 2, 0, 69],
[ 0, 2, 1, 31],
[ 0, 2, 2, 90],
[ 1, 0, 0, 73],
[ 1, 0, 1, 65],
[ 1, 0, 2, 73],
[ 1, 1, 0, 27],
[ 1, 1, 1, 51],
[ 1, 1, 2, 45],
[ 1, 2, 0, 89],
[ 1, 2, 1, 50],
[ 1, 2, 2, 74]])
For large arrays, AFAIK, senderle's cartesian_product is the fastest way1 to generate cartesian products using NumPy :
In [372]: A = np.random.random((100,100,100))
In [373]: %timeit indices_merged_arr_generic_using_cp(A)
100 loops, best of 3: 16.8 ms per loop
In [374]: %timeit indices_merged_arr_generic(A)
10 loops, best of 3: 28.9 ms per loop
Here is the setup I used to benchmark.
Below, indices_merged_arr_generic_using_cp
is a modification of senderle's cartesian_product
to include the flattened array beside with the cartesian product:
import numpy as np
import functools
def indices_merged_arr_generic_using_cp(arr):
"""
Based on cartesian_product
http://stackoverflow.com/a/11146645/190597 (senderle)
"""
shape = arr.shape
arrays = [np.arange(s, dtype='int') for s in shape]
broadcastable = np.ix_(*arrays)
broadcasted = np.broadcast_arrays(*broadcastable)
rows, cols = functools.reduce(np.multiply, broadcasted[0].shape), len(broadcasted)+1
out = np.empty(rows * cols, dtype=arr.dtype)
start, end = 0, rows
for a in broadcasted:
out[start:end] = a.reshape(-1)
start, end = end, end + rows
out[start:] = arr.flatten()
return out.reshape(cols, rows).T
def indices_merged_arr_generic(arr):
"""
https://stackoverflow.com/a/46135084/190597 (Divakar)
"""
n = arr.ndim
grid = np.ogrid[tuple(map(slice, arr.shape))]
out = np.empty(arr.shape + (n+1,), dtype=arr.dtype)
for i in range(n):
out[...,i] = grid[i]
out[...,-1] = arr
out.shape = (-1,n+1)
return out
1Note that above I actually used senderle's cartesian_product_transpose
. For me, this is
the fastest version. For others, including senderle, cartesian_product
is
faster.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With