2016-11-25 91 views
11

考慮以下事項熊貓系列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') 

enter image description here

如何獲取包含標題橫跨整個小區的盒子?

+0

我唯一能想到的就是手動設置。比方說,你的'figsize =(9,7)',然後你的標題大小,手動設置:'size = 40.5'。但是,我也有興趣瞭解是否有其他方式。 –

回答

14

當然可以得到標題的邊界框,這是一個Text元素。這可以用

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

原則上,可以操縱邊界框,例如,通過 bb.set_width(...)。但是,一旦matplotlib將標題繪製到畫布上,所有設置都將丟失。至少這是我如何解釋Textdraw()方法。

我不知道設置邊界框的其他方法。例如,legend的邊界框可通過
plt.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=3, mode="expand")進行設置,使其在整個軸範圍內擴展(請參閱here)。對Text也有相同的選項會非常有用。但就目前而言,我們並沒有。

Text對象允許設置bbox參數,該參數通常用於設置邊界框的樣式。沒有辦法設置邊界框範圍,但它接受周圍框屬性的一些字典。其中一個被接受的屬性是boxstyle。默認情況下,這是一個square,但可以設置爲圓形或箭頭或其他奇怪的形狀。

那些boxstyle實際上是可能的解決方案的關鍵。它們都從BoxStyle._Base繼承,並且 - 可以在the bottom of the annotations guide處看到 - 可以定義自定義形狀,子類別爲BoxStyle._Base

以下解決方案基於子類化BoxStyle._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() 

enter image description here


萬一有人有興趣在一個輕量級的解決方案,也有與標題的邊界的mutation_aspect發揮各地的選項在繪製標題時顯然保持不變。雖然mutation_aspect本身基本上只是改變了盒子的高度,但可以使用非常大的填充來填充盒子,並將mutation_aspect設置爲非常小的數字,從而最終盒子看起來在寬度上延伸。這個解決方案的明顯缺點是,填充和方面的值必須通過試驗和錯誤找到,並且會因不同的字體和圖形大小而發生變化。 在我的情況下,mutation_aspect = 0.04pad=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() 
+0

我不知道賞金今天結束,需要一段時間才能輸入好的解決方案。;-) – ImportanceOfBeingErnest

+0

這是一個很好的答案。 – piRSquared

+0

我開始了新的賞金。這將是你的,幾乎可以肯定的,在24小時內。除非別人有更好的答案(我懷疑它,但可能)。 – piRSquared

11

除了縮放標題文本本身的邊界框外,還可以在主要文本的上方創建輔助軸,並將其用作標題的「框」。由於軸通常看起來不像框,我們將關閉其軸標籤和刻度,並將背景顏色設置爲黑色以匹配OP。

我正在使用相同的方法來製作第二個匹配軸,如here

此外,我使用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() 

enter image description here

編輯:用於設置背景顏色時,你可能需要切換到舊的cax.set_axis_bgcolor('black')版本matplotlib

+0

你說過,「不是縮放標題文本本身的邊界框......」。您是否知道通過設置「文本」的邊界框來真正解決此問題的方法。這個選項是否存在? – ImportanceOfBeingErnest

+0

我想象一個人不得不修補邊框的填充。問題在於它在所有方向上均勻地按標量值縮放。可能有一種方法只在水平方向上設置大的填充([this](http://stackoverflow.com/questions/35249489/side-specific-padding-for-matplotlib-text-bbox)看起來很有希望),但我沒有看過它。 –

+0

呃...沒有人能給我更好的答案,這是一個很好的答案。 – piRSquared

相關問題