Pular para o conteúdo

    Capítulo 27, Intermediário

    Indexação avançada e malhas

    Quando a pergunta é "o maior de cada linha, e onde ele está", a indexação simples não basta. Estas ferramentas resolvem esses casos sem laço.

    Código deste capítulo: intermediario/cap27_indexacao_avancada.py

    Pegar o melhor de cada linha

    O argmax(axis=1) dá a posição do maior valor em cada linha. Para pegar os valores nessas posições, o take_along_axis usa as posições ao longo de um eixo, e o [:, None] as prepara para combinar por broadcasting:

    intermediario/cap27_indexacao_avancada.pylinhas 10 a 15
    import numpy as np
    
    notas = np.array([[7, 9, 5], [4, 8, 6]])
    melhor = notas.argmax(axis=1)
    print(melhor, np.take_along_axis(notas, melhor[:, None], axis=1).ravel())
    print(np.take_along_axis(notas, np.argsort(notas, axis=1), axis=1))
    
    Saída
    [1 1] [9 8]
    [[5 7 9]
     [4 6 8]]
    

    A segunda linha ordena cada linha pela sua própria ordem, algo que o np.sort(axis=1) também faria. A vantagem do take_along_axis aparece quando você quer reordenar outro array com as posições de um primeiro.

    Selecionar linhas e colunas ao mesmo tempo

    Com duas listas, a indexação fancy combina par a par (capítulo 9). Para o produto cartesiano (todas as combinações de linhas e colunas escolhidas), o np.ix_ monta os índices certos:

    intermediario/cap27_indexacao_avancada.pylinhas 20 a 21
    m = np.arange(16).reshape(4, 4)
    print(m[np.ix_([0, 2], [1, 3])])
    
    Saída
    [[ 1  3]
     [ 9 11]]
    

    O resultado é o bloco formado pelas linhas 0 e 2 e pelas colunas 1 e 3.

    Malhas de coordenadas

    O np.meshgrid gera, a partir de dois vetores, duas grades com todas as coordenadas de um plano. É a base para calcular uma função em uma região inteira e depois desenhá-la:

    intermediario/cap27_indexacao_avancada.pylinhas 26 a 30
    x = np.linspace(-1, 1, 3)
    y = np.linspace(-1, 1, 3)
    X, Y = np.meshgrid(x, y)
    print(X)
    print(np.hypot(X, Y).round(2))
    
    Saída
    [[-1.  0.  1.]
     [-1.  0.  1.]
     [-1.  0.  1.]]
    [[1.41 1.   1.41]
     [1.   0.   1.  ]
     [1.41 1.   1.41]]
    

    O np.hypot(X, Y) calcula a distância de cada ponto da malha à origem. O resultado é a distância sobre uma grade, sem nenhum laço.

    Onde está o máximo?

    O argmax de um array 2D devolve a posição no array achatado. O np.unravel_index a converte em coordenadas (linha, coluna). E o np.argwhere lista todas as posições que satisfazem uma condição:

    intermediario/cap27_indexacao_avancada.pylinhas 35 a 38
    plano = np.array([[3, 8, 1], [9, 2, 7]])
    pos = np.unravel_index(plano.argmax(), plano.shape)
    print(pos, plano[pos])
    print(np.argwhere(plano > 6))
    
    Saída
    (np.int64(1), np.int64(0)) 9
    [[0 1]
     [1 0]
     [1 2]]
    

    Repare na forma como o NumPy 2 imprime a tupla: cada coordenada aparece como np.int64(...), porque elas são escalares NumPy, e não inteiros do Python. É só a representação, e as contas funcionam igual. Em NumPy 1.x, você veria apenas os números.

    One-hot por indexação

    Transformar rótulos numéricos em vetores "one-hot" (um 1 na posição da classe, zeros no resto) é uma operação comum em aprendizado de máquina, e a indexação fancy de uma matriz identidade a faz em uma linha:

    intermediario/cap27_indexacao_avancada.pylinhas 43 a 44
    rotulos = np.array([0, 2, 1])
    print(np.eye(4)[rotulos])
    
    Saída
    [[1. 0. 0. 0.]
     [0. 0. 1. 0.]
     [0. 1. 0. 0.]]
    

    Exercício 1

    Ordenar as linhas pela soma

    Escreva ordenar_linhas_pela_soma(m) que devolva a matriz com as linhas reordenadas da menor para a maior soma.