Сортировка массивов в NumPy по столбцам

336

Как я могу отсортировать массив в NumPy по n-му столбцу?

Например,

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

Я хотел бы отсортировать строки по второму столбцу, чтобы я вернулся:

array([[7, 0, 5],
       [9, 2, 3],
       [4, 5, 6]])
Пол Винц
источник
8
Это действительно плохой пример, так как np.sort(a, axis=0)это было бы удовлетворительным решением для данной матрицы. Я предложил редактирование с лучшим примером, но был отклонен, хотя на самом деле вопрос был бы гораздо более понятным. Пример должен быть примерно таким a = numpy.array([[1, 2, 3], [6, 5, 2], [3, 1, 1]])с желаемым результатомarray([[3, 1, 1], [1, 2, 3], [6, 5, 2]])
David
29
Дэвид, ты не понял сути вопроса. Он хочет сохранить порядок в каждом ряду одинаковым.
marcorossi
@marcorossi Я понял, но пример был очень плохо сформулирован, потому что, как я уже сказал, было несколько возможных ответов (которые, однако, не удовлетворили бы запрос ОП). Более позднее редактирование, основанное на моем комментарии, действительно было одобрено (забавно, что мое было отклонено, хотя). Так что теперь все в порядке.
Дэвид

Ответы:

141

@steve «s ответ на самом деле самый элегантный способ сделать это.

Для «правильного» способа смотрите аргумент порядка ключевых слов numpy.ndarray.sort

Тем не менее, вам нужно будет просматривать ваш массив как массив с полями (структурированный массив).

«Правильный» способ довольно уродлив, если вы изначально не определяли свой массив с полями ...

В качестве быстрого примера, чтобы отсортировать его и вернуть копию:

In [1]: import numpy as np

In [2]: a = np.array([[1,2,3],[4,5,6],[0,0,1]])

In [3]: np.sort(a.view('i8,i8,i8'), order=['f1'], axis=0).view(np.int)
Out[3]: 
array([[0, 0, 1],
       [1, 2, 3],
       [4, 5, 6]])

Чтобы отсортировать на месте:

In [6]: a.view('i8,i8,i8').sort(order=['f1'], axis=0) #<-- returns None

In [7]: a
Out[7]: 
array([[0, 0, 1],
       [1, 2, 3],
       [4, 5, 6]])

@ Насколько мне известно, Стив - самый элегантный способ сделать это ...

Единственное преимущество этого метода заключается в том, что аргумент «порядок» - это список полей, по которым нужно упорядочить поиск. Например, вы можете отсортировать по второму столбцу, затем по третьему столбцу, затем по первому столбцу, указав order = ['f1', 'f2', 'f0'].

Джо Кингтон
источник
3
В моем numpy 1.6.1rc1 он поднимаетсяValueError: new type not compatible with array.
Clippit
9
Имеет ли смысл подавать запрос на добавление функции, чтобы «правильный» путь был менее уродливым?
эндолит
4
Что если значения в массиве float? Должен ли я что-то изменить?
Марко
1
И для гибридного типа, a = np.array([['a',1,2,3],['b',4,5,6],['c',0,0,1]])какой подход я должен следовать?
ePascoal
10
Одним из главных преимуществ этого метода перед Стивом является то, что он позволяет сортировать очень большие массивы на месте. Для достаточно большого массива индексы, возвращаемые самим np.argsortсобой, могут занимать довольно много памяти, и, в дополнение к этому, индексирование с помощью массива также будет генерировать копию сортируемого массива.
ali_m
738

Я полагаю, это работает: a[a[:,1].argsort()]

Это указывает на второй столбец aи сортирует его по нему соответственно.

Стив Тджоа
источник
2
Это не ясно, что 1здесь? индекс для сортировки?
orezvani
29
[:,1]указывает на второй столбец a.
Стив Тджоа
60
Если вы хотите обратную сортировку, измените это такa[a[:,1].argsort()[::-1]]
Стивен С. Хауэлл,
1
Выглядит просто и работает! Это быстрее чем np.sortили нет?
Вацлав Павлик
14
Мне легче читать:ind = np.argsort( a[:,1] ); a = a[ind]
Поппи
32

Вы можете сортировать по нескольким столбцам согласно методу Стива Тджоа, используя стабильную сортировку, такую ​​как сортировка слиянием, и сортируя индексы от наименее значимых к наиболее значимым столбцам:

a = a[a[:,2].argsort()] # First sort doesn't need to be stable.
a = a[a[:,1].argsort(kind='mergesort')]
a = a[a[:,0].argsort(kind='mergesort')]

Это сортирует по столбцу 0, затем 1, затем 2.

JJ
источник
4
Почему First Sort не должен быть стабильным?
Маленькие столики Бобби
10
Хороший вопрос - стабильный означает, что при наличии связи вы сохраняете исходный порядок, а исходный порядок несортированного файла не имеет значения.
JJ
Это кажется очень важным моментом. иметь список, который молча не сортирует, было бы плохо.
Неуклюжий кот
19

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

import numpy as np
table = np.random.rand(5000, 10)

%timeit table.view('f8,f8,f8,f8,f8,f8,f8,f8,f8,f8').sort(order=['f9'], axis=0)
1000 loops, best of 3: 1.88 ms per loop

%timeit table[table[:,9].argsort()]
10000 loops, best of 3: 180 µs per loop

import pandas as pd
df = pd.DataFrame(table)
%timeit df.sort_values(9, ascending=True)
1000 loops, best of 3: 400 µs per loop

Итак, похоже, что индексация с помощью argsort является самым быстрым методом на сегодняшний день ...

prl900
источник
19

Из документации вики Python , я думаю , что вы можете сделать:

a = ([[1, 2, 3], [4, 5, 6], [0, 0, 1]]); 
a = sorted(a, key=lambda a_entry: a_entry[1]) 
print a

Выход:

[[[0, 0, 1], [1, 2, 3], [4, 5, 6]]]
user541064
источник
21
При таком решении вместо массива NumPy получается список, поэтому это не всегда удобно (занимает больше памяти, возможно медленнее и т. Д.).
Эрик О Лебигот
это «решение» медленнее из-за ответа, получившего наибольшее количество голосов, в факторе… ну, на самом деле, оно близко к бесконечности
Дживан
16

Из списка рассылки NumPy есть еще одно решение:

>>> a
array([[1, 2],
       [0, 0],
       [1, 0],
       [0, 2],
       [2, 1],
       [1, 0],
       [1, 0],
       [0, 0],
       [1, 0],
      [2, 2]])
>>> a[np.lexsort(np.fliplr(a).T)]
array([[0, 0],
       [0, 0],
       [0, 2],
       [1, 0],
       [1, 0],
       [1, 0],
       [1, 0],
       [1, 2],
       [2, 1],
       [2, 2]])
fgregg
источник
3
Правильное обобщение есть a[np.lexsort(a.T[cols])]. где cols=[1]в оригинальном вопросе.
Радиоуправление
5

У меня была похожая проблема.

Моя проблема:

Я хочу рассчитать SVD и нужно отсортировать мои собственные значения в порядке убывания. Но я хочу сохранить соответствие между собственными значениями и собственными векторами. Мои собственные значения были в первом ряду, а соответствующий собственный вектор - в том же столбце.

Поэтому я хочу отсортировать двумерный массив по столбцам по первой строке в порядке убывания.

Мое решение

a = a[::, a[0,].argsort()[::-1]]

Так, как это работает?

a[0,] это только первый ряд, по которому я хочу отсортировать.

Теперь я использую argsort для получения порядка индексов.

Я использую, [::-1]потому что мне нужно в порядке убывания.

Наконец, я использую, a[::, ...]чтобы получить представление со столбцами в правильном порядке.

xuma202
источник
1

Немного более сложный lexsortпример - спуск по 1-му столбцу, второй по возрастанию по 2-му. Хитрости в том lexsort, что он сортирует по строкам (отсюда и .T) и отдает приоритет последним.

In [120]: b=np.array([[1,2,1],[3,1,2],[1,1,3],[2,3,4],[3,2,5],[2,1,6]])
In [121]: b
Out[121]: 
array([[1, 2, 1],
       [3, 1, 2],
       [1, 1, 3],
       [2, 3, 4],
       [3, 2, 5],
       [2, 1, 6]])
In [122]: b[np.lexsort(([1,-1]*b[:,[1,0]]).T)]
Out[122]: 
array([[3, 1, 2],
       [3, 2, 5],
       [2, 1, 6],
       [2, 3, 4],
       [1, 1, 3],
       [1, 2, 1]])
hpaulj
источник
0

Вот еще одно решение, учитывающее все столбцы (более компактный способ ответа JJ );

ar=np.array([[0, 0, 0, 1],
             [1, 0, 1, 0],
             [0, 1, 0, 0],
             [1, 0, 0, 1],
             [0, 0, 1, 0],
             [1, 1, 0, 0]])

Сортировка с помощью lexsort,

ar[np.lexsort(([ar[:, i] for i in range(ar.shape[1]-1, -1, -1)]))]

Вывод:

array([[0, 0, 0, 1],
       [0, 0, 1, 0],
       [0, 1, 0, 0],
       [1, 0, 0, 1],
       [1, 0, 1, 0],
       [1, 1, 0, 0]])
Сефа
источник
0

Просто используя сортировку, используйте номер столбца, по которому вы хотите отсортировать.

a = np.array([1,1], [1,-1], [-1,1], [-1,-1]])
print (a)
a=a.tolist() 
a = np.array(sorted(a, key=lambda a_entry: a_entry[0]))
print (a)
Jerin
источник
0

Это старый вопрос, но если вам нужно обобщить это для массивов с размерами выше 2, вот решение, которое можно легко обобщить:

np.einsum('ij->ij', a[a[:,1].argsort(),:])

Это избыточное решение для двух измерений, и a[a[:,1].argsort()]этого будет достаточно для ответа @ steve, однако этот ответ нельзя обобщить для более высоких измерений. Вы можете найти пример трехмерного массива в этом вопросе.

Вывод:

[[7 0 5]
 [9 2 3]
 [4 5 6]]
Ehsan
источник