Другое

Получить индексы 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.argsort() возвращает индексы, которые бы отсортировали массив по возрастанию. Чтобы получить индексы N самых больших значений, можно:

  1. Получить индексы всех отсортированных значений с помощью np.argsort()
  2. Взять последние N индексов из результата
  3. При необходимости обратить порядок, чтобы получить их от большего к меньшему
python
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) времени:

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

Если вам нужны индексы, отсортированные по соответствующим значениям, можно отсортировать их:

python
# Сортируем индексы по их значениям в порядке убывания
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():

python
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: Базовое использование

python
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 массив

python
# Для 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: С одинаковыми значениями

python
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]}")

Граничные случаи и соображения

  1. Когда N превышает размер массива:

    python
    arr = np.array([1, 2, 3])
    n = 5  # Больше длины массива
    # Должен вернуть индексы всех элементов в порядке убывания
    
  2. Когда N равно 0:

    python
    n = 0
    # Должен вернуть пустой массив
    
  3. Когда массив содержит отрицательные значения:

    python
    arr = np.array([-5, -2, -8, -1, -3])
    n = 2
    # Должен вернуть индексы наименее отрицательных (самых больших) значений
    
  4. Когда массив содержит NaN:

    python
    arr = np.array([1, 2, np.nan, 3])
    n = 3
    # NaN обычно помещаются в конец по умолчанию
    
  5. При использовании параметра axis:

    python
    arr_2d = np.array([[1, 5], [3, 2]])
    n = 1
    # Находим максимальное значение вдоль axis=0 (столбцы)
    indices = np.argpartition(arr_2d, -n, axis=0)[-n:, :]
    

Функция indices_of_n_largest корректно обрабатывает большинство этих граничных случаев. Для более сложных сценариев может потребоваться дополнительная проверка ошибок или обработка параметров.


Источники

  1. Документация NumPy – argsort
  2. Документация NumPy – argpartition
  3. Документация NumPy – unravel_index
  4. Советы по производительности NumPy

Заключение

Чтобы эффективно найти индексы N самых больших значений в массиве NumPy:

  1. Используйте np.argpartition() для оптимальной производительности, когда нужны только топ‑N значений.
  2. Объедините с np.argsort(), если нужны индексы, отсортированные по соответствующим значениям.
  3. Учитывайте конкретные требования – если N очень мало (1‑3), простой цикл с np.argmax() может быть достаточным.
  4. Обрабатывайте граничные случаи: массивы меньше N, отрицательные значения, NaN и т.д.
  5. Для многомерных массивов используйте ravel() или укажите параметр axis, чтобы работать вдоль конкретных измерений.

Предоставленная функция indices_of_n_largest обеспечивает надёжное решение, сочетающее производительность и читаемость для большинства типичных задач.

Авторы
Проверено модерацией
Модерация