@JoeKington's numpy solution is very clever, but it relies on A[:,1] being in sorted order. Here is a fix for the general case:
import numpy as np
np.random.seed(1)
N=5
A = np.arange(2*N).reshape((-1,2))+100
np.random.shuffle(A)
print(A)
If A looks like this:
[[104 105]
[102 103]
[108 109]
[100 101]
[106 107]]
and V
V = A[:,1].copy()
np.random.shuffle(V)
print(V)
looks like this:
[105 109 107 101 103]
then we use Joe's solution:
vals, order = np.unique(np.hstack((A[:,1],V)), return_inverse=True)
but save both the order of A[:,1] and V:
a_order = order[:V.size]
v_order = order[-V.size:]
and sort A (by forming A[np.argsort(a_order)]) before reordering with v_order:
print A[np.argsort(a_order)][v_order]
[[104 105]
[108 109]
[106 107]
[100 101]
[102 103]]
(A[np.argsort(a_order)] is A sorted according to its second column.)
Note that np.unique always returns the array in sorted order. The documentation guarantees with return_inverse=True that the returned indices are the indices of the unique array that reconstructs the original array. That is, if you call np.unique like this:
uniq_arr, indices = np.unique(arr, return_inverse=True)
you are guaranteed that
unique_arr[indices] = arr
Because you can rely on this relationship, Joe's method does not depend on a mere implementation detail -- unique will always behave this way. (Famous last words -- considering what happened to the order of output arguments returned by np.unique1d ... but never mind that :))