If I understand it correctly, you have a list of coordinates like this:
coords = [[y0, x0], [y1, x1], ...]
To get the values of array a at these coordinates you need:
a[[y0, y1, ...], [x0, x1, ...]]
So a[coords] will not work. One way to do it is:
Y = [c[0] for c in coords]
X = [c[1] for c in coords]
or
Y = np.transpose(coords)[0]
X = np.transpose(coords)[1]
Then
a[Y, X]
Answer from kuzand on Stack OverflowIf I understand it correctly, you have a list of coordinates like this:
coords = [[y0, x0], [y1, x1], ...]
To get the values of array a at these coordinates you need:
a[[y0, y1, ...], [x0, x1, ...]]
So a[coords] will not work. One way to do it is:
Y = [c[0] for c in coords]
X = [c[1] for c in coords]
or
Y = np.transpose(coords)[0]
X = np.transpose(coords)[1]
Then
a[Y, X]
Does fancy indexing do what you want? np.take seems to flatten the array before operating.
import numpy as np
a = np.arange(1, 10).reshape(3,3)
a
# array([[1, 2, 3],
# [4, 5, 6],
# [7, 8, 9]])
rows = [ 1,1,2,0]
cols = [ 0,1,1,2]
# Use the indices to access items in a
a[rows, cols]
# array([4, 5, 8, 3])
a[1,0], a[1,1], a[2,1], a[0,2]
# (4, 5, 8, 3)
We can compute the linear indices and then use np.take -
np.take(lut, np.ravel_multi_index(arr.T, lut.shape)).T
If you are open to alternatives, we can reshape the indices array to 2D, convert to tuples, index into the data array with it, to give us 1D, which could be reshaped back to 2D -
lut[tuple(arr.reshape(-1,arr.shape[-1]).T)].reshape(arr.shape[:2])
Sample run -
In [49]: lut = np.random.randint(11,99,(13,13,13))
In [50]: arr = np.arange(12).reshape([2,2,3])
In [51]: lut[ arr[:,:,0],arr[:,:,1],arr[:,:,2] ] # Original approach
Out[51]:
array([[41, 21],
[94, 22]])
In [52]: np.take(lut, np.ravel_multi_index(arr.T, lut.shape)).T
Out[52]:
array([[41, 21],
[94, 22]])
In [53]: lut[tuple(arr.reshape(-1,arr.shape[-1]).T)].reshape(arr.shape[:2])
Out[53]:
array([[41, 21],
[94, 22]])
We can avoid the double transposing for the np.take approach, like so -
In [55]: np.take(lut, np.ravel_multi_index(arr.transpose(2,0,1), lut.shape))
Out[55]:
array([[41, 21],
[94, 22]])
Generalize to multi-dimensional arrays of generic dimensions
This could be generalized to ndarrays of generic no. of dims, like so -
np.take(lut, np.ravel_multi_index(np.rollaxis(arr,-1,0), lut.shape))
The tuple-based approach should work without any change.
Here's a sample run for the same -
In [95]: lut = np.random.randint(11,99,(13,13,13,13))
In [96]: arr = np.random.randint(0,13,(2,3,4,4))
In [97]: lut[ arr[:,:,:,0] , arr[:,:,:,1],arr[:,:,:,2],arr[:,:,:,3] ]
Out[97]:
array([[[95, 11, 40, 75],
[38, 82, 11, 38],
[30, 53, 69, 21]],
[[61, 74, 33, 94],
[90, 35, 89, 72],
[52, 64, 85, 22]]])
In [98]: np.take(lut, np.ravel_multi_index(np.rollaxis(arr,-1,0), lut.shape))
Out[98]:
array([[[95, 11, 40, 75],
[38, 82, 11, 38],
[30, 53, 69, 21]],
[[61, 74, 33, 94],
[90, 35, 89, 72],
[52, 64, 85, 22]]])
I did not try in 3-dimensions. But in 2-dimensions I get the result I want with using numpy.take :
np.take(np.take(T,ix,axis=0), iy,axis=1 )
Perhaps you can expand that to 3-dimensions.
As an example I can adress with two 1-dim arrays for the indices ix and iy the 2-dimensional stencil for the discrete Laplace equation,
ΔT = T[ix-1,iy] + T[ix+1, iy] + T[ix,iy-1] + T[ix,iy+1] - 4*T[ix,iy]
Introducing for a leaner writing:
def q(Φ,kx,ky):
return np.take(np.take(Φ,kx,axis=0), ky,axis=1 )
then I can run the following python code with numpy.take:
nx = 6; ny= 10
T = np.arange(nx*ny).reshape(nx, ny)
ix = np.linspace(1,nx-2,nx-2,dtype=int)
iy = np.linspace(1,ny-2,ny-2,dtype=int)
ΔT = q(T,ix-1,iy) + q(T,ix+1,iy) + q(T,ix,iy-1) + q(T,ix,iy+1) - 4.0 * q(T,ix,iy)
Work it from here
[array_a[i][wanted_values[i]] for i in range(len(wanted_values))]
#output
[array([6.2, 8. ]), array([5.1, 7.1, 8.2])]
You are having a problem with the size of the rows in your array. Consider the following example
>>> a = np.array([[1],[1,2]])
<stdin>:1: VisibleDeprecationWarning: Creating an ndarray from ragged nested
sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different
lengths or shapes) is deprecated. If you meant to do this, you must specify
'dtype=object' when creating the ndarray
Moreover, numpy function take does not work as you expect. That is, given numpy admitted ragged nested sequences what the function does is:
- It flattens the array ,
- it takes elements from that flattened array indexed by the numbers inside the second argument of the function in your case
wanted_valuesand places them in the same manner they are placed inwanted_valuesthat is your code would givenp.array([6.2, 8.0],[2.0, 5.5, 8.0], ...])that is different from what you are expecting.
What I suggest you to do is store every selection in a list choices that you build iterating both iterables at the same time:
array_a = np.array([[6.2, 2.0, 5.5, 8.0], [6.0, 5.1, 7.1, 8.2]])
wanted_values = [[0,3], [1,2,3]]
choice = []
for xs, indices in zip(array_a, wanted_values):
choice.append(xs[indices])
The corollary of numpy.take for setting elements is numpy.put, but unfortunately np.put does not take an axis argument. numpy.put_along_axis exists, but this has the indexing semantics of np.take_along_axis, which is different than what you asked.
I suspect the easiest way to achieve what you have in mind is to use np.take to generate indices that can then be passed to np.put. For example:
>>> a=np.array([[1,2],[3,4]])
>>> i = np.take(np.arange(a.size).reshape(a.shape), 0, axis=0)
>>> np.put(a, i, 10)
>>> print(a)
[[10 10]
[ 3 4]]
Another possibility would be to combine numpy.apply_along_axis with np.put. For example:
>>> a = np.array([[1,2],[3,4]])
>>> np.apply_along_axis(np.put, arr=a, axis=0, ind=0, v=10)
>>> print(a)
[[10 10]
[ 3 4]]
Though please be aware that apply_along_axis is implemented via loops rather than vectorized operations, so it may have poor performance for larger arrays.
put_along_axis was mentioned. Looking at its [source], the key step is
arr[_make_along_axis_idx(arr_shape, indices, axis)] = values
_make_along_axis_index is more involved ,but a key comment is
# build a fancy index, consisting of orthogonal aranges, with the
# requested index inserted at the right location
Those are similar to the broadcastable indexing arrays produced by np.ix_. You can also make an indexing tuple like (0, slice(None)).
Yes, since numpy vectorizes operations, you can just do:
y[:,0] = np.sin(np.pi * x / L)
Note that y[:,0] grabs the first column of y (the : in the first coordinate essentially means "grab all rows", and the 0 in the second coordinate means "from the column at index 0" (ie the first column)). Since np.sin(np.pi * x / L) is also an array, you can assign the latter to the former directly.
This question is rather for codereview@stackexchange, but this snippet works!
import numpy as np
N = 1000 # arbitrary
T1 = 1000 # arbitrary
L = 10 # arbitrary
x = np.linspace(0,L,num = N)
# you don't need reshape here, give the size as a tuple!
y = np.zeros((N,T1))
# use a vectorized call here:
y[:,0] = np.sin(np.pi*x/L)