[FIXED] Wie mache ich die Breite des Titelfelds über die gesamte Handlung?

Ausgabe

Betrachten Sie die folgende Pandas-Serie sund Handlung

import pandas as pd
import numpy as np

s = pd.Series(np.random.lognormal(.001, .01, 100))
ax = s.cumprod().plot()
ax.set_title('My Log Normal Example', position=(.5, 1.02),
             backgroundcolor='black', color='white')

Geben Sie hier die Bildbeschreibung ein

Wie bekomme ich die Box, die den Titel enthält, um die gesamte Handlung zu überspannen?

Lösung

Es ist natürlich möglich, den Begrenzungsrahmen des Titels zu erhalten, der ein TextElement ist. Dies kann mit erfolgen

title = ax.set_title(...) 
bb = title.get_bbox_patch() 

Im Prinzip kann man dann die Begrenzungsbox manipulieren, zB über
bb.set_width(...). Allerdings gehen alle Einstellungen verloren, sobald matplotlib den Titel auf die Leinwand zeichnet. So interpretiere ich zumindest die TextMethode von draw().

Mir sind keine anderen Methoden zum Festlegen des Begrenzungsrahmens bekannt. Zum Beispiel legendkann der Begrenzungsrahmen von a so eingestellt werden
plt.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=3, mode="expand"), dass er sich über den gesamten Achsenbereich ausdehnt (siehe hier ). Es wäre sehr nützlich, die gleiche Option auch für zu haben Text. Aber im Moment tun wir das nicht.

Das TextObjekt ermöglicht das Festlegen eines bboxArguments, das normalerweise zum Festlegen des Stils des Begrenzungsrahmens gedacht ist. Es gibt keine Möglichkeit, die Grenzen des Begrenzungsrahmens festzulegen, aber es akzeptiert ein Wörterbuch der Eigenschaften des umgebenden Rahmens. Und eine der akzeptierten Eigenschaften ist eine boxstyle. Standardmäßig ist dies ein square, kann aber auf einen Kreis oder Pfeil oder andere seltsame Formen eingestellt werden.

Diese boxstylesind eigentlich der Schlüssel zu einer möglichen Lösung. Sie alle erben von BoxStyle._Baseund – wie am Ende des Anmerkungsleitfadens zu sehen ist – man kann eine benutzerdefinierte Form definieren, indem man Unterklassen bildet BoxStyle._Base.

Die folgende Lösung basiert auf der Unterklassenbildung BoxStyle._Basein der Weise, dass sie die Breite der Achsen als Argument akzeptiert und den Rechteckpfad des Titels so zeichnet, dass er genau diese Breite hat.

Als Bonus können wir einen Event-Handler registrieren, der diese Breite anpasst, sobald sie sich aufgrund einer Größenänderung des Fensters ändert.

Hier ist der Code:

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

from matplotlib.path import Path
from matplotlib.patches import BoxStyle


class ExtendedTextBox(BoxStyle._Base):
    """
    An Extended Text Box that expands to the axes limits 
                        if set in the middle of the axes
    """

    def __init__(self, pad=0.3, width=500.):
        """
        width: 
            width of the textbox. 
            Use `ax.get_window_extent().width` 
                   to get the width of the axes.
        pad: 
            amount of padding (in vertical direction only)
        """
        self.width=width
        self.pad = pad
        super(ExtendedTextBox, self).__init__()

    def transmute(self, x0, y0, width, height, mutation_size):
        """
        x0 and y0 are the lower left corner of original text box
        They are set automatically by matplotlib
        """
        # padding
        pad = mutation_size * self.pad

        # we add the padding only to the box height
        height = height + 2.*pad
        # boundary of the padded box
        y0 = y0 - pad
        y1 = y0 + height
        _x0 = x0
        x0 = _x0 +width /2. - self.width/2.
        x1 = _x0 +width /2. + self.width/2.

        cp = [(x0, y0),
              (x1, y0), (x1, y1), (x0, y1),
              (x0, y0)]

        com = [Path.MOVETO,
               Path.LINETO, Path.LINETO, Path.LINETO,
               Path.CLOSEPOLY]

        path = Path(cp, com)

        return path

dpi = 80

# register the custom style
BoxStyle._style_list["ext"] = ExtendedTextBox

plt.figure(dpi=dpi)
s = pd.Series(np.random.lognormal(.001, .01, 100))
ax = s.cumprod().plot()
# set the title position to the horizontal center (0.5) of the axes
title = ax.set_title('My Log Normal Example', position=(.5, 1.02), 
             backgroundcolor='black', color='white')
# set the box style of the title text box toour custom box
bb = title.get_bbox_patch()
# use the axes' width as width of the text box
bb.set_boxstyle("ext", pad=0.4, width=ax.get_window_extent().width )


# Optionally: use eventhandler to resize the title box, in case the window is resized
def on_resize(event):
    print "resize"
    bb.set_boxstyle("ext", pad=0.4, width=ax.get_window_extent().width )

cid = plt.gcf().canvas.mpl_connect('resize_event', on_resize)

# use the same dpi for saving to file as for plotting on screen
plt.savefig(__file__+".png", dpi=dpi)
plt.show()

Geben Sie hier die Bildbeschreibung ein


Für den Fall, dass jemand an einer leichteren Lösung interessiert ist, besteht auch die Möglichkeit, mit dem mutation_aspectBegrenzungsrahmen des Titels herumzuspielen, der beim Zeichnen des Titels anscheinend unverändert bleibt. Während das mutation_aspectselbst im Grunde nur die Höhe der Box verändert, kann man extrem große Paddings für die Box verwenden und mutation_aspectauf eine sehr kleine Zahl setzen, so dass die Box am Ende in der Breite erweitert erscheint. Der klare Nachteil dieser Lösung ist, dass die Werte für Padding und Aspekt durch Versuch und Irrtum gefunden werden müssen und sich für unterschiedliche Schrift- und Zifferngrößen ändern. Bei mir bringen die Werte von mutation_aspect = 0.04und pad=11.9das gewünschte Ergebnis, aber auf anderen Systemen können sie natürlich anders sein.

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

s = pd.Series(np.random.lognormal(.001, .01, 100))
ax = s.cumprod().plot()
title = ax.set_title('My Log Normal Example', position=(.5, 1.02),
             backgroundcolor='black', color='white',
             verticalalignment="bottom", horizontalalignment="center")
title._bbox_patch._mutation_aspect = 0.04
title.get_bbox_patch().set_boxstyle("square", pad=11.9)
plt.tight_layout()
plt.savefig(__file__+".png")
plt.show()


Beantwortet von –
ImportanceOfBeingErnest


Antwort geprüft von –
Candace Johnson (FixError Volunteer)

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like