亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Squarify - 自動調整樹狀圖中標簽的大小

Squarify - 自動調整樹狀圖中標簽的大小

臨摹微笑 2023-08-03 16:37:00
我正在使用Squarify在 Python 中實現一個簡單的樹形圖。我正在繪制藝術家姓名及其在所考慮的歌曲圖表中的流百分比,正方形越大/越暗,值越高。我的代碼如下:dataGoals = sort_by_streams[sort_by_streams["Streams"]>1]#Utilise matplotlib to scale our stream number between the min and max, then assign this scale to our values.norm = matplotlib.colors.Normalize(vmin=min(dataGoals.Streams), vmax=max(dataGoals.Streams))colors = [matplotlib.cm.Blues(norm(value)) for value in dataGoals.Streams]#Create our plot and resize it.fig1 = plt.figure()ax = fig1.add_subplot()fig1.set_size_inches(16, 4.5)#Use squarify to plot our data, label it and add colours. We add an alpha layer to ensure black labels show throughlabels = ["%s\n%.2f" % (label) for label in zip(dataGoals.Artist, dataGoals.Streams)]squarify.plot(label=labels,sizes=dataGoals.Streams, color = colors, alpha=.7, bar_kwargs=dict(linewidth=0.5, edgecolor="#222222"),text_kwargs={'fontsize':15})plt.title("Streams Percentage",fontsize=23,fontweight="bold")#Remove our axes and display the plotplt.axis('off')plt.show()這是結果:您可能會注意到,較小方塊的標簽重疊并超出邊界。有沒有辦法自動調整標簽大小以適應正方形?編輯:我嘗試使用以下代碼實現 matplotlib 的自動換行功能:squarify.plot(label=labels,sizes=dataGoals.Streams, color = colors, alpha=.7, bar_kwargs=dict(linewidth=0.5, edgecolor="#222222"),text_kwargs={'fontsize':20, 'wrap':True})但這并不能解決我的問題,我的文本標簽仍然超出范圍。
查看完整描述

1 回答

?
慕妹3242003

TA貢獻1824條經驗 獲得超6個贊

嘗試使用 繪制樹狀圖時我遇到了同樣的問題squarify。經過一番搜索,我想出了一個解決方案,它似乎按預期工作。


import matplotlib.patches as mpatches

import matplotlib.text as mtext


# Refrence https://stackoverflow.com/questions/48079364/wrapping-text-not-working-in-matplotlib

# and https://stackoverflow.com/questions/50742503/how-do-i-get-the-height-of-a-wrapped-text-in-matplotlib

class WrapText(mtext.Text):

    def __init__(self,

                 x=0, y=0, text='',

                 width=0,

                 **kwargs):

        mtext.Text.__init__(self,

                 x=x, y=y, text=text,

                 wrap=True,

                 **kwargs)

        self.width = width  # in screen pixels. You could do scaling first


    def _get_wrap_line_width(self):

        return self.width

    

    def get_lines_num(self):

        return len(self._get_wrapped_text().split('\n'))

    


class WrapAnnotation(mtext.Annotation):

    def __init__(self,

                 text, xy,

                 width, **kwargs):

        mtext.Annotation.__init__(self, 

                                  text=text,

                                  xy=xy,

                                  wrap=True,

                                  **kwargs)

        self.width = width

        

    def _get_wrap_line_width(self):

        return self.width

    

    def get_lines_num(self):

        return len(self._get_wrapped_text().split('\n'))



def text_with_autofit(self, txt, xy, width, height, *, 

                      transform=None, 

                      ha='center', va='center',

                      wrap=False, show_rect=False,

                      min_size=1, adjust=0,

                      **kwargs):

    if transform is None:

        if isinstance(self, Axes):

            transform = self.transData

        if isinstance(self, Figure):

            transform = self.transFigure

        

        

    x_data = {'center': (xy[0] - width/2, xy[0] + width/2), 

            'left': (xy[0], xy[0] + width),

            'right': (xy[0] - width, xy[0])}

    y_data = {'center': (xy[1] - height/2, xy[1] + height/2),

            'bottom': (xy[1], xy[1] + height),

            'top': (xy[1] - height, xy[1])}

    

    (x0, y0) = transform.transform((x_data[ha][0], y_data[va][0]))

    (x1, y1) = transform.transform((x_data[ha][1], y_data[va][1]))

    # rectange region size to constrain the text

    rect_width = x1 - x0

    rect_height = y1- y0

    

    fig = self.get_figure() if isinstance(self, Axes) else self

    dpi = fig.dpi

    rect_height_inch = rect_height / dpi

    fontsize = rect_height_inch * 72


    if isinstance(self, Figure):

        if not wrap:

            text = self.text(*xy, txt, ha=ha, va=va, transform=transform, 

                             fontsize=min_size, 

                             **kwargs)

        else:

            fontsize /= 2

            text = WrapText(*xy, txt, width=rect_width, ha=ha, va=va,

                            transform=transform, fontsize=fontsize,

                            **kwargs)

            self.add_artist(text)

            

    if isinstance(self, Axes):

        if not wrap:

            text = self.annotate(txt, xy, ha=ha, va=va, xycoords=transform,

                                 fontsize=min_size, 

                                 **kwargs)

        else:

            fontsize /= 2

            text = WrapAnnotation(txt, xy, ha=ha, va=va, xycoords=transform,

                                  fontsize=fontsize, width=rect_width,

                                  **kwargs)

            self.add_artist(text)

    

    while fontsize > min_size:

        text.set_fontsize(fontsize)

        bbox = text.get_window_extent(fig.canvas.get_renderer())

        bbox_width = bbox.width / text.get_lines_num() if wrap else bbox.width

        if bbox_width <= rect_width:

            while bbox_width <= rect_width:

                fontsize += 1

                text.set_fontsize(fontsize)

                bbox = text.get_window_extent(fig.canvas.get_renderer())

                bbox_width = bbox.width / text.get_lines_num() if wrap else bbox.width

            else:

                fontsize = fontsize - 1

                text.set_fontsize(fontsize)

                break;

        

        fontsize /= 2      

    

    if fig.get_constrained_layout():

        c_fontsize = fontsize + adjust + 0.5

        text.set_fontsize(c_fontsize if c_fontsize > min_size else min_size)

    if fig.get_tight_layout():

        c_fontsize = fontsize + adjust

        text.set_fontsize(c_fontsize if c_fontsize > min_size else min_size)

    

    if show_rect and isinstance(self, Axes):   

        rect = mpatches.Rectangle((x_data[ha][0], y_data[va][0]), 

                                  width, height, fill=False, ls='--')

        self.add_patch(rect)

        

    return text

此功能支持將文本自動調整到框中。如果wrap是True,則文本將根據框的大小自動換行。


grow=True下圖是自動調整( )和自動換行(wrap=True)的圖


數據是來自treemapify的 G20 ,這是一個用于繪制樹形圖的優秀 R 包。


自動擬合的圖:

https://img1.sycdn.imooc.com//64cb675b000142ba06340475.jpg

自動調整和自動換行的圖形: 

https://img1.sycdn.imooc.com//64cb6767000161c306270475.jpg

自動調整的基本過程是根據框的高度設置字體大小,將文本寬度與框的寬度進行比較,然后減小字體大小,直到文本寬度小于框的寬度。

至于自動換行,底層過程依賴于 matplotlib 中內置的自動換行,通過設置wrap=True. 自動調整字體大小的過程是相同的。

然而,自動適配的過程有點慢。我希望有人能夠找出一些更有效的自動擬合算法。

希望這個功能可以幫助到您。


查看完整回答
反對 回復 2023-08-03
  • 1 回答
  • 0 關注
  • 363 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號