Сортировка двумерного массива по нескольким осям - PullRequest
28 голосов
/ 25 апреля 2010

У меня есть двумерный массив фигур (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 нене принимать пользовательские компараторы.Есть идеи?


РЕДАКТИРОВАТЬ: Спасибо за идеи!Я настроил быстрый тестовый случай с 1000000 случайных целых точек и сравнил те, которые я мог запустить (извините, в данный момент я не могу обновить numpy).

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

Ответы [ 7 ]

43 голосов
/ 25 апреля 2010

Использование 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]]
14 голосов
/ 25 сентября 2014

В заголовке написано "сортировка 2D массивов". Несмотря на то, что спрашивающий использует массив (N,2) -образный, можно обобщить решение 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]])
4 голосов
/ 21 апреля 2016

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

import numpy_indexed as npi
npi.sort(a)  # by default along axis=0, but configurable
3 голосов
/ 18 декабря 2013

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

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

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

3 голосов
/ 25 апреля 2010

Вы можете использовать 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.]])
2 голосов
/ 25 апреля 2010

РЕДАКТИРОВАТЬ: удален плохой ответ.

Вот один из способов сделать это, используя промежуточный структурированный массив:

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]]

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

1 голос
/ 25 апреля 2010

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

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), но она хорошо умещается в одну строку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...