Сортування масивів у 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]])
Девід

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
У моїй нуме 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

737

Я думаю, це працює: a[a[:,1].argsort()]

Це вказує на другий стовпчик aі відповідно відпорядкуйте його.


2
Це не ясно, що 1тут? індекс, відсортований за?
орезвані

29
[:,1]позначає другий стовпчик a.
Стів Тьоа

60
Якщо ви хочете зворотного сортування, змініть це такa[a[:,1].argsort()[::-1]]
Steven C. Howell

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.


4
Чому перший сорт не повинен бути стабільним?
Столи маленького

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

Таким чином, схоже, що індексація аргсортом - це найшвидший метод дотепер ...


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

21
За допомогою цього рішення, отримує список замість масиву NumPy, тому це може бути не завжди зручно (займає більше пам'яті, можливо, повільніше тощо).
Ерік О Лебігот

це "рішення" повільніше, за найвідповідальнішою відповіддю від коефіцієнта ... ну, майже до нескінченності насправді
Jivan

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

3
Правильне узагальнення a[np.lexsort(a.T[cols])]. де cols=[1]в оригінальному питанні.
Радіокерований

5

У мене була подібна проблема.

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

Я хочу обчислити SVD і мені потрібно сортувати власні значення в порядку зменшення. Але я хочу зберегти відображення власних значень та власних векторів. Мої власні значення були в першому ряду, а відповідний власний вектор під ним у тому ж стовпці.

Тому я хочу сортувати двовимірний стовпчик масиву за першим рядком у порядку зменшення.

Моє рішення

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

То як це працює?

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

Зараз я використовую argsort, щоб отримати порядок індексів.

Я використовую, [::-1]тому що мені потрібен низхідний порядок.

Нарешті, я використовую a[::, ...]для перегляду стовпців у потрібному порядку.


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

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)

0

Це старе питання, але якщо вам потрібно узагальнити це до масивів розмірів, що перевищують два, ось рішення, яке можна легко узагальнити:

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

Це надмірне значення для двох вимірів і a[a[:,1].argsort()]цього буде достатньо за відповідь @ steve, однак цю відповідь неможливо узагальнити до вищих розмірів. Ви можете знайти приклад 3D-масиву в цьому запитанні.

Вихід:

[[7 0 5]
 [9 2 3]
 [4 5 6]]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.