Подтвердить что ты не робот

Сортировка массива 2D numpy несколькими осями

У меня есть двумерная матрица формы (N, 2), которая удерживает N точек (координаты x и y). Например:

array([[3, 2],
       [6, 2],
       [3, 6],
       [3, 4],
       [5, 3]])

Я хотел бы отсортировать его так, чтобы мои точки упорядочивались по координате x, а затем по y в случаях, когда координата x одинакова. Итак, массив выше должен выглядеть так:

array([[3, 2],
       [3, 4],
       [3, 6],
       [5, 3],
       [6, 2]])

Если это был обычный список Python, я бы просто определил, что компаратор выполняет то, что я хочу, но, насколько я могу судить, функция сортировки numpy не принимает пользовательских компараторов. Любые идеи?


EDIT: Спасибо за идеи! Я установил быстрый тестовый пример со 1000000 случайными целыми точками и сравнил те, которые я мог запустить (извините, не может обновить numpy в данный момент).

Mine:   4.078 secs 
mtrw:   7.046 secs
unutbu: 0.453 secs
4b9b3361

Ответ 1

Использование lexsort:

import numpy as np    
a = np.array([(3, 2), (6, 2), (3, 6), (3, 4), (5, 3)])

ind = np.lexsort((a[:,1],a[:,0]))    

a[ind]
# array([[3, 2],
#       [3, 4],
#       [3, 6],
#       [5, 3],
#       [6, 2]])

a.ravel() возвращает представление, если a - C_CONTIGUOUS. Если это так, метод @ars, слегка модифицированный с помощью ravel вместо flatten, дает хороший способ сортировки a на месте:

a = np.array([(3, 2), (6, 2), (3, 6), (3, 4), (5, 3)])
dt = [('col1', a.dtype),('col2', a.dtype)]
assert a.flags['C_CONTIGUOUS']
b = a.ravel().view(dt)
b.sort(order=['col1','col2'])

Так как b является видом a, сортировка b сортирует a:

print(a)
# [[3 2]
#  [3 4]
#  [3 6]
#  [5 3]
#  [6 2]]

Ответ 2

Название гласит: "Сортировка 2D-массивов". Хотя вопросник использует массив (N,2) -shaped, он может обобщать решение unutbu для работы с любым массивом (N,M), как то, что люди действительно могут искать.

Можно было transpose массива и использовать нотацию среза с отрицательным step, чтобы передать все столбцы в lexsort в обратном порядке:

>>> import numpy as np
>>> a = np.random.randint(1, 6, (10, 3))
>>> a
array([[4, 2, 3],
       [4, 2, 5],
       [3, 5, 5],
       [1, 5, 5],
       [3, 2, 1],
       [5, 2, 2],
       [3, 2, 3],
       [4, 3, 4],
       [3, 4, 1],
       [5, 3, 4]])

>>> a[np.lexsort(np.transpose(a)[::-1])]
array([[1, 5, 5],
       [3, 2, 1],
       [3, 2, 3],
       [3, 4, 1],
       [3, 5, 5],
       [4, 2, 3],
       [4, 2, 5],
       [4, 3, 4],
       [5, 2, 2],
       [5, 3, 4]])

Ответ 3

Вы можете использовать np.complex_sort. Это имеет побочный эффект от изменения ваших данных до плавающей запятой, я надеюсь, что это не проблема:

>>> a = np.array([[3, 2], [6, 2], [3, 6], [3, 4], [5, 3]])
>>> atmp = np.sort_complex(a[:,0] + a[:,1]*1j)
>>> b = np.array([[np.real(x), np.imag(x)] for x in atmp])
>>> b
array([[ 3.,  2.],
       [ 3.,  4.],
       [ 3.,  6.],
       [ 5.,  3.],
       [ 6.,  2.]])

Ответ 4

Я боролся с одним и тем же, просто получил помощь и решил проблему. Он работает плавно, если ваш массив имеет имена столбцов (структурированный массив), и я думаю, что это очень простой способ сортировки с использованием той же логики, что и excel:

array_name[array_name[['colname1','colname2']].argsort()]

Обратите внимание на двойные скобки, содержащие критерии сортировки. И, конечно же, вы можете использовать более двух столбцов в качестве критериев сортировки.

Ответ 5

Пакет numpy_indexed (отказ от ответственности: я являюсь его автором) можно использовать для решения таких проблем с обработкой на n-массиве в эффективный полностью векторизованный способ:

import numpy_indexed as npi
npi.sort(a)  # by default along axis=0, but configurable

Ответ 6

ИЗМЕНИТЬ: удаленный плохой ответ.

Здесь один из способов сделать это с использованием промежуточного структурированного массива:

from numpy import array

a = array([[3, 2], [6, 2], [3, 6], [3, 4], [5, 3]])

b = a.flatten()
b.dtype = [('x', '<i4'), ('y', '<i4')]
b.sort()
b.dtype = '<i4'
b.shape = a.shape

print b

который дает желаемый результат:

[[3 2]
 [3 4]
 [3 6]
 [5 3]
 [6 2]]

Не уверен, что это лучший способ сделать это.

Ответ 7

Я нашел один способ сделать это:

from numpy import array
a = array([(3,2),(6,2),(3,6),(3,4),(5,3)])
array(sorted(sorted(a,key=lambda e:e[1]),key=lambda e:e[0]))

Очень сложно сортировать дважды (и использовать обычную функцию python sorted вместо более быстрой сортировки numpy), но она отлично подходит для одной строки.