【问题标题】:How to modify matplotlib legend after it has been created?matplotlib图例创建后如何修改?
【发布时间】:2014-07-04 13:53:41
【问题描述】:

我可以访问图形实例fig = pylab.gcf()。我知道在这个图中有一个图例,我可以通过myLegend = fig.gca().legend_ 访问它。现在我想更改图例的属性。其中一些我可以通过像myLegend.set_frame_on(True) 这样的设置器访问。

创建图例时,它接受多个关键字参数:

class matplotlib.legend.Legend(父、句柄、标签、loc=None, numpoints=None,markerscale=None,scatterpoints=None, scatteryoffsets=None,prop=None,fontsize=None,borderpad=None, 标签间距=无,句柄长度=无,句柄高度=无, handletextpad=None,borderaxespad=None,columnspacing=None,ncol=1, mode=None,fancybox=None,shadow=None,title=None,framealpha=None, bbox_to_anchor=无,bbox_transform=无,frameon=无, handler_map=无)

在创建图例之后如何修改图例中的所有关键字参数?

其中一个有问题的是numpoints(图例中的标记数,默认为2)。下面是我想如何改变它的例子:

这显示了我想如何编程

import pylab
pylab.plot(0,0,'ro', label = 'one point')
pylab.legend(loc = "lower left")
# no modifications above this line
setattr(pylab.gcf().gca().legend_, 'numpoints',1)
pylab.show()

这显示了我想要的样子

import pylab
pylab.plot(0,0,'ro', label = 'one point')
pylab.legend(numpoints = 1, loc = "lower left")
pylab.show()

我查了源代码,有一个numpoint变量被改变了,但是大写没有更新到屏幕上。我错过了什么?

【问题讨论】:

  • 我怀疑你可以伸手去戳处理程序。

标签: python matplotlib legend


【解决方案1】:

您在图例中看到的实际上是Line2D。在创建该行后更改 numpoints 不会更新该行,因此您必须获取 Line2D 对象的句柄并手动删除其中一个点:

import pylab
pylab.plot(0,0,'ro', label = 'one point')
legend = pylab.legend(loc = "lower left")
markers = legend.get_children()[0].get_children()[1].get_children()[0].get_children()[0].get_children()[0].get_children()[1]
markers.set_data(map(pylab.mean, markers.get_data()))
pylab.show()

get_children() 链是必需的,因为 matplotlib 将该行包装在几层水平和垂直包中。上面的 sn-p 应该足以为您提供一般概念,但在实际应用程序中,获取句柄的更好方法是遵循 the legend guide's hint on legend handlers 并使用自定义的 HandlerLine2D 以某种方式存储该行.

【讨论】:

    【解决方案2】:

    您可以使用正确的关键字/参数再次使用命令pylab.legend。这将修改现有图例,而不是创建一个新图例。您可以在下面找到您的示例,稍作修改。

    import pylab
    pylab.plot(0,0,'ro', label = 'one point')
    pylab.legend(loc = "lower left")
    # Change the number of markers shown in the legend
    pylab.legend(numpoints = 1, loc = "lower left")
    
    pylab.show()
    

    希望对你有帮助。

    【讨论】:

    • 这是个好方法。但也有一些改进。例如,您丢失了瓷砖。可以按照 letit = ax.get_legend().get_title().get_text() ax.legend(shadow=True, facecolor=(0.7,0.75,0.7,0.8)) ax.get_legend().set_title(letit )
    【解决方案3】:

    我编写了一个函数modify_legend,它会在创建图例后对其进行修改。它基本上从已经创建的图例中读取所有参数,使用您提供的键值参数对其进行更新,然后再次使用所有可能的参数调用legend(...)

    您的问题将通过以下方式解决:

    import pylab
    pylab.plot(0,0,'ro', label = 'one point')
    pylab.legend(loc = "lower left")
    
    modify_legend(numpoints = 1)
    
    pylab.show()
    

    这是modify_legend的代码:

    def modify_legend(**kwargs):
        import matplotlib as mpl
    
        l = mpl.pyplot.gca().legend_
    
        defaults = dict(
            loc = l._loc,
            numpoints = l.numpoints,
            markerscale = l.markerscale,
            scatterpoints = l.scatterpoints,
            scatteryoffsets = l._scatteryoffsets,
            prop = l.prop,
            # fontsize = None,
            borderpad = l.borderpad,
            labelspacing = l.labelspacing,
            handlelength = l.handlelength,
            handleheight = l.handleheight,
            handletextpad = l.handletextpad,
            borderaxespad = l.borderaxespad,
            columnspacing = l.columnspacing,
            ncol = l._ncol,
            mode = l._mode,
            fancybox = type(l.legendPatch.get_boxstyle())==mpl.patches.BoxStyle.Round,
            shadow = l.shadow,
            title = l.get_title().get_text() if l._legend_title_box.get_visible() else None,
            framealpha = l.get_frame().get_alpha(),
            bbox_to_anchor = l.get_bbox_to_anchor()._bbox,
            bbox_transform = l.get_bbox_to_anchor()._transform,
            frameon = l._drawFrame,
            handler_map = l._custom_handler_map,
        )
    
        if "fontsize" in kwargs and "prop" not in kwargs:
            defaults["prop"].set_size(kwargs["fontsize"])
    
        mpl.pyplot.legend(**dict(defaults.items() + kwargs.items()))
    

    代码注释:

    • 一些参数可以很容易地从Legend 对象中读取,其他参数(如titlefancybox)需要一些“艺术”。您可以查看matplotlib.legend.Legend.__init__ 了解它是如何以及为什么这样做的。
    • fontsize 参数上的额外条件用于在最初使用prop 创建图例时覆盖字体大小,因为prop 通常会覆盖fontsize
    • 我没有测试所有案例,因为我没有太多时间(尤其是bbox_to_anchorbbox_transform-参数),所以请随意尝试和改进代码:)

    【讨论】:

    • 这太好了,谢谢。我不得不评论 #handler_map = l._custom_handler_map, 行,因为它在我的图例中不存在(python 2.7.8,mpl 1.3.1)。
    • 如果我没记错的话,属性_custom_handler_map 被称为_handler_map before matplotlib version 1.4.0。因此,如果您在注释行遇到问题,请尝试更改属性名称而不是 :)
    • 但是如何更改文本?
    【解决方案4】:

    如果是我,我会把它放到另一个文本文件中,因为这样更容易更改和跟踪,特别是如果你在此之前和之后有很多代码。

    要打开一个文件进行写入,我们将第二个参数设置为“w”而不是“r”。(fobj = open("ad_lesbiam.txt", "r"))要实际将数据写入该文件,我们使用文件句柄的 write() 方法目的。

    让我们从一个非常简单直接的例子开始:

    fh = open("example.txt", "w")
    fh.write("To write or not to write\nthat is the question!\n")
    fh.close()
    

    尤其是当您正在写入文件时,您永远不应忘记再次关闭文件句柄。否则,您将面临数据处于不一致状态的风险。

    您经常会发现用于读写文件的 with 语句。好处是with执行完后缩进块后文件会自动关闭:

    with open("example.txt", "w") as fh:
        fh.write("To write or not to write\nthat is the question!\n")
    

    我们的第一个例子也可以用 with 语句改写成这样:

    with open("ad_lesbiam.txt") as fobj:
        for line in fobj:
            print(line.rstrip())
    

    同时读写示例:

    fobj_in = open("ad_lesbiam.txt")
    fobj_out = open("ad_lesbiam2.txt","w")
    i = 1
    for line in fobj_in:
        print(line.rstrip())
        fobj_out.write(str(i) + ": " + line)
        i = i + 1
    fobj_in.close()
    fobj_out.close()
    

    仅供参考。输入文本文件的每一行都以其行号为前缀

    【讨论】:

    • mmmh 这与问题有什么关系?
    猜你喜欢
    • 2020-04-28
    • 2020-01-24
    • 2011-02-08
    • 2021-12-13
    • 2020-03-27
    • 1970-01-01
    • 2011-08-13
    • 1970-01-01
    相关资源
    最近更新 更多