python - array - Selecionando várias fatias de uma matriz numpy de uma só vez




reshape list python (4)

Estou procurando uma maneira de selecionar várias fatias de uma matriz numpy de uma só vez. Digamos que temos uma matriz de dados 1D e queremos extrair três partes, como abaixo:

data_extractions = []

for start_index in range(0, 3):
    data_extractions.append(data[start_index: start_index + 5])

Depois, as data_extractions serão:

data_extractions = [
    data[0:5],
    data[1:6],
    data[2:7]
]

Existe alguma maneira de executar a operação acima sem o loop for? Algum tipo de esquema de indexação numpy que me permitia selecionar várias fatias de uma matriz e devolvê-las como muitas matrizes, digamos em uma matriz dimensional n + 1?

Eu pensei que talvez eu possa replicar meus dados e selecione um intervalo de cada linha, mas o código abaixo gera um IndexError

replicated_data = np.vstack([data] * 3)
data_extractions = replicated_data[[range(3)], [slice(0, 5), slice(1, 6), slice(2, 7)]

Nesta publicação, há uma abordagem com strided-indexing scheme np.lib.stride_tricks.as_strided usando np.lib.stride_tricks.as_strided que basicamente cria uma visualização na matriz de entrada e, como tal, é bastante eficiente para criação e, sendo assim, a visualização ocupa espaço de memória nomore. Além disso, isso funciona para ndarrays com número genérico de dimensões.

Aqui está a implementação -

def strided_axis0(a, L):
    # Store the shape and strides info
    shp = a.shape
    s  = a.strides

    # Compute length of output array along the first axis
    nd0 = shp[0]-L+1

    # Setup shape and strides for use with np.lib.stride_tricks.as_strided
    # and get (n+1) dim output array
    shp_in = (nd0,L)+shp[1:]
    strd_in = (s[0],) + s
    return np.lib.stride_tricks.as_strided(a, shape=shp_in, strides=strd_in)

Exemplo de execução para um caso de matriz 4D -

In [44]: a = np.random.randint(11,99,(10,4,2,3)) # Array

In [45]: L = 5      # Window length along the first axis

In [46]: out = strided_axis0(a, L)

In [47]: np.allclose(a[0:L], out[0])  # Verify outputs
Out[47]: True

In [48]: np.allclose(a[1:L+1], out[1])
Out[48]: True

In [49]: np.allclose(a[2:L+2], out[2])
Out[49]: True

No caso geral, você precisa fazer algum tipo de iteração - e concatenação - ao construir os índices ou ao coletar os resultados. Somente quando o padrão de fatia é regular, é possível usar uma fatia generalizada via as_strided .

A resposta aceita constrói uma matriz de indexação, uma linha por fatia. Portanto, ele está iterando sobre as fatias e a própria arange é uma iteração (rápida). E o np.array concatena em um novo eixo (o np.stack generaliza isso).

In [264]: np.array([np.arange(0,5), np.arange(1,6), np.arange(2,7)])
Out[264]: 
array([[0, 1, 2, 3, 4],
       [1, 2, 3, 4, 5],
       [2, 3, 4, 5, 6]])

métodos de conveniência indexing_tricks para fazer a mesma coisa:

In [265]: np.r_[0:5, 1:6, 2:7]
Out[265]: array([0, 1, 2, 3, 4, 1, 2, 3, 4, 5, 2, 3, 4, 5, 6])

Isso pega a notação de fatia, a expande com arange e concatena. Até me permite expandir e concatenar em 2D

In [269]: np.r_['0,2',0:5, 1:6, 2:7]
Out[269]: 
array([[0, 1, 2, 3, 4],
       [1, 2, 3, 4, 5],
       [2, 3, 4, 5, 6]])

In [270]: data=np.array(list('abcdefghijk'))
In [272]: data[np.r_['0,2',0:5, 1:6, 2:7]]
Out[272]: 
array([['a', 'b', 'c', 'd', 'e'],
       ['b', 'c', 'd', 'e', 'f'],
       ['c', 'd', 'e', 'f', 'g']], 
      dtype='<U1')
In [273]: data[np.r_[0:5, 1:6, 2:7]]
Out[273]: 
array(['a', 'b', 'c', 'd', 'e', 'b', 'c', 'd', 'e', 'f', 'c', 'd', 'e',
       'f', 'g'], 
      dtype='<U1')

Concatenar resultados após a indexação também funciona.

In [274]: np.stack([data[0:5],data[1:6],data[2:7]])

Minha memória de outras questões de SO é que os tempos relativos estão na mesma ordem de magnitude. Pode variar, por exemplo, com o número de fatias versus seu comprimento. No geral, o número de valores que precisam ser copiados da origem para o destino será o mesmo.

Se as fatias variarem em comprimento, você precisará usar a indexação plana.


Você pode fatiar sua matriz com uma matriz de fatias preparada

a = np.array(list('abcdefg'))

b = np.array([
        [0, 1, 2, 3, 4],
        [1, 2, 3, 4, 5],
        [2, 3, 4, 5, 6]
    ])

a[b]

No entanto, b não precisa ser gerado manualmente dessa maneira. Pode ser mais dinâmico com

b = np.arange(5) + np.arange(3)[:, None]

Você pode usar os índices para selecionar as linhas que deseja na forma apropriada. Por exemplo:

 data = np.random.normal(size=(100,2,2,2))

 # Creating an array of row-indexes
 indexes = np.array([np.arange(0,5), np.arange(1,6), np.arange(2,7)])
 # data[indexes] will return an element of shape (3,5,2,2,2). Converting
 # to list happens along axis 0
 data_extractions = list(data[indexes])

 np.all(data_extractions[1] == s[1:6])
 True




slice