博客專欄

        EEPW首頁 > 博客 > Matplotlib 可視化之圖例與標(biāo)簽高級應(yīng)用

        Matplotlib 可視化之圖例與標(biāo)簽高級應(yīng)用

        發(fā)布人:AI科技大本營 時間:2022-05-15 來源:工程師 發(fā)布文章

        以下文章來源于數(shù)據(jù)STUDIO ,作者云朵君

        作者 | 云朵君     

        來源 | 數(shù)據(jù)STUDIO

        裝飾物指的是你可以添加到一個圖形上的所有額外元素,以美化它或使它更清晰。裝飾物包括圖例、注釋、顏色條、文本等標(biāo)準(zhǔn)元素,但也可以專門設(shè)計自己的元素。今天一起繼續(xù)學(xué)習(xí)圖例與標(biāo)簽元素的應(yīng)用實(shí)例。

        配置圖例

        想在可視化圖形中使用圖例,可以為不同的圖形元素分配標(biāo)簽。圖例非常容易使用,只要求用戶命名圖。Matplotlib將自動創(chuàng)建一個包含每個圖形元素的圖例。即使在大多數(shù)情況下,一個簡單的legend() 調(diào)用就足夠了,但圖例還是提供了幾個選項,允許我們自定義圖例的各個配置。如使用

        ax.legend(loc='upper left',
                  frameon=False,
                  edgecolor="None")


        完整代碼解析

        上下滑動查看更多源碼
        fig = plt.figure(figsize=(9.64))
        ax = plt.subplot(
            xlim=[-np.pi, np.pi],
            xticks=[-np.pi, -np.pi / 20, np.pi / 2, np.pi],
            xticklabels=["$-\pi$""$-\pi/2$""0""$+\pi/2$""$+\pi$"],
            ylim=[-11],
            yticks=[-101],
            yticklabels=["-1""0""+1"],
        )

        X = np.linspace(-np.pi, np.pi, 256, endpoint=True)
        C, S = np.cos(X), np.sin(X)
        # 繪制兩條折線,顏色默認(rèn)
        ax.plot(X, C, label="$cos(x)$", clip_on=False)
        ax.plot(X, S, label="$sin(x)$", clip_on=False)
        # 隱藏上右邊的軸線
        ax.spines["right"].set_visible(False)
        ax.spines["top"].set_visible(False)
        # 移動下左邊的軸線
        ax.spines["left"].set_position(("data"-3.25))
        ax.spines["bottom"].set_position(("data"-1.25))
        ax.legend(edgecolor="None",loc=2,frameon=False)
        然而,在某些情況下,用圖例來添加信息可能不是最合適的方式。例如,當(dāng)你有多個圖表時,讀者在閱讀圖表,視線在圖表和圖例之間來回切換時,可能會覺得很乏味。另一種可以解決此類困惑的方法是在下圖所示的圖上直接添加信息。圖片

        詳細(xì)代碼解析

        上下滑動查看更多源碼
        X = np.linspace(-np.pi, np.pi, 400, endpoint=True)
        C, S = np.cos(X), np.sin(X)
        plot1, plot2 = plot(ax) # 繪制折線圖的對象
        # --------------------P1-------------------------
        # 用小橫線標(biāo)注在折線旁邊
        ax.text(
            X[-1], C[-1],
            " — " + plot1.get_label(), # 從對象中獲取標(biāo)簽   
            color=plot1.get_color(),   # 從對象中獲取線條顏色
            size="small", ha="left", va="center",)
        # --------------------P2--------------------------
        # 標(biāo)注在對應(yīng)折線上,且有透明邊框
        ax.text(
            X[100], C[100],
            " " + plot1.get_label(),
            bbox=dict(facecolor="white", edgecolor="None", alpha=0.85),
            color=plot1.get_color(),
            ha="center", va="center", size="small",
            rotation=42.5,)
        # --------------------P3--------------------------
        # 使用箭頭
        ax.annotate(
            "$cos(x)$",
            (X[100], C[100]),
            size="medium",
            color=plot1.get_color(),
            xytext=(-50, +10),
            textcoords="offset points",
            arrowprops=dict(
                arrowstyle="->", color=plot1.get_color(), 
                connectionstyle="arc3,rad=-0.3"),)
        # --------------------P4--------------------------
        # 圈點(diǎn)和注釋的組合
        index = 10
        ax.scatter(
            [X[index]], [C[index]],
            s=100, marker="o", zorder=10,     
            edgecolor=plot1.get_color(),
            facecolor="white", linewidth=1, clip_on=False,)
        ax.text(
            X[index], 1.01 * C[index],
            "A",
            zorder=20,size="small",
            color=plot1.get_color(),
            ha="center", va="center", clip_on=False,)
        當(dāng)然,這里是沒有最好的選擇,因為它真的取決于數(shù)據(jù)。對于上述的sin / cos的示例(非常簡單),這四種解決方案都是合適的,但當(dāng)有很多實(shí)際數(shù)據(jù)一起使用時,可能這種方法就失效了。此時我們可能需要尋求其他方式來標(biāo)記數(shù)據(jù),如將圖分成幾個圖分別展示。

        標(biāo)題和標(biāo)簽

        我們已經(jīng)使用 set_titleset_xlabel 和 set_ylabel 方法操作了標(biāo)題和標(biāo)簽。當(dāng)僅僅使用默認(rèn)參數(shù)時,確實(shí)比較方便。并且它們的默認(rèn)位置通常對大多數(shù)圖表都比較合適。盡管如此,仍然可以使用各種參數(shù)來定制和美化圖形。如下面兩個圖所示,對比觀察,可以明顯發(fā)現(xiàn):上圖大部分使用了默認(rèn)參數(shù)。而下圖中,用軸標(biāo)簽替換軸刻度標(biāo)簽,即在軸中間加上說明標(biāo)簽,為了使其更靠近軸,刪除了可能與標(biāo)簽碰撞的中心刻度。此外,將標(biāo)題其向右移動,并相應(yīng)地移動圖例框,將其放置在標(biāo)題下方,并且使用一行兩列的排列方式。其實(shí)這里沒有做過復(fù)雜的操作,但我認(rèn)為結(jié)果在視覺上更驚艷。圖片

        完整代碼解析
        ax.legend(
            edgecolor="None",
            ncol=2,
            loc="upper right",
            bbox_to_anchor=(1.011.225),   
            # 用于與loc一起定位圖例的框。(x, y, width, height)
            borderaxespad=1,                
            # 軸線和圖例邊框之間的填充,以字體大小為單位。
        )
        # 設(shè)置標(biāo)題
        ax.set_title("三角函數(shù)", x=1, y=1.2, ha="right",size=14)
        # 設(shè)置x軸標(biāo)簽
        ax.set_xlabel("角度", va="center", weight="bold",size=12)
        ax.xaxis.set_label_coords(0.5-0.25)
        # 設(shè)置標(biāo)簽的坐標(biāo)。
        # 默認(rèn)情況下,y 標(biāo)簽的 x 坐標(biāo)和 x 標(biāo)簽的 y 坐標(biāo)由刻度標(biāo)簽邊界框確定,
        # 但是如果有多個軸,這可能會導(dǎo)致多個標(biāo)簽對齊不良。
        # 設(shè)置y軸標(biāo)簽
        ax.set_ylabel("值", ha="center", weight="bold",size=12)
        ax.yaxis.set_label_coords(-0.0250.5)

        在某些情況下(如會議海報),可能需要讓標(biāo)題更吸引眼球,如下圖所示。這可以通過使用make_axes_locatable 方法來劃分每個軸,并為標(biāo)題區(qū)域預(yù)留15%的高度。在這個圖中,還用Latex 插入了一個完全對齊的文本,它可以被看作是另一種形式或(高級)裝飾。圖片完整代碼參見latex-text-box[1]

        注釋

        在matplotlib中,注釋可能是最難處理的對象。原因是它包含的概念眾多,而這些概念又具有大量的參數(shù)。此外,由于注釋所涉及的文本大小是按點(diǎn)排列的,這無疑又是雪上加霜。此外可能需要混合使用像素、點(diǎn)、分?jǐn)?shù)或數(shù)據(jù)單元中的絕對坐標(biāo)或相對坐標(biāo)。你可以這么認(rèn)為,你可以對具有任何類型投影的任何軸進(jìn)行注釋,那么你現(xiàn)在應(yīng)該可以理解到為什么annotate方法提供這么多參數(shù)。

        上面這段話比較抽象,接下來我們一起看下具體例子。注釋圖形最簡單的方法是在想要注釋的點(diǎn)附近添加標(biāo)簽,如下圖所示。圖中,為了使得標(biāo)簽獨(dú)立于數(shù)據(jù)分布保持可讀性,為標(biāo)簽添加了一個白色的輪廓。然而,如果這樣的點(diǎn)過多,所有不同的標(biāo)簽可能會使圖形變得混亂,并可能會掩蓋潛在的重要信息。

        圖片

        完整代碼解析

        上下滑動查看更多源碼
        import matplotlib.patheffects as path_effects
        fig = plt.figure(figsize=(105))
        ax = plt.subplot(121, xlim=[-1, +1], 
                         xticks=[], ylim=[-1, +1], 
                         yticks=[], aspect=1)
        # --------------------------------------------- 
        # 繪制散點(diǎn)圖
        np.random.seed(123)
        X = np.random.normal(00.351000)
        Y = np.random.normal(00.351000)

        ax.scatter(X, Y, edgecolor="None", s=60,
                   facecolor="C1", alpha=0.5)

        # 不重復(fù)采用:array([1, 4, 0, 3, 2])
        I = np.random.choice(len(X), size=5
                             replace=False)
        # 根據(jù)y值,從大到小排序
        Px, Py = X[I], Y[I]
        I = np.argsort(Y[I])[::-1]
        Px, Py = Px[I], Py[I]
        # 將隨機(jī)選取的五個點(diǎn)用黑色邊框框選出
        ax.scatter(Px, Py, edgecolor="black", facecolor="white", zorder=20)
        ax.scatter(Px, Py, edgecolor="None", facecolor="C1", alpha=0.5, zorder=30)

        添加標(biāo)簽注釋
        for i in range(len(I)):
        # 五個注釋是樣式是一樣的,可以使用循環(huán)添加
            text = ax.annotate(
                "Point " + chr(ord("A") + i),
                xy=(Px[i], Py[i]),
                xycoords="data",
                xytext=(018),
                textcoords="offset points",
                ha="center",
                size="medium",
                arrowprops=dict(
                    arrowstyle="->", shrinkA=0, shrinkB=5, color="black", linewidth=0.75),
            )
            text.set_path_effects(
                [path_effects.Stroke(linewidth=2, foreground="white"), path_effects.Normal()]
            )
            text.arrow_patch.set_path_effects(
                [path_effects.Stroke(linewidth=2, foreground="white"), path_effects.Normal()]
            )

        另一種方法是將標(biāo)簽推到圖的一側(cè),并使用虛線來建立點(diǎn)和標(biāo)簽之間的鏈接,如下圖所示。圖片但這些形狀、位置、排列方式等樣式的設(shè)計并不是圖形自動的,為了繪制出該圖形,就必須計算幾乎所有的東西。首先,為了不讓線相互交叉,將目標(biāo)標(biāo)記的點(diǎn)排序:

        X = np.random.normal(0.351000
        Y = np.random.normal(0.351000)
        ax.scatter(X, Y, edgecolor="None"
                   facecolor="C1", alpha=0.5)
        I = np.random.choice(len(X), size=5, replace=False)
        Px, Py = X[I], Y[I]
        I = np.argsort(Y[I])[::-1]
        Px, Py = Px[I], Py[I]

        從這些點(diǎn)開始,使用一個相當(dāng)復(fù)雜的連接樣式來注釋它們:

        上下滑動查看更多源碼
        y, dy = 0.250.125
        style = "arc,angleA=-0,angleB=0,armA=-100,armB=0,rad=0"
        for i in range(len(I)):
            text = ax2.annotate(
                "Point " + chr(ord("A") + i),
                xy=(Px[i], Py[i]),
                xycoords="data",
                xytext=(1.25, y - i * dy),
                textcoords="data",
                arrowprops=dict(
                    arrowstyle="->",
                    color="black",
                    linewidth=0.75,
                    shrinkA=20,
                    shrinkB=5,
                    patchA=None,
                    patchB=None,
                    connectionstyle=style,
                ),
            )
            text.arrow_patch.set_path_effects(
                [path_effects.Stroke(linewidth=2, foreground="white"), path_effects.Normal()]
            )
        也可以使用連接補(bǔ)片的方式在軸外來注釋的目標(biāo)對象,如下圖所示。
        圖片該圖中,創(chuàng)建了幾個矩形,在一些點(diǎn)周圍顯示感興趣的區(qū)域,并創(chuàng)建了與相應(yīng)的縮放軸的連接。注意連接開始在外面的矩形,這是一個不錯的功能提供的注釋:可以指定對象的性質(zhì)要注釋(通過提供一個patche)和matplotlib會照顧的連接邊界的起源的patche。

        完整代碼解析

        上下滑動查看更多源碼
        from matplotlib.gridspec import GridSpec
        from matplotlib.patches import Rectangle, ConnectionPatch
        # 設(shè)置畫布
        fig = plt.figure(figsize=(65))
        n = 5
        gs = GridSpec(n, n + 1)
        ax = plt.subplot( gs[:n, :n], 
                  xlim=[-1, +1], xticks=[], 
                  ylim=[-1, +1], yticks=[], aspect=1)

        # 繪制散點(diǎn)圖略(見上面代碼)
        dx, dy = 0.0750.075
        for i, (x, y) in enumerate(zip(Px, Py)):
        # 設(shè)置子畫布
            sax = plt.subplot(
                gs[i, n],
                xlim=[x - dx, x + dx],
                xticks=[],
                ylim=[y - dy, y + dy],
                yticks=[],
                aspect=1,)
        # 在子畫布上繪制散點(diǎn)
            sax.scatter(X, Y, edgecolor="None"
                        facecolor="C1", alpha=0.5,s=60)
            sax.scatter(Px, Py, edgecolor="black"
                        facecolor="None", linewidth=0.75,s=60)
        # 加上注釋
            sax.text(
                1.10.5,
                "Point " + chr(ord("A") + i),
                rotation=90, size=8, ha="left", va="center",
                transform=sax.transAxes, )
        # 繪制矩形
            rect = Rectangle(
                (x - dx, y - dy),
                2 * dx, 2 * dy,
                edgecolor="black", facecolor="None",
                linestyle="--", linewidth=0.75, )
            ax.add_patch(rect)
        # 繪制連接補(bǔ)丁Patch
            con = ConnectionPatch(
                xyA=(x, y), coordsA=ax.transData,
                xyB=(00.5), coordsB=sax.transAxes,
                linestyle="--", linewidth=0.75,
                patchA=rect, arrowstyle="->", )
            fig.add_artist(con)
        GridSpec:指定子圖將放置的網(wǎng)格的幾何位置。需要設(shè)置網(wǎng)格的行數(shù)和列數(shù)。子圖布局參數(shù)(例如,左,右等)可以選擇性調(diào)整。
        ConnectionPatch:用于在兩點(diǎn)之間建立連接線。
        上下滑動查看更多參數(shù)
        參數(shù):xyA: 它是x-y圖上也稱為點(diǎn)A的連接線的起點(diǎn)。xyB: 它是x-y圖上連接線的起點(diǎn),也稱為點(diǎn)B。coordsA: A點(diǎn)的坐標(biāo)。coordsB: B點(diǎn)的坐標(biāo)。axesA: 它是x-y圖上連接軸的起點(diǎn)。axesB: 它是x-y圖上連接軸的終點(diǎn)。arrowstyle: 用于設(shè)置連接箭頭的樣式。其默認(rèn)類型為“-”。arrow_transmuter: 用于忽略連接線。connectionstyle: 它描述了posA和posB的連接方式。它可以是ConnectionStyle類的實(shí)例,也可以是connectionstyle名稱的字符串,它具有可選的逗號分隔屬性。connector: 通常忽略它,并決定忽略哪個連接器。patchA: 用于在A點(diǎn)添加補(bǔ)丁。patchB: 用于在B點(diǎn)添加補(bǔ)丁shrinkA: 用于在A點(diǎn)收縮連接器。shrinkB: 用于在B點(diǎn)收縮連接器。mutation_scale: 箭頭樣式的屬性(例如head_length)的縮放比例值。mutation_aspect: 變異前,矩形的高度將被該值擠壓,變異框?qū)⒈黄涞箶?shù)拉伸。clip_on: 設(shè)置藝術(shù)家是否使用剪輯。dpi_cor: dpi_cor當(dāng)前用于linewidth-related事物和收縮因子。突變規(guī)模受此影響。

        參考資料[1]

        latex-text-box: https://github.com/rougier/scientific-visualization-book/blob/master/code/ornaments/latex-text-box.py

        [2]

        Scientific Visualisation-Python & Matplotlib



        *博客內(nèi)容為網(wǎng)友個人發(fā)布,僅代表博主個人觀點(diǎn),如有侵權(quán)請聯(lián)系工作人員刪除。



        關(guān)鍵詞: AI

        相關(guān)推薦

        技術(shù)專區(qū)

        關(guān)閉
        主站蜘蛛池模板: 沾益县| 类乌齐县| 龙岩市| 青冈县| 闸北区| 牟定县| 托克逊县| 通城县| 丰台区| 错那县| 淮安市| 邵武市| 汶川县| 汕尾市| 浏阳市| 黔江区| 喀什市| 靖边县| 阿勒泰市| 哈巴河县| 搜索| 自治县| 施甸县| 上饶县| 金山区| 商南县| 新丰县| 黄骅市| 河池市| 定襄县| 蓬安县| 白沙| 霞浦县| 德庆县| 镇宁| 霸州市| 沁源县| 建湖县| 乐平市| 峡江县| 郓城县|