Вот решение, которое работает, следуя циклам в массиве индекса.При желании его можно скомпилировать, используя pythran, что дает значительное ускорение, если строки маленькие (80x для 10 элементов), и небольшое ускорение, если строки большие (30% для 1000 элементов).
Чтобы обеспечить его совместимость с pythran, у меня былонемного упростить его, поэтому он принимает только двумерные массивы и сортирует только по оси 0.
Код:
import numpy as np
#pythran export take_inplace(float[:, :] or int[:, :], int[:])
def take_inplace(a, idx):
n, m = a.shape
been_there = np.zeros(n, bool)
keep = np.empty(m, a.dtype)
for i in range(n):
if been_there[i]:
continue
keep[:] = a[i]
been_there[i] = True
j = i
k = idx[i]
while not been_there[k]:
a[j] = a[k]
been_there[k] = True
j = k
k = idx[k]
a[j] = keep
Пример запуска с использованием скомпилированной версии.Как указано выше, компиляция требуется только для небольших строк, для больших строк чистый python должен быть достаточно быстрым.
>>> from timeit import timeit
>>> import numpy as np
>>> import take_inplace
>>>
>>> a = np.random.random((1000, 10))
>>> idx = a[:, 4].argsort()
>>>
>>> take_inplace.take_inplace(a, idx)
>>>
# correct
>>> np.all(np.arange(1000) == a[:, 4].argsort())
True
>>>
# speed
>>> timeit(lambda: take_inplace.take_inplace(a, idx), number=1000)
0.011950935004279017
>>>
# for comparison
>>> timeit(lambda: a[idx], number=1000)
0.02985276997787878