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 .
1.17.3 3.7.5 (padrão, 25 de outubro de 2019, 10:52:18)
[Clang 4.0.1 (tags / RELEASE_401 / final)]
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:
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.
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 comoseries, ..., 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