Python Matplotlib图例垂直旋转

Python Matplotlib图例垂直旋转,python,matplotlib,Python,Matplotlib,是否有人知道是否可以在matplotlib中旋转绘图上的图例?我用下面的代码做了一个简单的绘图,并用画图的方式编辑了这个图来显示我想要的东西 plt.plot([4,5,6], label = 'test') ax = plt.gca() ax.legend() plt.show() 我昨天花了几个小时在这方面做了一些改进,并取得了一些进展,因此我将在下面与大家分享这一点以及一些前进的建议 首先,我们似乎可以围绕图例旋转和平移边界框(bbox)或框架。在下面的第一个示例中,您可以看到可以应用变

是否有人知道是否可以在
matplotlib
中旋转绘图上的图例?我用下面的代码做了一个简单的绘图,并用画图的方式编辑了这个图来显示我想要的东西

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

我昨天花了几个小时在这方面做了一些改进,并取得了一些进展,因此我将在下面与大家分享这一点以及一些前进的建议

首先,我们似乎可以围绕图例旋转和平移边界框(bbox)或框架。在下面的第一个示例中,您可以看到可以应用
变换
,尽管在应用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

接下来,我认为探索其他legend组件的
get\u方法()
是明智的。你可以用
dir(legend)
legend.\uuu dict\uuuu
等等来挖掘这些东西。特别是,我注意到您可以这样做:
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) 

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

我在这类方法中遇到了类似的技术困难:

bbox = matplotlib.transforms.Bbox([[0.,1],[1,1]])
trans_bbox = matplotlib.transforms.TransformedBbox(bbox, transform)
legend.set_bbox_to_anchor(trans_bbox)
其他说明和建议:

  • 深入研究图例标题和框架对象之间的行为差异可能是一个明智的想法——为什么它们都接受变换,但只有框架接受旋转?也许可以在源代码中对legend对象进行子类化并进行一些调整
  • 我们还需要找到一种解决方案,以解决旋转/平移图例框架无法保存到输出的问题,即使在遵循了各种相关建议之后(即)

  • 我遇到了一个类似的问题,并通过编写函数
    legendasletex
    解决了这个问题,该函数生成一个latex代码,用作y轴的标签。该函数收集提供给plot函数的颜色、标记、线条样式和标签。它需要启用latex并加载所需的包。以下是使用两个垂直轴生成带有额外曲线的绘图的代码

    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()
    
    从matplotlib导入pyplot作为plt
    将matplotlib.Color导入为cor
    plt.rc('text',usetex=True)
    plt.rc('text.latex',premission=r'\usepackage{amsmath}\usepackage{wasysym}'+
    r'\usepack[dvipsnames]{xcolor}\usepack{MnSymbol}\usepack{txfonts}')
    def legendAsLatex(轴,旋转=90):
    ''生成一个latex代码以代替图例。
    使用提供给pyplot.plot的标签、颜色、标记和线型。
    必须使用一个或两个字符定义标记和线型
    在pyplot.plot的帮助下显示的缩写。
    标记的旋转必须是90的倍数。
    '''
    latexLine={'-':'\\textbf{\Large---}',
    “-”:“\\textbf{\Large--\:\!$\\boldsymbol{\cdot}$\:\!--}”,
    “——”:“\\textbf{\Large--\,--},”:“:”\\textbf{\Large--\:\!-}”
    latexSymbol={'o':'medbill','d':'diamond','s':'filledmedsquare',
    'D':'Diamondblack','*':'bigstar','+':'boldsymbol{\plus}',
    'x':'boldsymbol{\\times}','p':'pentagon','h':'hexagon',
    “,”:“boldsymbol{\cdot}',“\u':”boldsymbol{\minus}',“:”RHD“,”v“:”blacktriangledown“,”^“:”blacktriangle“}”
    rot90=['^','']
    di=[0,-1,2,1][旋转%360//90]
    latexSymbol.update({rot90[i]:范围(4)中i的latexSymbol[rot90[(i+di)%4]]
    返回“,”.join(['\\textcolor[rgb]{'\
    +“,”.join([str(x)for x in cor.to_rgb(handle.get_color())]))+“}{”
    +“$\\”+latexSymbol.get(handle.get\u marker(),“;”)+“$”
    +latexLine.get(handle.get_linestyle(),'')+'}'+标签
    对于句柄,在zip中添加标签(*axes.get_legend_handles_labels()))
    ax=plt.axs()
    ax.绘图(范围(0,10),“b-”,标签=“蓝线”)
    ax.绘图(范围(10,0,-1),“sm”,标签=“洋红色正方形”)
    ax.set_ylabel(legendasletex(ax))
    ax2=plt.twinx()
    ax2.绘图([x**0.5表示范围(0,10)内的x)],“ro”,标签=“红圈”)
    ax2.绘图([x**0.5表示范围(10,0,-1)内的x)],'g--',标签='绿色虚线')
    ax2.集合标签(legendAsLatex(ax2))
    plt.savefig('legend.eps'))
    plt.close()
    
    由代码生成的图:


    matplotlib邮件列表中有一个问题,答案是,当前(2009年)的legend()实现无法实现。在使用更新的matplotlib版本时,我从未遇到过这样的选项,也许这仍然是不可能的。谢谢Chris-我找到了相同的线程,应该在我的原始帖子中包含这一点。感谢探索这一点!这太棒了。最后,我用了一个类似但不太通用的代码来处理我的数字。“我会把你的一些想法融入到我的函数中。谢谢你,”MadPhysicast。你可能想检查我刚刚上传的版本。它更简单,正确处理颜色并接受更多旋转角度。不确定这是否重要,但你可以
    rot90
    进入一个由
    rotation%360
    键入的dict,而不是通过怪物
    if
    。好主意。根据你的建议,我去掉了di定义中的ifs。现在干净多了。