Gire uma matriz com Matplotlib

Oct 29 2020

Estou girando a matriz ansn (n = 20, embora possa mudar) 30 graus para a direita usando os métodos de transformação do Matplotlib .

O erro aparece porque a rotação é feita por cima e não pela base. Tentei inverter o índice por meio de np.flip()ou, ax.imshow(origin = 'lower')mas também inverteu o triângulo, então preciso descobrir como definir o ponto de origem da transformação .

Defintley, é isso que eu gostaria de obter :

Observe que os pequenos quadrados que conformam a matriz diagonal seriam transformados em triângulos. Isso poderia ser feito? Talvez por um método imshow que retorna meio pixel? O resto dos pixeles permaneceria o mesmo (pequenos quadrados deformados).

Aqui está o código para gerar a matriz ( ponto de partida ):

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.transforms as mtransforms

matrix = np.random.rand(20,20)

# Generate a boolean matrix (same shape than 'matrix') and select lower triangle values:

condition = np.tril(np.ones((matrix.shape))).astype(np.bool)
triangle = np.where(condition, matrix, np.nan)

fig, ax = plt.subplots(figsize = (8,8))

ax.imshow(triangle, cmap = 'Spectral')

E aqui está o código tentando girá- lo:

im = ax.imshow(matrix, cmap = 'Spectral')
im.set_transform(mtransforms.Affine2D().skew(30, 0) + ax.transData)
ax.plot(transform = trans_data)

Não estou usando a classe Triangle do Matplotlib porque o diagrama ternário é representado por meio de uma operação de interpolação e quero representar os valores da matriz original.

Eu realmente aprecio a ajuda de alguém. Muito obrigado antecipadamente.

Respostas

3 tmdavison Oct 29 2020 at 13:39

Em vez de alterar a origem da transformação de inclinação, você pode encadea-la com uma translação na direção x para obter a transformação que está procurando.

Observe que a skewtransformação assume um ângulo em radianos (você estava usando em graus). Existe uma skew_degtransformação equivalente se você quiser trabalhar em graus, mas aqui eu trabalho apenas em radianos.

Observe também que acho que você deseja ter um triângulo isósceles com base e altura iguais a 20 (ou o que você escolher como N), o ângulo que você deseja não é de 30 graus, mas sim de arctan (1/2) (= 26,56 deg).

O valor que você precisa para traduzir na direção x é xtrans = N * np.tan(angle).

Você pode encadear transformações facilmente em matplotlib. Aqui podemos inclinar primeiro e depois traduzir:

mtransforms.Affine2D().skew(-angle, 0).translate(xtrans, 0)

Observe que este script funciona para qualquer valor de N.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.transforms as mtransforms

N = 20
matrix = np.random.rand(N, N)

# Generate a boolean matrix (same shape than 'matrix') and select lower triangle values:

condition = np.tril(np.ones((matrix.shape))).astype(np.bool)
triangle = np.where(condition, matrix, np.nan)

fig, ax = plt.subplots(figsize = (8,8))

im = ax.imshow(triangle, cmap = 'Spectral')

angle = np.arctan(1/2)
xtrans = N * np.tan(angle)
im.set_transform(mtransforms.Affine2D().skew(-angle, 0).translate(xtrans, 0) + ax.transData)

ax.set_xlim(-0.5, N + 0.5)
plt.show()

Para N = 20

E para N = 30

MiguelGonzalez Oct 30 2020 at 10:20

Finalmente obtenho um triângulo equilátero com escala no eixo y. Aqui, mostro o código.

Portanto, permite converter uma matriz em um triângulo equilátero , o que responde à minha pergunta anterior:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.transforms as mtransforms
import matplotlib

bins = 50
Z = np.random.rand(bins, bins)

# Generate a boolean matrix (same shape than 'matrix') and select lower triangle values:
condition = np.tril(np.ones((Z.shape))).astype(np.bool)
Z = np.where(condition, Z, np.nan)

fig, ax = plt.subplots(figsize = (8,8))
im = ax.imshow(Z, cmap = 'Spectral')

# Required angles (in Rad)
alpha = np.arctan(1/2)        # 26 deg angle, in radians.
beta = np.arctan(np.pi/6)     # 30 deg angle, in radians.

# Coefficients:
xtrans = np.sin(beta) * bins
scale_y = np.cos(beta)     

# Transformation:
im.set_transform(mtransforms.Affine2D().skew      (-alpha, 0)
                                       .scale     (1,scale_y)
                                       .translate (xtrans, 0) 
                                        + ax.transData)

ax.set_ylim(bins,-5)
ax.set_xlim(-5,bins)

plt.show()