Index a 2D Numpy Array with 2 Lists of Indices

Index a 2D Numpy array with 2 lists of indices

Your first try would work if you write it with np.newaxis

x_new = x[row_indices[:, np.newaxis],column_indices]

Update 2D numpy array using pairs of indices without iteration

Simply use:

import numpy as np

indexarray = np.array([[0, 2, 4, 6],
[1, 3, 5, 7]])
values = [1, 2, 3, 4]

rows, cols = indexarray[0], indexarray[1]

zeros = np.zeros((10, 9))
zeros[rows, cols] = values

print(zeros)

Output

[[0. 1. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 2. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 3. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 4. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]]

An alternative that will add together repeating coordinates, is to use add.at:

np.add.at(zeros, (rows, cols), values)

A second alternative is to use a sparse matrix constructor, for example:

from scipy.sparse import csr_matrix
rows, cols = indexarray[0], indexarray[1]
zeros = csr_matrix((values, (rows, cols)), shape=(10, 9)).toarray()

Output

[[0 1 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0]
[0 0 0 2 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 3 0 0 0]
[0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 4 0]
[0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0]]

Index 2D numpy array by a 2D array of indices without loops

When using arrays of indices to index another array, the shape of each index array should match the shape of the output array. You want the column indices to match inds, and you want the row indices to match the row of the output, something like:

array([[0, 0],
[1, 1],
[2, 2]])

You can just use a single column of the above, due to broadcasting, so you can use np.arange(3)[:,None] is the vertical arange because None inserts a new axis:

>>> np.arange(3)[:, None]
array([[0],
[1],
[2]])

Finally, together:

>>> a[np.arange(3)[:,None], inds]
array([[0, 3], # a[0,[0,1]]
[6, 0], # a[1,[1,2]]
[0, 9]]) # a[2,[0,2]]

How to pass list of indices along a dimension of a 2D array in numpy?

You should use zip to iterate over two arrays simultaneously.

data = [
[1,2,3,4,5],
[2,4,6,8,10],
[3,6,9,12,15]
]

indexes = [0,1,2]

for (arr, i) in zip(data, indexes):
print(arr[i])

# Or more pythonic way
print([arr[i] for (arr,i) in zip(data, indexes)]) # [1, 4, 9]

Numpy: assigning values to 2d array with list of indices

We can unpack each row of coords as row, col indices for indexing into img and then assign.

Now, since the question is tagged : Python 3.x, on it we can simply unpack with [*coords.T] and then assign -

img[[*coords.T]] = 255

Generically, we can use tuple to unpack -

img[tuple(coords.T)] = 255

We can also compute the linear indices and then assign with np.put -

np.put(img, np.ravel_multi_index(coords.T, img.shape), 255)

Numpy: access values of multidimensional array based on list of indices

Here,

>>> desired_cols = np.array([0, 2])
>>> desired_rows = np.arange(len(desired_cols))
>>> x[desired_rows, desired_cols]
array([1, 1])

Your np.array([0, 2]) doesn't provide enough information to index into a multi-dimensional array. Here, I'm assuming based on your example that those are the columns which you want to select. Since you also need to supply the corresponding rows you want to select, I've created an arange based on the length of the desired columns.

Selecting single elements

In general, advanced indexing requires lists of indices for each axis:

x[[axis_0_idxs], [axis_1_idxs], ...]

Where if you were to zip(axis_0_idxs, axis_1_idxs, ...), you'd produce coordinate tuples. For example, with the indices used for your problem:

>>> list(zip(desired_rows, desired_columns))
[(0, 0), (1, 2)]

Selecting subarrays

If, however, you want to select ALL the values from the desired rows together with ALL the values from the desired columns, you can use np.ix_(). Here's a more complex example:

>>> x = np.random.randint(0, 9, (5, 5), dtype="uint8")
>>> x
array([[87, 57, 64, 48, 15],
[72, 8, 0, 81, 63],
[63, 51, 66, 0, 68],
[77, 46, 74, 74, 86],
[51, 59, 48, 81, 75]], dtype=uint8)

Suppose we want the subarray corresponding to rows 1, 2, and 3, and columns 0, 2, and 4. By using basic lists to index into x, we instead get an array of three items:

>>> rows = [1, 2, 3]
>>> cols = [0, 2, 4]
>>> x[rows, cols]
array([72, 66, 86], dtype=uint8)

This is because we're using 1D lists which, again, are essentially zipped into coordinate tuples. If we want to select the subarray made up of rows 1, 2, 3 and columns 0, 2, 4, we need to select all the columns for each of the rows. This, in a sense, is the cartesian product of the rows and columns, but because the cartesian product would still only produce a 1D sequence of coordinate tuples, we'd still only get a 1D output, even if we get the correct values.

But by using np.ix_(), we get a grid of coordinates represented in a very compact way:

>>> np.ix_(rows, cols)
(array([[1],
[2],
[3]]),
array([[0, 2, 4]]))

Using this to index gets us the 3x3 subarray we wanted:

>>> x[np.ix_(rows, cols)]
array([[72, 0, 63],
[63, 66, 68],
[77, 74, 86]], dtype=uint8)

Here's some pure Python to demonstrate how indexing with an np.ix_ object behaves:

>>> all_rows = [[r]*len(cols) for r in rows]

>>> all_cols = [cols]*len(rows)

>>> all_rows
[[1, 1, 1], [2, 2, 2], [3, 3, 3]]

>>> all_cols
[[0, 2, 4], [0, 2, 4], [0, 2, 4]]

>>> x[all_rows, all_cols]
array([[72, 0, 63],
[63, 66, 68],
[77, 74, 86]], dtype=uint8)

Notice that all_rows and all_cols are 2D lists. Notice also that this is much more tedious and prone to error (multiplying columns by number of rows, rows by number of columns, which one to repeat element-wise, which one to repeat sublist-wise, etc.).

Another nice benefit of using np.ix_() is that we can select non-square subarrays very easily without needing to worry about the headache behind the pure Python approach:

>>> x[np.ix_([1, 2], [0, 1, 3, 4])]
array([[72, 8, 81, 63],
[63, 51, 0, 68]], dtype=uint8)

How to create a list of indices from a 2D list?

With list comprehension:

>>> [(r, c) for r, line in enumerate(two_d_list) for c, num in enumerate(line) if num==1]

[(0, 1), (1, 0), (1, 1), (2, 3)]

Indexing with lists and arrays in numpy appears inconsistent

The reason is the interpretation of lists as index for numpy arrays: Lists are interpreted like tuples and indexing with a tuple is interpreted by NumPy as multidimensional indexing.

Just like arr[1, 2] returns the element arr[1][2] the arr[[[4, 3], [2, 1]]] is identical to arr[[4, 3], [2, 1]] and will, according to the rules of multidimensional indexing return the elements arr[4, 2] and arr[3, 1].

By adding one more list you do tell NumPy that you want slicing along the first dimension, because the outermost list is effectively interpreted as if you only passed in one "list of indices for the first dimension": arr[[[[4, 3], [2, 1]]]].

From the documentation:

Example

From each row, a specific element should be selected. The row index is just [0, 1, 2] and the column index specifies the element to choose for the corresponding row, here [0, 1, 0]. Using both together the task can be solved using advanced indexing:

>>> x = np.array([[1, 2], [3, 4], [5, 6]])
>>> x[[0, 1, 2], [0, 1, 0]]
array([1, 4, 5])

and:

Warning

The definition of advanced indexing means that x[(1,2,3),] is fundamentally different than x[(1,2,3)]. The latter is equivalent to x[1,2,3] which will trigger basic selection while the former will trigger advanced indexing. Be sure to understand why this occurs.

In such cases it's probably better to use np.take:

>>> y.take([[4, 3], [2, 1]])  # 2D array
array([[4, 3],
[2, 1]])

This function [np.take] does the same thing as “fancy” indexing (indexing arrays using arrays); however, it can be easier to use if you need elements along a given axis.

Or convert the indices to an array. That way NumPy interprets it (array is special cased!) as fancy indexing instead of as "multidimensional indexing":

>>> y[np.asarray([[4, 3], [2, 1]])]
array([[4, 3],
[2, 1]])


Related Topics



Leave a reply



Submit