Fatia de índice Numpy sem perder informações de dimensão

96

Estou usando numpy e desejo indexar uma linha sem perder as informações de dimensão.

import numpy as np
X = np.zeros((100,10))
X.shape        # >> (100, 10)
xslice = X[10,:]
xslice.shape   # >> (10,)  

Neste exemplo, xslice agora tem 1 dimensão, mas eu quero que seja (1,10). Em R, eu usaria X [10,:, drop = F]. Existe algo semelhante em entorpecido. Não consegui encontrar na documentação e não vi uma pergunta semelhante feita.

Obrigado!

assuntos mentais
fonte

Respostas:

58

Provavelmente é mais fácil de fazer x[None, 10, :]ou de forma equivalente (mas mais legível)x[np.newaxis, 10, :] .

Quanto ao motivo pelo qual não é o padrão, pessoalmente, acho que ter matrizes constantemente com dimensões singleton se torna irritante muito rapidamente. Eu acho que os devs entorpecidos se sentiram da mesma maneira.

Além disso, arrays de transmissão de controle entorpecido muito bem, então geralmente há poucos motivos para reter a dimensão do array de onde veio o slice. Se você fez isso, coisas como:

a = np.zeros((100,100,10))
b = np.zeros(100,10)
a[0,:,:] = b

ou não funcionaria ou seria muito mais difícil de implementar.

(Ou pelo menos esse é o meu palpite sobre o raciocínio do devedor entorpecido por trás de descartar informações de dimensão ao fatiar)

Joe Kington
fonte
6
@Lisa: x[None, 10]fará o que você quiser.
naught101
Sim. Coloque seus Nones ao lado das sombras que você está cortando.
Mad Physicist
1
O exemplo não contém colchetes extras para a tupla na atribuição a b; deveria ser b = np.zeros((100,10)).
Jerzy
Qual é a razão para usar 3 índices no total em vez de apenas dois? Quero dizer X[10,None](usando seu código como exemplo).
greenoldman
8
" geralmente há pouca razão para reter a dimensão da matriz " ... Bem, certamente, totalmente, e confundirá a multiplicação da matriz ( np.matmul()ou@ ). Acabei de me queimar com isso.
Jean-François Corbett
89

Outra solução é fazer

X[[10],:]

ou

I = array([10])
X[I,:]

A dimensionalidade de uma matriz é preservada quando a indexação é realizada por uma lista (ou matriz) de índices. Isso é bom porque deixa você com a escolha entre manter a dimensão e comprimir.

gnebehay
fonte
2
Isso copia os dados da matriz
por
Isso não é sempre o caso. Veja: x = np.array([[1,2,3,4]]) se você cortá- x[[0],[1,2]] lo, obterá o unidimensional. array([2, 3]) Minha opinião é que ao selecionar vetores de coluna ou linha, é melhor tornar o corte simples e, em seguida np.reshape, usar . Então, no meu exemplo serianp.reshape(x[0,[1,2]],[1,2])
Alexander
1
outros, fiquem atentos ao ponto-e-vírgula no final - é importante, X[[10]]seria interpretado como X[10]e a forma seria menor; da mesma forma, X[[10, 20]] == X[10, 20]e a forma é ainda menor
Ben Usman
1
Aviso : não misture esta forma de indexação apenas com indexação de inteiros! Se você tivesse aforma (10, 20, 30), então a[0, :, [0]]terá forma (1, 20), não (20, 1), porque neste último os índices são transmitidos para o a[[0], :, [0]]que muitas vezes não é bem o que você espera! Visto a[0, :, :1]que lhe dará (20, 1)como esperado. Além disso, veja o comentário acima para casos extremos esquisitos com índice único. No geral, parece que esse método tem muitos casos extremos.
Ben Usman
29

Encontrei algumas soluções razoáveis.

1) usar numpy.take(X,[10],0)

2) use esta indexação estranha X[10:11:, :]

Idealmente, este deve ser o padrão. Nunca entendi por que as dimensões são abandonadas. Mas isso é uma discussão para numpy ...

assuntos mentais
fonte
1
'dimensões' são eliminadas ao indexar listas Python alist[0]e mantidas ao dividi-las.
hpaulj
4
A opção 2 (que pode ser escrita slice(n, n+1)para extração de índice n) deve ser a resposta aceita, pois é a única que se estende naturalmente ao caso n-dimensional.
norok2 de
A opção 2 parece ser capaz de ser escrita como X[10:11, :]no Python 3.7.5 (ou seja, sem os dois pontos extras após o 11)
Joe
6

Aqui está uma alternativa que eu gosto mais. Em vez de indexar com um único número, indexe com um intervalo. Ou seja, use X[10:11,:]. (Observe que 10:11não inclui 11).

import numpy as np
X = np.zeros((100,10))
X.shape        # >> (100, 10)
xslice = X[10:11,:]
xslice.shape   # >> (1,10)

Isso torna mais fácil entender com mais dimensões também, sem Nonefazer malabarismos e descobrir qual eixo usar qual índice. Também não há necessidade de fazer contabilidade extra em relação ao tamanho do array, apenas i:i+1para qualquer um ique você usaria na indexação regular.

b = np.ones((2, 3, 4))
b.shape # >> (2, 3, 4)
b[1:2,:,:].shape  # >> (1, 3, 4)
b[:, 2:3, :].shape .  # >> (2, 1, 4)
Andrew Schwartz
fonte
0

Isso é especialmente irritante se você estiver indexando por um array que pode ter comprimento 1 em tempo de execução. Para esse caso, há np.ix_:

some_array[np.ix_(row_index,column_index)]
Jthorpe
fonte