Miguel Gonzalez
Miguel Gonzalez

Reputation: 773

Rotate a matrix with Matplotlib

I am rotating a n x n matrix (n = 20, although it could change) 30 degrees rightwards using Matplotlib's transformation methods.

The error shows up because rotation is perfomed from the top and not from the base. I have tried to inverse the index through np.flip() or ax.imshow(origin = 'lower') but it also invert the triangle, so I need to discovered how to set the transformation origin point.

Defintley, this is what I would like to obtain:

2

Note that the little squares that conforms the diagonal matrix would be turned into triangles. Could this be done? Maybe by an imshow method that returns half a pixel? The rest of the pixeles would stay the same (deformed little squares).

Here is the code for generate the matrix (starting point):

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')

1

And here is the code trying to rotate it:

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

I am not using Triangle class of Matplotlib because the ternary diagram is represented througout an interpolation operation, and I want to represent the original matrix values.

I'd really appreciate some one's help. Thank you very much in advance.

Upvotes: 4

Views: 808

Answers (2)

Miguel Gonzalez
Miguel Gonzalez

Reputation: 773

I finally obtain an equilateral triangle scaling y-axis.Here I show the code.

Therefore, it allows converting a matrix into an equilateral triangle, what answer my previous question:

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()

Upvotes: 0

tmdavison
tmdavison

Reputation: 69116

Instead of changing the origin of the skew transformation, you could chain it with a translation in the x direction to achieve the transformation you are looking for.

Note that the skew transform takes an angle in radians (you were using it with degrees). There is an equivalent skew_deg transform if you want to work in degrees, but here I just work in radians.

Note also that I think you want to have an isosceles triangle with base and height both equal to 20 (or whatever you choose N to be), the angle you want is not 30 degrees, but actually arctan(1/2) (=26.56deg).

The amount you need to translate in the x direction is xtrans = N * np.tan(angle).

You can chain transforms easily in matplotlib. Here we can skew first, then translate:

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

Note that this script works for any value of 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()

For N = 20 enter image description here

And for N = 30 enter image description here

Upvotes: 3

Related Questions