Matplotlib图例垂直旋转

时间:2015-08-14 14:09:40

标签: python matplotlib

是否有人知道是否可以在matplotlib的地块上旋转图例?我使用下面的代码制作了一个简单的图,并在绘画中编辑了图表以显示我想要的内容。

plt.plot([4,5,6], label = 'test')
ax = plt.gca()
ax.legend()
plt.show()

http://www.abraham.dreamhosters.com/Capture.PNG

2 个答案:

答案 0 :(得分:2)

我遇到了类似的问题并通过编写函数legendAsLatex来解决它,该函数生成一个乳胶代码,用作y轴的标签。该功能收集提供给绘图功能的颜色,标记,线条样式和标签。它需要启用乳胶并装入所需的包装。下面是使用两个垂直轴的额外曲线生成绘图的代码。

from matplotlib import pyplot as plt
import matplotlib.colors as cor

plt.rc('text', usetex=True)
plt.rc('text.latex', preamble=r'\usepackage{amsmath} \usepackage{wasysym}'+
    r'\usepackage[dvipsnames]{xcolor} \usepackage{MnSymbol}  \usepackage{txfonts}')

def legendAsLatex(axes, rotation=90) :
    '''Generate a latex code to be used instead of the legend. 
       Uses the label, color, marker and linestyle provided to the pyplot.plot.
       The marker and the linestyle must be defined using the one or two character
           abreviations shown in the help of pyplot.plot.
       Rotation of the markers must be multiple of 90.
    '''
    latexLine = {'-':'\\textbf{\Large ---}',
        '-.':'\\textbf{\Large --\:\!$\\boldsymbol{\cdot}$\:\!--}',
        '--':'\\textbf{\Large --\,--}',':':'\\textbf{\Large -\:\!-}'}
    latexSymbol = {'o':'medbullet', 'd':'diamond', 's':'filledmedsquare',
        'D':'Diamondblack', '*':'bigstar', '+':'boldsymbol{\plus}',
        'x':'boldsymbol{\\times}', 'p':'pentagon', 'h':'hexagon',
        ',':'boldsymbol{\cdot}', '_':'boldsymbol{\minus}','<':'LHD',
        '>':'RHD','v':'blacktriangledown', '^':'blacktriangle'} 
    rot90=['^','<','v','>']
    di = [0,-1,2,1][rotation%360//90]
    latexSymbol.update({rot90[i]:latexSymbol[rot90[(i+di)%4]] for i in range(4)})
    return ', '.join(['\\textcolor[rgb]{'\
            + ','.join([str(x) for x in cor.to_rgb(handle.get_color())]) +'}{'
            + '$\\'+latexSymbol.get(handle.get_marker(),';')+'$'
            + latexLine.get(handle.get_linestyle(),'') + '} ' + label 
                for handle,label in zip(*axes.get_legend_handles_labels())])

ax = plt.axes()
ax.plot(range(0,10), 'b-', label = 'Blue line')
ax.plot(range(10,0,-1), 'sm', label = 'Magenta squares')
ax.set_ylabel(legendAsLatex(ax))

ax2 = plt.twinx()
ax2.plot([x**0.5 for x in range(0,10)],  'ro', label = 'Red circles')
ax2.plot([x**0.5 for x in range(10,0,-1)],'g--', label = 'Green dashed line')
ax2.set_ylabel(legendAsLatex(ax2)) 

plt.savefig('legend.eps')

plt.close()

代码生成的图:

enter image description here

答案 1 :(得分:1)

昨天我花了几个小时来讨论这个问题并取得了一些进展,所以我将分享下面的内容以及一些前进的建议。

首先,我们似乎可以旋转和翻译图例周围的边界框(bbox)或框架。在下面的第一个示例中,您可以看到可以应用transform,尽管在应用90度旋转后需要一些奇怪的大翻译数。但是,实际上存在将翻译的图例框架保存到图像文件的问题,因此我不得不从IPython笔记本中截取屏幕截图。我也添加了一些评论。

import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import matplotlib.transforms

fig = plt.figure()
ax = fig.add_subplot('121') #make room for second subplot, where we are actually placing the legend
ax2 = fig.add_subplot('122') #blank subplot to make space for legend
ax2.axis('off')
ax.plot([4,5,6], label = 'test')

transform = matplotlib.transforms.Affine2D(matrix=np.eye(3)) #start with the identity transform, which does nothing
transform.rotate_deg(90) #add the desired 90 degree rotation
transform.translate(410,11) #for some reason we need to play with some pretty extreme translation values to position the rotated legend

legend = ax.legend(bbox_to_anchor=[1.5,1.0])
legend.set_title('test title')
legend.get_frame().set_transform(transform) #This actually works! But, only for the frame of the legend (see below)
frame = legend.get_frame()
fig.subplots_adjust(wspace = 0.4, right = 0.9)
fig.savefig('rotate_legend_1.png',bbox_extra_artists=(legend,frame),bbox_inches='tight', dpi = 300) #even with the extra bbox parameters the legend frame is still getting clipped

enter image description here

接下来,我认为探索get_methods()其他图例组件会很聪明。您可以使用dir(legend)legend.__dict__等来挖掘这些内容。特别是,我注意到你可以这样做:legend.get_title().set_transform(transform),这似乎意味着我们可以翻译图例文本(而不仅仅是上面的框架)。让我们看看当我尝试时会发生什么:

fig2 = plt.figure()
ax = fig2.add_subplot('121') 
ax2 = fig2.add_subplot('122')
ax2.axis('off')
ax.plot([4,5,6], label = 'test')

transform = matplotlib.transforms.Affine2D(matrix=np.eye(3)) 
transform.rotate_deg(90) 
transform.translate(410,11) 

legend = ax.legend(bbox_to_anchor=[1.5,1.0])
legend.set_title('test title')
legend.get_frame().set_transform(transform) 
legend.get_title().set_transform(transform) #one would expect this to apply the same transformation to the title text in the legend, rotating it 90 degrees and translating it

frame = legend.get_frame()
fig2.subplots_adjust(wspace = 0.4, right = 0.9)
fig2.savefig('rotate_legend_1.png',bbox_extra_artists=(legend,frame),bbox_inches='tight', dpi = 300) 

enter image description here

传说标题似乎已经从IPython笔记本的屏幕截图中消失了。但是,如果我们查看保存的文件,图例标题现在位于左下角,似乎忽略了转换的旋转组件(为什么?):

enter image description here

我对这种方法有类似的技术困难:

bbox = matplotlib.transforms.Bbox([[0.,1],[1,1]])
trans_bbox = matplotlib.transforms.TransformedBbox(bbox, transform)
legend.set_bbox_to_anchor(trans_bbox)

其他说明和建议:

  1. 挖掘图例标题和框架对象之间的行为差​​异可能是一个明智的想法 - 为什么它们都接受变换,但只有框架接受旋转?也许可以在源代码中对图例对象进行子类化并进行一些调整。
  2. 我们还需要找到一个解决方案,即使在SO上遵循各种相关建议(即Matplotlib savefig with a legend outside the plot)之后,旋转/翻译的图例框架也不会保存到输出中。