Как справиться со странным поведением индексации со ссылкой на координаты массива numpy?

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

t = np.zeros((5,5))
coord = [[2,3], [1,2]]
t[coord] = 30
print(t)

Выход:

[[ 0.  0.  0.  0.  0.]
[ 0.  0.  0.  0.  0.]
[ 0. 30.  0.  0.  0.]
[ 0.  0. 30.  0.  0.]
[ 0.  0.  0.  0.  0.]]

Но тогда, если в списке всего одна точка:

t = np.zeros((5,5))
coord = [[2,3]]
t[coord] = 30
print(t)

Выход:

[[ 0.  0.  0.  0.  0.]
[ 0.  0.  0.  0.  0.]
[30. 30. 30. 30. 30.]
[30. 30. 30. 30. 30.]
[ 0.  0.  0.  0.  0.]]

Затем, если я конвертирую список в массив numpy, он еще больше разбивается:

t = np.zeros((5,5))
coord = np.array([[2,3], [1,2]])
t[coord] = 30
print(t)

Выход:

[[ 0.  0.  0.  0.  0.]
[30. 30. 30. 30. 30.]
[30. 30. 30. 30. 30.]
[30. 30. 30. 30. 30.]
[ 0.  0.  0.  0.  0.]]

Как мне справиться с этим, чтобы я всегда получал первый вывод, даже если есть только один элемент, и это пустой массив?

Спасибо!

Обновлено:

Что в настоящее время происходит в моем коде, так это то, что программа возвращает массив точек:

array([[ 9,  5,  0],
       [ 4,  2,  2],
       [11,  4,  2],
       [ 5,  7,  2],
       [11, 12,  2],
       [12,  9,  0],
       [ 5,  4,  7],
       [ 3,  2,  1],
       ...

Затем я хочу использовать это, чтобы изменить эти точки координат в большей матрице 14 * 14 * 9. big_matrix[координата] = 0

EDIT2: на основе комментария @hpaulj

Вот пример полномасштабной проблемы:

coord = np.array([[ 4,  7,  0],
       [ 9,  6,  1],
       [ 8,  2,  0],
       [ 8,  7,  6],
       [ 3, 10,  4],
       [ 6,  4,  3],
       [10, 10,  3],
       [ 3,  2,  1]], dtype='int32')
matrix[coord]

возвращает:

array([[[[0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         ...,
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.]],

Вы можете рассмотреть возможность использования Обозначение среза Numpy, который должен дать вам то, что вы ищете.

not link 08.04.2019 22:47

Возможный дубликат Индексирование массива numpy со списком кортежей

Mark 08.04.2019 22:48

Я пытался объяснить, что происходит в ваших различных случаях, но мне пришло в голову, что я не уверен, чего вы хотите. Возможно, вам потребуется продемонстрировать желаемый результат, будь то присвоение значений определенным точкам или определенным строкам.

hpaulj 09.04.2019 00:19

@hpaulj я отредактировал, чтобы ответить на ваш вопрос.

Collin Cunningham 09.04.2019 01:05

Ваши примеры были 2x2, которые в массиве 2d можно интерпретировать в любом случае. Новый пример с массивом (8,3) для трехмерного случая работает только в одном направлении, как набор из 8 точек.

hpaulj 09.04.2019 01:25

Вышеприведенный случай является упрощенной версией. В полномасштабном проекте я видел поведение «выбор строки», когда список имел длину 1. Чтобы предотвратить это поведение, попытка запустить tuple(coord) исправляет случай с одной точкой, но затем выполняет поведение «выбор строки» в случае с несколькими точками. numpy не интерпретирует это как набор точек.

Collin Cunningham 09.04.2019 01:34
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
6
146
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Индексированное присваивание может скрывать некоторые детали, которые, как мне кажется, более понятны с эквивалентом getitem.

In [88]: arr = np.arange(25).reshape(5,5)                                       
In [89]: arr                                                                    
Out[89]: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24]])


In [90]: coord = [[2,3],[1,2]]                                                  
In [91]: arr[coord]                                                             
FutureWarning: Using a non-tuple sequence for multidimensional indexing 
is deprecated; use `arr[tuple(seq)]` instead of `arr[seq]`. In the 
future this will be interpreted as an array index, `arr[np.array(seq)]`, 
which will result either in an error or a different result.

Out[91]: array([11, 17])

Правильная индексация пары точек, применяя [2,3] для 1-й оси, [1,2] для 2-й:

In [92]: coord = ([2,3],[1,2])                                                  
In [93]: arr[coord]                                                             
Out[93]: array([11, 17])
In [94]: arr[[2,3], [1,2]]                                                      
Out[94]: array([11, 17])

Исторически numpy был немного неаккуратным и интерпретировал список списков как кортеж списков (при определенных обстоятельствах). Более новые версии пытаются устранить это несоответствие.

In [95]: coord = [[2,3]]                                                        
In [96]: arr[coord]                                                             
FutureWarning: Using a non-tuple sequence for multidimensional indexing is deprecated; use `arr[tuple(seq)]` instead of `arr[seq]`. In the future this will be interpreted as an array index, `arr[np.array(seq)]`, which will result either in an error or a different result.

Out[96]: 
array([[10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])

In [97]: coord = ([2,3],)          # clearer - pick 2 rows, e.g. arr[[2,3],:]                                              
In [98]: arr[coord]                                                             
Out[98]: 
array([[10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])
In [99]: arr[2,3]                 # pick one point
Out[99]: 13
In [100]: coord = (2,3)                                                         
In [101]: arr[coord]                                                            
Out[101]: 13

С массивом нет ни одного из этих запутанных списков для кортежей:

In [102]: coord = np.array([[2,3], [1,2]])                                      
In [103]: arr[coord]                                                            
Out[103]: 
array([[[10, 11, 12, 13, 14],
        [15, 16, 17, 18, 19]],

       [[ 5,  6,  7,  8,  9],
        [10, 11, 12, 13, 14]]])

Это выбирает (2,2) блок строк. Ваш arr[coord]=30 скрыл этот шаблон, так как в выборе строк были дубликаты (и назначение буферизуется). (для небуферизованного назначения проверьте np.add.at(t,coord,30)).

Если мы явно укажем, что coord применяется к 1-му измерению, мы будем использовать тот же стиль индексации массива:

In [111]: coord = [[2,3],[1,2]]                                                 
In [112]: arr[coord,:]                                                          
Out[112]: 
array([[[10, 11, 12, 13, 14],
        [15, 16, 17, 18, 19]],

       [[ 5,  6,  7,  8,  9],
        [10, 11, 12, 13, 14]]])

Обратите внимание на разницу в форме, если я использую этот последний [coord,] со списком из 1 элемента:

In [117]: coord = [[2,3]]                                                       
In [118]: arr[coord,]                                                           
Out[118]: 
array([[[10, 11, 12, 13, 14],
        [15, 16, 17, 18, 19]]])
In [119]: _.shape                                                               
Out[119]: (1, 2, 5)

Так что сделайте coord кортеж, а не список, если вы хотите, чтобы каждый элемент применялся к другому измерению. Или используйте массив, если вы хотите, чтобы он применялся только к одному измерению, или явно используйте нотацию [coord,:] like.


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

In [120]: coord = np.array([[2,3],[1,2]])                                       
In [121]: coord                                                                 
Out[121]: 
array([[2, 3],
       [1, 2]])
In [123]: tuple(coord.T)                                                        
Out[123]: (array([2, 1]), array([3, 2]))
In [124]: arr[tuple(coord.T)]                                                   
Out[124]: array([13,  7])

и с 4 баллами:

In [125]: coord = np.array([[2,3],[1,2],[0,0],[3,4]])                           
In [126]: arr[tuple(coord.T)]                                                   
Out[126]: array([13,  7,  0, 19])

Не знаю, поможет это или нет, но np.where часто используется для выбора точек в массиве:

Условие - кратное 4:

In [135]: arr%4==0                                                              
Out[135]: 
array([[ True, False, False, False,  True],
       [False, False, False,  True, False],
       [False, False,  True, False, False],
       [False,  True, False, False, False],
       [ True, False, False, False,  True]])

Индексы этих точек — кортеж с массивом для каждого измерения. Это можно использовать непосредственно в качестве индекса:

In [136]: np.where(arr%4==0)                                                    
Out[136]: (array([0, 0, 1, 2, 3, 4, 4]), array([0, 4, 3, 2, 1, 0, 4]))
In [137]: arr[_]                                                                
Out[137]: array([ 0,  4,  8, 12, 16, 20, 24])

argwhere применяет np.transpose к этому кортежу, создавая массив (n,2):

In [138]: np.argwhere(arr%4==0)                                                 
Out[138]: 
array([[0, 0],
       [0, 4],
       [1, 3],
       [2, 2],
       [3, 1],
       [4, 0],
       [4, 4]])

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

In [144]: [arr[i,j] for i,j in np.argwhere(arr%4==0)]                           
Out[144]: [0, 4, 8, 12, 16, 20, 24]

Я думаю, что вы генерируете координаты в этом стиле argwhere, но они вам действительно нужны в стиле where — как кортеж массивов.

Большое спасибо за этот подробный ответ. Основываясь на вашем ответе, я поигрался и немного смущен тем, что происходит, когда я делаю следующее: coord = tuple(np.array([[11, 6], [ 8, 5]])). Буферизируется ли выбор строки, потому что кортеж имеет элементы массива numpy?

Collin Cunningham 09.04.2019 00:27

Что именно вы пытаетесь сделать с этим последним случаем? Присвоить значения строкам 11,6,8 и 5, или точкам (11,6) и (8,5), или точкам (11,8) и (6,5)? Или что-то другое?

hpaulj 09.04.2019 00:35

Я хочу присвоить значения отдельным точкам в этих координатах "x, y".

Collin Cunningham 09.04.2019 01:06

Спасибо, эта операция транспонирования решила мой вопрос.

Collin Cunningham 09.04.2019 01:48

Другие вопросы по теме