簡體   English   中英

如何用箭頭填充pyplot圖表的條形?

[英]How to fill the bars of a pyplot barchart with arrows?

我正在嘗試創建一個條形圖,其中的條形圖通過使條形圖內的箭頭指向基點來包含其他信息。

現在,我只設法創建了一個帶有彩色條的圖形。 由於需要8個方向的顏色和1個不存在的顏色,所以它不如我希望的那樣好。 區分顏色也很困難。

import matplotlib.pyplot as plt
import numpy as np

x = range(12)
y_bar = np.random.random(12)

colors = ['green', 'yellow', 'blue', 'pink', 'orange']
bar_cols = [ colors[int(b*100) % len(colors)] for b in y_bar]

plt.bar(x, y_bar, color=bar_cols)
plt.show()

因此,指向由單獨變量提供的方向的箭頭是直觀且易於查看的。

我不知道該怎么做。 我已經嘗試使用陰影線,但是似乎只有有限的一組符號。

有沒有辦法在條形圖中得到一些箭頭?

編輯:這是一個奇特的東西,它看起來如何,我在追求什么。 箭頭的形狀可以不同。 當然,預計會有更多支柱。 由於任務數據,甚至可能會有一些沒有任何箭頭的箭頭。 帶箭頭的barchar

這是使用ax.annotate在每個條形圖中繪制箭頭的解決方案。 由於OP對箭頭的外觀不太清楚,因此將每個條形划分為矩形(在代碼中稱其為squares ,但是如果您固定繪圖的長寬比,它們只是正方形),並繪制了一個居中的箭頭每個矩形的方向由應該由用戶提供的角度給定(此處為稱為wind_direction的向量)。

在代碼中,我將Axes的長寬比設置為xy限制的長寬比,這使Axes正方形,因此可以輕松繪制相同長度的箭頭,而與箭頭的方向無關。 如果不需要,可以將相應的行注釋掉。 如果箭頭必須具有相同的長度而沒有限制,則必須計算圖形的長寬比,例如,請參見此處的操作方法。

我還用風度(以度為單位)注釋了每個條形,只是為了易於檢查箭頭是否與給定的風向相對應。

from matplotlib import pyplot as plt
import numpy as np

fig,ax = plt.subplots()

x = np.arange(12)
wind_strength = np.random.random(12)
wind_direction = np.linspace(0,2*np.pi,12, endpoint = False)

colors = ['green', 'yellow', 'blue', 'pink', 'orange']
bar_cols = [colors[i%len(colors)] for i,s in enumerate(wind_strength)]

bars = ax.bar(x,wind_strength, color=bar_cols)

##computing the aspect ratio of the plot ranges:
xlim = ax.get_xlim()
ylim = ax.get_ylim()
aspect = (xlim[1]-xlim[0])/(ylim[1]-ylim[0])


##comment out this line if you don't care about the arrows being the
##same length
ax.set_aspect(aspect)


##dividing each bar into 'squares' and plotting an arrow into each square
##with orientation given by wind direction
for bar,angle in zip(bars,wind_direction):
    (x1,y1),(x2,y2) = bar.get_bbox().get_points()
    w = x2-x1
    h = w/aspect
    x_mid = (x1+x2)/2

    dx = np.sin(angle)*w
    dy = np.cos(angle)*h

    ##the top and bottom of the current square:
    y_bottom = y1
    y_top = y_bottom+h

    ##draw at least one arrow (for very small bars)
    first = True

    while y_top < y2 or first:
        y_mid = (y_top+y_bottom)/2

        ax.annotate(
            '',xytext=(x_mid-dx/2,y_mid-dy/2),
            xy=(x_mid+dx/2,y_mid+dy/2),
            arrowprops=dict(arrowstyle="->"),
        )

        ##next square
        y_bottom = y_top
        y_top += h

        ##first arrow drawn:
        first = False

    ##annotating the wind direction:
    ax.text(x_mid, y2+0.05, '{}'.format(int(180*angle/np.pi)), ha = 'center')

plt.show()

最終結果如下所示:

以上代碼的結果

希望這可以幫助。

在條形圖中繪制箭頭的一種選擇確實是陰影線。 但是,這有點涉及。 需要創建一些自定義陰影,如以下答案所示: 如何在matplotlib中用自定義陰影填充多邊形? 在這里,我們可以使用箭頭的路徑。

因此,在下面的代碼中,我們子類化matplotlib.hatch.Shapes並創建箭頭的某些路徑。 現在的問題是,我們需要一些參數來插入陰影,以便能夠定義角度。 然后,我們可以定義一個自定義的填充圖案,我選擇如下所示

hatch="arr{angle}{size}{density}"

哪里

  • 角度:0到360之間的整數
  • 大小:2到20之間的某個整數
  • 密度:> = 1的一些整數

這類似於我先前對這個問題的回答 取決於路徑旋轉的角度,大小和密度基本上決定了顯示多少個大小的箭頭。 請注意,並非所有參數看起來都不錯,有些會導致剖面線重疊。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.offsetbox
import matplotlib.hatch
from matplotlib.patches import Polygon


class ArrowHatch(matplotlib.hatch.Shapes):
    """
    Arrow hatch. Use with hatch="arr{angle}{size}{density}"
                 angle: integer number between 0 and 360
                 size: some integer between 2 and 20
                 density: some integer >= 1 
    """
    filled = True
    size = 1

    def __init__(self, hatch, density):
        v1 = [[.355,0], [.098, .1], [.151,.018], [-.355,.018]]
        v2 = np.copy(v1)[::-1]
        v2[:,1] *= -1 
        v = np.concatenate((v1,v2))
        self.path = Polygon(v, closed=True, fill=False).get_path()
        self.num_lines = 0
        if len(hatch) >= 5:
            if hatch[:3] == "arr":
                h = hatch[3:].strip("{}").split("}{")
                angle = np.deg2rad(float(h[0]))
                self.size = float(h[1])/10.
                d = int(h[2])
                self.num_rows = 2*(int(density)//6*d)
                self.num_vertices = (self.num_lines + 1) * 2

                R = np.array([[np.cos(angle), -np.sin(angle)],
                              [np.sin(angle), np.cos(angle)]])
                self.shape_vertices = np.dot(R,self.path.vertices.T).T
                self.shape_codes = self.path.codes
        matplotlib.hatch.Shapes.__init__(self, hatch, density)

matplotlib.hatch._hatch_types.append(ArrowHatch)


n = 7
a = 1017
x = np.arange(n)
y = np.linspace(0.2*a,a,len(x))

fig, ax = plt.subplots()
bar = ax.bar(x,y, ec="k", color="lightblue")

angles = [0,45,360-45,90,225,360-90,160]
for b, a in zip(bar, angles):
    f = 'arr{{{}}}{{9}}{{3}}'.format(a)
    b.set_hatch(f)


plt.show()

輸出{angle}{9}{3}

在此處輸入圖片說明

輸出{angle}{11}{2}

在此處輸入圖片說明


下面將介紹有關填充圖案及其管理方式的說明。 或者換句話說, ArrowHatch如何知道它應該創建一個陰影?
將使用matplotlib.hatch._hatch_types內任何陰影線的頂點來應用陰影線。 這就是為什么我們需要將ArrowHatch類添加到此列表。 根據此類是否具有大於零的num_vertices屬性,它將有助於最終填充。 這就是為什么我們在init函數中將其設置為self.num_lines = 0的原因。 但是,如果hatch (是_hatch_types列表中每個類提供的字符串)包含我們的匹配模式,則將self.num_rows設置self.num_rows 0的值(對於Shapes,它應該是偶數,因為它們在2個移位的行中產生),從而有助於孵化。
這個概念有點異國情調,因為基本上每個班級本身都根據hatch字符串自行決定是否參與hatch 一方面,這非常方便,因為它允許輕松組合不同的圖案填充類型,例如"///++oo" 另一方面,在這種情況下,很難用於需要輸入參數的艙口蓋作為角度。 並且還需要注意不要在陰影線中使用任何其他陰影線使用的字符; 例如,我最初想使用類似"arrow45.8,9,2"類的東西,因為o "arrow45.8,9,2"不能使用. 並且,其他有效的孵化類型,這樣的結果會顯示一些點所有的地方。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM