Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何使标题框的宽度跨越整个绘图?_Python_Pandas_Matplotlib - Fatal编程技术网

Python 如何使标题框的宽度跨越整个绘图?

Python 如何使标题框的宽度跨越整个绘图?,python,pandas,matplotlib,Python,Pandas,Matplotlib,考虑以下熊猫系列s并绘制 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') 如何使包含标题的

考虑以下熊猫系列
s
并绘制

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')


如何使包含标题的框跨越整个绘图?

不必缩放标题文本本身的边界框,您可以在主轴上方创建一个次轴,并将其用作标题的“框”。由于轴通常看起来不像框,我们将关闭其轴标签和记号,并将背景颜色设置为黑色以匹配OP

我用同样的方法来做一个二次匹配轴

此外,我还使用了
AnchoredText
将标题文本捕捉到轴上,这样就可以轻松地将其定位到轴的中心

import matplotlib.pyplot as plt 
from matplotlib.offsetbox import AnchoredText
from mpl_toolkits.axes_grid1 import make_axes_locatable
import pandas as pd
import numpy as np

s = pd.Series(np.random.lognormal(.001, .01, 100))
ax = s.cumprod().plot()

divider = make_axes_locatable(ax)
cax = divider.append_axes("top", size="11%", pad=0)
cax.get_xaxis().set_visible(False)
cax.get_yaxis().set_visible(False)
cax.set_facecolor('black')

at = AnchoredText("My Log Normal Example", loc=10,
                  prop=dict(backgroundcolor='black',
                            size=12, color='white'))
cax.add_artist(at)

plt.show()


编辑:对于较旧的
matplotlib
版本,您可能需要切换到
cax。设置背景颜色时,设置轴颜色(“黑色”)

当然,可以获得标题的边界框,它是
文本
元素。这可以通过

title = ax.set_title(...) 
bb = title.get_bbox_patch() 
原则上,可以操纵边界框,例如通过
bb.设置宽度(…)
。但是,一旦matplotlib将标题绘制到画布上,所有设置都将丢失。至少我是这样解释
Text
draw()
方法的

我不知道其他设置边界框的方法。例如,可以通过
plt.legend(bbox_至_锚点=(0,1.02,1,0.102),loc=3,mode=“expand”)
以使其在整个轴范围内扩展(请参阅)。对于
文本
也使用相同的选项将非常有用。但到目前为止,我们没有

Text
对象允许设置参数,该参数通常用于设置边界框的样式。无法设置边界框范围,但它接受周围框的一些属性字典。其中一个可接受的属性是。默认情况下,这是一个正方形,但可以设置为圆形、箭头或其他奇怪的形状

这些
boxstyle
s实际上是可能的解决方案的关键。它们都继承自
BoxStyle.\u Base
,并且可以定义自定义形状,对
BoxStyle.\u Base
进行子类化

以下解决方案基于子类化
BoxStyle.\u Base
,它接受轴的宽度作为参数,并绘制标题的矩形路径,使其正好具有此宽度。

作为奖励,我们可以注册一个事件处理程序,这样一旦宽度因窗口大小的改变而改变,它就会被调整

代码如下:

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()


为了防止有人对更轻松的解决方案感兴趣,还可以选择使用标题边界框的
mutation\u方面
,在绘制标题时显然保持不变。虽然
mutation\u aspect
本身基本上只改变框的高度,但可以对框使用非常大的填充,并将
mutation\u aspect
设置为一个非常小的数字,这样框在结尾时会出现宽度扩展。这种解决方案的明显缺点是,填充和纵横比的值必须通过反复试验才能找到,并且会随着字体和图形大小的不同而变化。 在我的例子中,
mutation\u aspect=0.04
pad=11.9
的值产生了期望的结果,但在其他系统上,它们当然可能不同

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()

我唯一能想到的就是手动设置。比如说,你的
figsize=(9,7)
,然后你的标题大小,手动设置:
size=40.5
。但是,我也很想知道是否还有其他方法。你说,“不要缩放标题文本本身的边框…”。您知道通过设置
文本的边界框来实际解决此问题的方法吗。这个选项真的存在吗?我想人们必须修补边界框的填充。问题在于,它是由一个标量值在所有方向上均匀缩放的。也许有一种方法可以只在水平方向设置大的填充(看起来很有希望),但我还没有研究过。嗯。。。没有人能给我一个更好的答案,这是一个很好的答案。我不知道赏金今天结束了,好的解决方案需要一段时间才能输入。;-)这是一个很好的回答。我开始了一个新的悬赏。几乎可以肯定,24小时后它将是你的。除非有人提出更好的答案(我对此表示怀疑,但这是可能的)。