Есть ли эффективный способ получить позицию максимального элемента, кроме указанного столбца c в матрице NumPy? - PullRequest
3 голосов
/ 20 июня 2020

Например, существует матрица 2d Numpy M:

[[1,10,3],
 [4,15,6]]

Максимальный элемент, за исключением элементов в M[:][1], - 6, а его позиция - (1,2). Итак, ответ: (1,2).

Большое спасибо за любую помощь!

Ответы [ 5 ]

3 голосов
/ 20 июня 2020

Односторонний:

col = 1
skip_col = np.delete(x, col, axis=1)
row, column = np.unravel_index(skip_col.argmax(), skip_col.shape)
if column >= col:
    column += 1 

Переведено:

  1. Удалить столбец
  2. найти максимальный аргумент (argmax дает сглаженный результат, unravel_index возвращает размещение в 2-м массиве)
  3. Если столбец больше или равен пропущенному, добавить один

После Комментарий дюн , I нравится предложение. Он практически идентичен по количеству строк, но не требует копирования (как в np.delete). Итак, если вы ограничены памятью (как в действительно больших данных):

col = 1
row, column = np.unravel_index(x[:, :col].argmax(), x[:, :col].shape)  # left max, saving a line assuming it's the global max, but less readable
right_max = np.unravel_index(x[:, col+1:].argmax(), x[:, col+1:].shape)
if x[right_max] > x[row, column]:
    row, column = right_max
    column += col
2 голосов
/ 20 июня 2020

Вот решение, использующее набор функций nan:

In [180]: arr = np.array([[1,10,3],[4,15,6]])                                   
In [181]: arr1 = arr.astype(float)                                              
In [182]: arr1[:,1]=np.nan                                                      
In [183]: arr1                                                                  
Out[183]: 
array([[ 1., nan,  3.],
       [ 4., nan,  6.]])
In [184]: np.nanargmax(arr1)                                                    
Out[184]: 5
In [185]: np.unravel_index(np.nanargmax(arr1),arr.shape)                        
Out[185]: (1, 2)

Это может быть не оптимальным по времени, но, вероятно, легче отладить эти альтернативы.

Глядя на np.nanargmax Я вижу, что он просто заменяет np.nan на -np.inf. Поэтому мы делаем нечто подобное, просто заменяя значения столбца исключения на достаточно маленькое целое число, чтобы они не были максимальными.

In [188]: arr1=arr.copy()                                                       
In [189]: arr1[:,1] = np.min(arr1)-1                                            
In [190]: arr1                                                                  
Out[190]: 
array([[1, 0, 3],
       [4, 0, 6]])
In [191]: np.argmax(arr1)                                                       
Out[191]: 5
In [192]: np.unravel_index(np.argmax(arr1),arr.shape)                           
Out[192]: (1, 2)

Я также могу представить решение, использующее np.ma.masked_array, но это имеет тенденцию быть более удобным, чем инструмент скорости.

0 голосов
/ 21 июня 2020

Другой способ без копии, индексирование столбцов списком:

import numpy as np

m = np.array([[1, 10, 3], [4, 15, 6]])
exclude_col = 1

# assign nicer names to the shape
rows, cols = m.shape

# generate indices for slicing
inds = list(range(cols))
inds.remove(exclude_col)

# find the maximum in the sliced array
max_ind = np.unravel_index(np.argmax(m[:, inds]), (rows, cols - 1))
# fix the found column index if we exceeded exclude_col
max_ind = (max_ind[0], max_ind[1] if max_ind[1] < exclude_col else max_ind[1] + 1)

Последняя строка является хорошим кандидатом для выражения присваивания Python3 .8, поэтому в Python3 .8 + вы можете написать:

max_ind = (max_ind[0], v if (v := max_ind[1]) < exclude_col else v + 1)

EDIT: подобное индексирование, вероятно, также создает копию, я не тестировал ее, но элементы не являются смежными в памяти.

0 голосов
/ 20 июня 2020

Соглашаясь с комментарием Dunes :

С небольшими массивами, как в вашем примере, вероятно, проще сделать копию матрицы без данного столбца, а потом взять макс. С большим массивом может быть быстрее взять максимум с любой стороны столбца и взять максимум с левой и правой сторон столбца.

Вот реализация каждого из этих случаев , и функция диспетчера. (Значение для THRESHOLD_SIZE необходимо добавить на основе экспериментов.)

Малый регистр массива

Создает массив с удаленным указанным столбцом. Вычисляет общий максимум, а затем место его появления. Добавляет единицу к столбцу, если он находится справа.

Корпус большого массива

Создает временные 1d массивы, содержащие максимумы столбца. Обычно (хотя и не во всех случаях) они будут значительно меньше, чем двумерный массив. Сначала определяется, какая сторона исключенного столбца содержит максимум, затем определяется, какой это столбец и, наконец, какая это строка. Это избавляет от необходимости проверять каждый элемент дважды. Код также избегает создания любого двумерного фрагмента массива в любой точке.

THRESHOLD_SIZE = .....


def get_max_position(m, exclude_column):
    return (get_max_position_largearray if m.size > THRESHOLD_SIZE 
            else get_max_position_smallarray)(m, exclude_column)


def get_max_position_smallarray(m, exclude_column):

    mnew = np.delete(m, exclude_column, axis=1)

    row, col = np.argwhere(mnew == np.max(mnew))[0]

    # uses: int(True)=1 and int(False)=0
    return (row, col + (col >= exclude_column))


def get_max_position_largearray(m, exclude_column):

    column_maxima = np.max(m, axis=0)

    l_col_maxima = column_maxima[:exclude_column]
    r_col_maxima = column_maxima[exclude_column + 1:]

    l_max = np.max(l_col_maxima) if l_col_maxima.size else None
    r_max = np.max(r_col_maxima) if r_col_maxima.size else None

    use_left = (True if r_max == None else
                False if l_max == None else
                (l_max > r_max))

    if use_left:
        themax = l_max
        col = np.argwhere(l_col_maxima == themax)[0][0]
    else:
        themax = r_max
        col = exclude_column + 1 + np.argwhere(r_col_maxima == themax)[0][0]

    row = np.argwhere(m[:,col] == themax)[0][0]

    return (row, col)

Вот пример в вопросе обоими методами:

m = np.array([[1,10,3],
              [4,15,6]])

exclude_column = 1

print(get_max_position_largearray(m, exclude_column))
print(get_max_position_smallarray(m, exclude_column))

Вывод:

(1, 2)
(1, 2)
0 голосов
/ 20 июня 2020

Вот что вы можете сделать:

m = [[1,10,3],
     [4,15,6]]

c = 1 # Choose the column to exclude 

a = max([[n,(k,b)] for k,i in enumerate(m) for b,n in enumerate(i) if b!=c])[1]

print(a)

Вывод:

(1, 2)
...