Получить индексы N крупнейших значений в массиве NumPy
Узнайте эффективные методы поиска индексов N крупнейших значений в массивах NumPy. Сравните argsort и argpartition с примерами кода и советами производительности.
Как получить индексы N самых больших значений в массиве NumPy?
NumPy предоставляет np.argmax() для поиска индекса максимального значения. Как можно изменить этот подход, чтобы получить индексы N самых больших значений?
Например, для массива [1, 3, 2, 4, 5] я хочу функцию, которая вернет индексы трёх самых больших значений, т.е. [4, 3, 1], соответствующие элементам [5, 4, 3].
Для получения индексов N самых больших значений в массиве NumPy можно использовать либо np.argsort() с последующим срезом, либо более эффективную функцию np.argpartition(). Подход с np.argpartition() обычно предпочтительнее с точки зрения производительности, когда вам нужны только индексы топ‑N значений, а не полностью отсортированный массив.
Содержание
- Использование np.argsort()
- Использование np.argpartition()
- Сравнение производительности
- Полная реализация
- Практические примеры
- [Граничные случаи и соображения](#граничные-случаи-и- соображения)
Использование np.argsort()
Функция np.argsort() возвращает индексы, которые бы отсортировали массив по возрастанию. Чтобы получить индексы N самых больших значений, можно:
- Получить индексы всех отсортированных значений с помощью
np.argsort() - Взять последние N индексов из результата
- При необходимости обратить порядок, чтобы получить их от большего к меньшему
import numpy as np
# Пример массива
arr = np.array([1, 3, 2, 4, 5])
n = 3 # Количество самых больших значений
# Получаем индексы, которые бы отсортировали массив
sorted_indices = np.argsort(arr)
# Получаем индексы N самых больших значений (последние N индексов)
largest_indices = sorted_indices[-n:]
# Обратный порядок, чтобы получить от большего к меньшему
largest_indices_sorted = largest_indices[::-1]
print("Индексы N самых больших значений:", largest_indices_sorted)
# Вывод: [4 3 1] для массива [1, 3, 2, 4, 5]
Этот подход работает хорошо, но имеет недостаток: он сортирует весь массив, что имеет сложность O(n log n), хотя вам нужны только топ‑N значения.
Использование np.argpartition()
Более эффективный подход – использовать np.argpartition(), который может найти индексы N самых больших значений за O(n) времени:
import numpy as np
arr = np.array([1, 3, 2, 4, 5])
n = 3
# Получаем индексы, которые бы разделили массив с N самыми большими в конце
partitioned_indices = np.argpartition(arr, -n)
# Получаем последние N индексов (N самых больших)
largest_indices = partitioned_indices[-n:]
# Примечание: эти индексы не отсортированы по значению
print("Индексы N самых больших значений (неотсортированные):", largest_indices)
Если вам нужны индексы, отсортированные по соответствующим значениям, можно отсортировать их:
# Сортируем индексы по их значениям в порядке убывания
largest_indices_sorted = largest_indices[np.argsort(arr[largest_indices])[::-1]]
print("Индексы N самых больших значений (отсортированные):", largest_indices_sorted)
Сравнение производительности
| Метод | Сложность времени | Сложность памяти | Когда использовать |
|---|---|---|---|
np.argsort() |
O(n log n) | O(n) | Когда нужен полностью отсортированный список индексов или N велико по сравнению с размером массива |
np.argpartition() |
O(n) | O(n) | Когда нужны только N самых больших значений и критична производительность |
Ручной цикл с argmax() |
O(n N) | O(1) | Когда N очень мало (2‑3) и предпочтительна простота |
Метод np.argpartition() значительно быстрее для больших массивов, когда N намного меньше размера массива.
Полная реализация
Ниже приведена надёжная функция, реализующая подход np.argpartition():
import numpy as np
def indices_of_n_largest(arr, n):
"""
Возвращает индексы N самых больших значений в массиве NumPy.
Args:
arr: Входной массив NumPy
n: Количество самых больших значений
Returns:
Массив индексов, отсортированных по соответствующим значениям (убывание)
"""
if n <= 0:
return np.array([], dtype=int)
if n >= len(arr):
return np.argsort(arr)[::-1]
# Получаем индексы, которые бы разделили массив с N самыми большими в конце
partitioned_indices = np.argpartition(arr, -n)
# Получаем последние N индексов
largest_indices = partitioned_indices[-n:]
# Сортируем индексы по их значениям в порядке убывания
return largest_indices[np.argsort(arr[largest_indices])[::-1]]
# Пример использования
arr = np.array([1, 3, 2, 4, 5])
n = 3
result = indices_of_n_largest(arr, n)
print(f"Индексы {n} самых больших значений: {result}")
# Вывод: [4 3 1] для массива [1, 3, 2, 4, 5]
Практические примеры
Пример 1: Базовое использование
arr = np.array([10, 20, 30, 40, 50])
n = 2
indices = indices_of_n_largest(arr, n)
print(f"Массив: {arr}")
print(f"Индексы {n} самых больших значений: {indices}")
print(f"Соответствующие значения: {arr[indices]}")
Пример 2: 2‑D массив
# Для 2‑D массивов используйте параметр axis
arr_2d = np.array([[1, 5, 3],
[8, 2, 6],
[4, 9, 7]])
n = 2
# Находим индексы N самых больших значений во всём массиве
flat_indices = np.argpartition(arr_2d.ravel(), -n)[-n:]
row_indices, col_indices = np.unravel_index(flat_indices, arr_2d.shape)
print(f"2D массив:\n{arr_2d}")
print(f"Индексы {n} самых больших значений: {(row_indices, col_indices)}")
print(f"Соответствующие значения: {arr_2d[row_indices, col_indices]}")
Пример 3: С одинаковыми значениями
arr_with_ties = np.array([5, 2, 8, 8, 3, 1])
n = 3
indices = indices_of_n_largest(arr_with_ties, n)
print(f"Массив с одинаковыми значениями: {arr_with_ties}")
print(f"Индексы {n} самых больших значений: {indices}")
print(f"Соответствующие значения: {arr_with_ties[indices]}")
Граничные случаи и соображения
-
Когда N превышает размер массива:
pythonarr = np.array([1, 2, 3]) n = 5 # Больше длины массива # Должен вернуть индексы всех элементов в порядке убывания -
Когда N равно 0:
pythonn = 0 # Должен вернуть пустой массив -
Когда массив содержит отрицательные значения:
pythonarr = np.array([-5, -2, -8, -1, -3]) n = 2 # Должен вернуть индексы наименее отрицательных (самых больших) значений -
Когда массив содержит NaN:
pythonarr = np.array([1, 2, np.nan, 3]) n = 3 # NaN обычно помещаются в конец по умолчанию -
При использовании параметра axis:
pythonarr_2d = np.array([[1, 5], [3, 2]]) n = 1 # Находим максимальное значение вдоль axis=0 (столбцы) indices = np.argpartition(arr_2d, -n, axis=0)[-n:, :]
Функция indices_of_n_largest корректно обрабатывает большинство этих граничных случаев. Для более сложных сценариев может потребоваться дополнительная проверка ошибок или обработка параметров.
Источники
- Документация NumPy – argsort
- Документация NumPy – argpartition
- Документация NumPy – unravel_index
- Советы по производительности NumPy
Заключение
Чтобы эффективно найти индексы N самых больших значений в массиве NumPy:
- Используйте
np.argpartition()для оптимальной производительности, когда нужны только топ‑N значений. - Объедините с
np.argsort(), если нужны индексы, отсортированные по соответствующим значениям. - Учитывайте конкретные требования – если N очень мало (1‑3), простой цикл с
np.argmax()может быть достаточным. - Обрабатывайте граничные случаи: массивы меньше N, отрицательные значения, NaN и т.д.
- Для многомерных массивов используйте
ravel()или укажите параметрaxis, чтобы работать вдоль конкретных измерений.
Предоставленная функция indices_of_n_largest обеспечивает надёжное решение, сочетающее производительность и читаемость для большинства типичных задач.