【问题标题】:How to fill the bars of a pyplot barchart with arrows?如何用箭头填充 pyplot 条形图的条形?
【发布时间】:2018-01-19 04:54:14
【问题描述】:

我正在尝试创建一个条形图,其中条形包含附加信息,方法是让条形内的箭头指向基点。

目前我只设法创建了一个带有彩色条的。由于方向需要 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 with arrows

【问题讨论】:

  • 您能更好地解释一下您的想法吗?我认为一个人当然可以通过两个图的叠加来实现你想要的,但首先我们必须知道你到底在追求什么——你能不能画一幅画?
  • 这个问题是否仍会被编辑以阐明所需的内容以及实现该目标的问题(请参阅How to Ask)?否则我们也可能在不清楚的情况下关闭它?!
  • 我添加了一个示例图像,我的目标是。
  • 那么定义箭头的数量和方向的数据是什么?
  • 它真的必须是箭头还是像this 这样的东西就够了吗?

标签: python matplotlib bar-chart


【解决方案1】:

这是一个使用ax.annotate 在每个条形内绘制箭头的解决方案。由于 OP 对箭头的外观不是很清楚,我将每个条分成矩形(我在代码中称它们为squares,但如果你固定绘图的纵横比,它们只是正方形)并画了一个每个矩形的中心箭头,其方向由用户提供的角度给出(这里是一个名为 wind_direction 的向量)。

在代码中,我将Axes 的纵横比设置为x- 和y-limits 的纵横比,这使得Axes 呈方形,因此可以轻松绘制箭头长度相同,与方向无关。如果不需要,可以将相应的行注释掉。如果箭头必须具有相同的长度没有该限制,则必须计算图形纵横比,例如参见here 如何做到这一点。

我还用风向以度为单位对每个条形进行了注释,以方便检查箭头是否与给定的风向相对应。

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

最终的结果是这样的:

希望这会有所帮助。

【讨论】:

  • 我最初考虑过这样的解决方案,但要正确进行变换以使其显示独立于轴方面的正确角度似乎非常复杂。但我想我有点想多了,因为最后你可能会按应有的方式设置情节,然后无论如何都不会对其进行缩放。
  • 谢谢。这个解决方案符合我的需要。箭头也不错。
【解决方案2】:

在条内绘制箭头的一个选项确实是孵化。然而,这有点涉及。需要创建一些自定义孵化,如以下答案所示:How to fill a polygon with a custom hatch in matplotlib? 在这里我们可以使用箭头的路径。

因此,在下面我们继承matplotlib.hatch.Shapes 并创建一些箭头路径。现在的问题是我们需要一些参数来插入阴影,以便能够定义角度。然后我们可以定义一个自定义的阴影图案,我选择看起来像这样

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

在哪里

  • 角度:0到360之间的整数
  • 大小:2 到 20 之间的某个整数
  • 密度:某个整数 >= 1

这与我之前在this question 上的回答类似。 根据路径旋转的角度,大小和密度基本上决定了显示多少个大小的箭头。请注意,并非所有参数看起来都很好,有些参数会导致阴影重叠。

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 设置为0 以外的值(对于Shapes,它应该是偶数,因为它们是生成的在 2 个移动的行中),这样它可以有助于孵化。
这个概念有点异国情调,因为基本上每个班级自己决定是否参与孵化取决于hatch 字符串。一方面,这非常方便,因为它可以轻松组合不同的舱口类型,例如"///++oo"。另一方面,它很难用于需要输入参数的影线,在这种情况下是角度。并且还需要注意不要在孵化中使用任何角色,这些角色会被其他孵化使用;例如,我最初想使用"arrow45.8,9,2" 之类的东西,但它不起作用,因为o., 是其他有效的阴影类型,因此结果会在整个地方显示一些点。

【讨论】:

  • 这真是太好了!你能解释一下新舱口的登记是如何工作的吗?我只看到matplotlib.hatch._hatch_types.append(ArrowHatch)__init__ 函数if hatch[:3] == "arr",但是当您执行f = 'arr{{{}}}{{9}}{{3}}'.format(a) 时,孵化机械如何知道要使用哪种孵化形状?似乎没有标识符。
  • @ThomasKühn 我用一些解释更新了答案。
  • 谢谢。我总是再次对 matplotlib 的可能性感到惊讶。
猜你喜欢
  • 2015-09-27
  • 1970-01-01
  • 1970-01-01
  • 2016-01-15
  • 2015-07-13
  • 1970-01-01
  • 2019-06-06
  • 2021-12-10
  • 2020-08-20
相关资源
最近更新 更多