6

I am looking for a way to rotate a plot generated in matplotlib-pyplot (Python libraries) by 45 degrees (so that instead of a square shape you would have a diamond shape, for example), anyone know if this can be done?

One way I can think of is to use a rotation filter on all the data so that it appears rotated, but then the plot itself will still be in the original orientation.

I want to be able to use the matplotlib interactive features, so saving as an image and then rotating won't work.

Also, I want to use pyplot functions to draw the plot, so using a different library for the plotting is not an ideal solution.

Bitwise
  • 7,577
  • 6
  • 33
  • 50
  • 2
    can I ask why? If is for something external, you might be able to get away with saving the figure as a png and then using imagemagik or related program to rotate the saved image. – tacaswell Oct 11 '12 at 21:21
  • 2
    @tcaswell that is possible, but I would prefer not to save to a figure since I want to use the matplotlib display features (panning, zooming, cursor/data coordination, etc.) interactively. And of course an in-library solution would be much more convenient. – Bitwise Oct 11 '12 at 21:25

5 Answers5

7

Ok so currently the only partial solution I found is to apply a rotation to the plot. This allows to use the interactive interface that matplotlib/pyplot offers.

For dot plots like plot() and scatter() this is trivial, but I was specifically interested in rotating imshow(). This link discusses the transform keyword that could have potentially been used for this task, but it is apparently not working.

Fortunately, I found a workaround using pcolormesh(). pcolormesh() plots a quadrilateral mesh and allows you to specify the corner coordinates. So, the answer is to just apply the relevant transformations to the corner coordinates. Note however, that pcolormesh() works a bit different than imshow - it plots your matrix flipped.

I haven't seen this solution anywhere on the web, so here is some code for pcolormesh()/imshow() rotated by 45 degrees:

import matplotlib.pyplot as plt
import numpy as np

def pcolormesh_45deg(C):

    n = C.shape[0]
    # create rotation/scaling matrix
    t = np.array([[1,0.5],[-1,0.5]])
    # create coordinate matrix and transform it
    A = np.dot(np.array([(i[1],i[0]) for i in itertools.product(range(n,-1,-1),range(0,n+1,1))]),t)
    # plot
    plt.pcolormesh(A[:,1].reshape(n+1,n+1),A[:,0].reshape(n+1,n+1),np.flipud(C))
Bitwise
  • 7,577
  • 6
  • 33
  • 50
  • 1
    I know this is a very old post, but it was extremely useful to me. The only question I have is how to make coordinates be at the ends of the bins and not their centers? – Phlya Jul 17 '15 at 12:31
  • `C` is a symmetric matrix. –  Nov 22 '19 at 16:20
2

Based on Bitwise anwer, you can use the following function:

def pcolormesh_45deg(C, ax=None, xticks=None, xticklabels=None, yticks=None,
                     yticklabels=None, aspect='equal', rotation=45,
                     *args, **kwargs):
    import itertools

    if ax is None:
        ax = plt.gca()
    n = C.shape[0]
    # create rotation/scaling matrix
    t = np.array([[1, .5], [-1, .5]])
    # create coordinate matrix and transform it
    product = itertools.product(range(n, -1, -1), range(0, n + 1, 1))
    A = np.dot(np.array([(ii[1], ii[0]) for ii in product]), t)
    # plot
    ax.pcolormesh((2 * A[:, 1].reshape(n + 1, n + 1) - n),
                  A[:, 0].reshape(n + 1, n + 1),
                  np.flipud(C), *args, **kwargs)

    xticks = np.linspace(0, n - 1, n, dtype=int) if xticks is None else xticks
    yticks = np.linspace(0, n - 1, n, dtype=int) if yticks is None else yticks

    if xticks is not None:
        xticklabels = xticks if xticklabels is None else xticklabels
        for tick, label, in zip(xticks, xticklabels):
            ax.scatter(-n + tick + .5, tick + .5, marker='x', color='k')
            ax.text(-n + tick + .5, tick + .5, label,
                    horizontalalignment='right', rotation=-rotation)
    if yticks is not None:
        yticklabels = yticks if yticklabels is None else yticklabels
        for tick, label, in zip(yticks, yticklabels):
            ax.scatter(tick + .5, n - tick - .5, marker='x', color='k')
            ax.text(tick + .5, n - tick - .5, label,
                    horizontalalignment='left', rotation=rotation)

    if aspect:
        ax.set_aspect(aspect)
    ax.set_xlim(-n, n)
    ax.set_ylim(-n, n)
    ax.plot([-n, 0, n, 0., -n], [0, n, 0, -n, 0], color='k')
    ax.axis('off')
    return ax
kingjr
  • 173
  • 1
  • 7
1

Perhaps if you do it on a 3D plot?

http://matplotlib.1069221.n5.nabble.com/How-to-rotate-a-3D-plot-td19185.html

axes3d.view_init(elev, azim)

altendky
  • 4,176
  • 4
  • 29
  • 39
  • That is possible, although then I have to use mplot3d's plotting functions - hopefully they will have the same capabilities that pyplot functions have. Thanks. – Bitwise Oct 11 '12 at 21:30
  • Looking at mplot3d, I see that it is much more limited in its 2d plotting than pyplot. So I prefer a solution that manipulates my plot made by pyplot. – Bitwise Oct 12 '12 at 00:41
  • Just curious... why does it need to be rotated? I feel like there's some data visualization technique here for me to learn. :] – altendky Oct 12 '12 at 01:26
  • 1
    altendky - I have a dataset which gives a value for every pair of positions on some vector. This is visualized as a symmetric matrix (or a triangular matrix), and the most natural way to orient this matrix would be rotated by 45 degrees, so that the diagonal is horizontal. It is a common way to display this type of data. – Bitwise Oct 12 '12 at 01:30
0

This post suggests that you can only do it "by hand".

It's possible to draw it, but you would have to do all the transformations/rotations by hand, including drawing the axes as Line2D instances and labels as Text instances (see for example the "Scatter3D" example in the archives). There is no easy, built-in, way to do it, currently. In the planned refactoring of the axis handling,

parsley72
  • 8,449
  • 8
  • 65
  • 98
root
  • 76,608
  • 25
  • 108
  • 120
  • Thanks, although that post is from 2005 so there is a chance that in the 7+ years since then there have been changes... – Bitwise Oct 11 '12 at 21:19
  • @ i agree, I hope you find something, i would be happy to be wrong :) – root Oct 11 '12 at 21:22
0

Have you looked at PIL?

This code will rotate an image. So if you first output the plot to a file as an image, you could then do

import Image
img = Image.open("plot.jpg")
img2 = img.rotate(45)
img2.show()
img2.save("rotate.jpg")
mrchampe
  • 452
  • 3
  • 12