Numpy: O comportamento de indexação com reticências não é intuitivo.

Criado em 27 jul. 2020  ·  6Comentários  ·  Fonte: numpy/numpy

A indexação de matriz com elipses pode ser extremamente contra-intuitiva. Por exemplo, no exemplo a seguir, a introdução de elipses redundantes altera a forma da saída:

a = np.array([[[False]]])
a[0:0, 0, ..., [0]]
Out[23]: array([], shape=(1, 0), dtype=bool)
a[0:0, 0,  [0]]
Out[24]: array([], shape=(0, 1), dtype=bool)

Não acho que esse seja o comportamento desejado, mas parece resultar diretamente das decisões de design com relação a como a indexação complexa é tratada .

Informações sobre a versão Numpy / Python:

1.17.3 3.7.5 (padrão, 25 de outubro de 2019, 10:52:18)
[Clang 4.0.1 (tags / RELEASE_401 / final)]

04 - Documentation 33 - Question 57 - Close?

Comentários muito úteis

Concordo que tudo isso é confuso, e eu nem teria certeza sem verificar o que acontece aqui. Mas, parece a escolha correta para mim.

Por que deveria ser verdadeiramente "invisível"? ... deve se comportar de forma idêntica para qualquer número de dimensões. Para fazer isso, ele deve acionar a transposição em todos os casos. Ie imagine algo organizado como series, ..., color onde ... é definido pelo usuário e pode ser 0-d. Se você fosse escrever um programa para manipular esses dados, exigiria que a indexação fosse previsivelmente transposta, não importando para onde ... expande (ou não).

No final, isso é simplesmente confuso e teríamos que pegar .oindex e .vindex etc. mais a sério para resolver isso: https://numpy.org/neps/nep- 0021-advanced-indexing.html

Todos 6 comentários

Este parece um caso particularmente desagradável - acho que consideramos ... forçando os dois índices avançados, 0 e [0] , a serem considerados não adjacentes - embora os eixos que eles indexam _são_ adjacentes.

Encontramos esse problema ao tentar replicar a indexação numpy. Acho que é uma interação estranha entre duas regras:

  • A elipse se expande para uma tupla de fatias completas, cada uma considerada um índice básico
  • Índices avançados e básicos de ordem mista acionam uma operação de transposição.

Este caso especial é acionado porque uma tupla 0-d (quando ... é usada com todos os índices presentes) ainda é considerada um bloco de índice básico, quando deveria ser realmente invisível.

Concordo que tudo isso é confuso, e eu nem teria certeza sem verificar o que acontece aqui. Mas, parece a escolha correta para mim.

Por que deveria ser verdadeiramente "invisível"? ... deve se comportar de forma idêntica para qualquer número de dimensões. Para fazer isso, ele deve acionar a transposição em todos os casos. Ie imagine algo organizado como series, ..., color onde ... é definido pelo usuário e pode ser 0-d. Se você fosse escrever um programa para manipular esses dados, exigiria que a indexação fosse previsivelmente transposta, não importando para onde ... expande (ou não).

No final, isso é simplesmente confuso e teríamos que pegar .oindex e .vindex etc. mais a sério para resolver isso: https://numpy.org/neps/nep- 0021-advanced-indexing.html

Estou com o argumento de @seberg de que o comportamento atual é a melhor generalização. Certamente poderíamos ter certeza que os documentos são mais claros.

Não sabia dessa proposta, obrigado pela referência @seberg!

Deixe-me reformular apenas para ter certeza de que entendi seu argumento. Digamos que rotulemos os índices avançados A e os índices básicos B e, como acima, estou chamando a operação de reordenamento de uma transposição (generalizada). Em seu exemplo, temos quatro casos:

  • [A1, ..., A2] e [B1, ... A1] : esses casos disparam a transposição para [A1, A2, ....] e [A1, B1, ...] independentemente de como ... expande.
  • [A1, ..., B1] e [B1, ..., B2] : esses casos não.

Essas regras são consistentes quando você sabe a qual classe (A ou B) series e color pertencem, independentemente de como ... expande. Isso é equivalente a tratar ... como um bloco (potencialmente 0-d) de índices básicos. Tratar 0-d ... como um caso especial seria ruim porque a transposição seria condicional se o usuário passou em um array 2-D ou 3-ou-mais-D. Eu concordo, este é um lugar ruim para se estar.

Minha intuição de que os blocos 0-d deveriam ser autônomos foi impulsionada por como as tuplas se comportam, onde para qualquer i ,

i: int
M: Tuple[int, ...]
N = ()
assert M[:i] + N + M[i:] == M

Isso entra em conflito com a convenção de indexação numpy que resulta nos quatro casos acima, dependendo do que i é. Isso tem mais a ver com a própria operação de transposição, em vez de como ... é tratado, e é um argumento para a proposta NEP21.

Durante o desenvolvimento, mudaríamos a forma como fizemos a indexação e inseriríamos ... para tornar o código à prova de futuro e ficaríamos realmente confusos quando as formas fossem magicamente transpostas. Foi apenas agravado pelo fato de a caixa vazia de ... ser particularmente contraintuitiva.

Obrigado por dar uma olhada! Posso enviar um documento de relações públicas, se desejar.

@antonl a doc PR é sempre bem-vindo, há muitas melhorias possíveis aqui.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

dmvianna picture dmvianna  ·  4Comentários

toddrjen picture toddrjen  ·  4Comentários

MareinK picture MareinK  ·  3Comentários

navytux picture navytux  ·  4Comentários

inducer picture inducer  ·  3Comentários