matplotlib的"barh"中的高度变量单位是多少?

hmi*_*nle 5 python matplotlib

在matplotlib函数barh的定义中:

matplotlib.pyplot.barh(bottom, width, height=0.8, left=None, hold=None, **kwargs)

默认的"高度"是0.8,但是当我绘制一些具有不同图形高度的图形时,例如(30,40,...)和dpi = 100.我看到酒吧的高度发生了变化.这不是固定的.所以我想知道什么是高度的单位,以及如何使其固定(不依赖于图的高度).

use*_*786 5

我将把它分成两部分:

我想知道barh的高度单位是什么

(显然人们自 2009 年以来就一直在想这个问题……所以我猜你是个好伙伴!)

这个问题是比较容易的部分——它是分配给图中条形高度的百分比。例如,默认值height=0.8表示条的高度将为0.8 * (plot_height / n_bars)。您可以通过设置height=1.0(甚至值 > 1,条形会重叠)来查看这一点。

如果你真的想确定,这里axes.barh. 这只是调用axes.bar- 看看这些行

nbars = len(bottom)
if len(left) == 1:
    left *= nbars
if len(height) == 1:
    height *= nbars
Run Code Online (Sandbox Code Playgroud)

后来...

args = zip(left, bottom, width, height, color, edgecolor, linewidth)
for l, b, w, h, c, e, lw in args:
    if h < 0:
        b += h
        h = abs(h)
    if w < 0:
        l += w
        w = abs(w)
    r = mpatches.Rectangle(
        xy=(l, b), width=w, height=h,
        facecolor=c,
        edgecolor=e,
        linewidth=lw,
        label='_nolegend_',
        margins=margins
        )
    r.update(kwargs)
    r.get_path()._interpolation_steps = 100
    #print r.get_label(), label, 'label' in kwargs
    self.add_patch(r)
    patches.append(r)
Run Code Online (Sandbox Code Playgroud)

所以你看到高度被缩放nbars,当你绘制矩形时,它们被这个高度隔开。

如何修复

This is harder, you will have to manually set it. The bars on the chart are ultimately matplotlib.patches.Rectangle objects, which have a width and height... which is also a percentage. I think the best solution is to compute the appropriate percentage manually.

Here's a short example, based off a barh demo:

import matplotlib.pyplot as plt
plt.rcdefaults()
import numpy as np
import matplotlib.pyplot as plt

# Example data
people = ('Tom', 'Dick', 'Harry', 'Slim', 'Jim')
y_pos = np.arange(len(people))
performance = 3 + 10 * np.random.rand(len(people))
error = np.random.rand(len(people))

plt.figure(figsize=(5,5), dpi=80)
myplot = plt.barh(y_pos, performance, height=0.8, xerr=error, align='center', alpha=0.4)
plt.yticks(y_pos, people)
plt.xlabel('Performance')
plt.title('How fast do you want to go today?')

for obj in myplot:
    # Let's say we want to set height of bars to always 5px..
    desired_h = 5
    current_h = obj.get_height()
    current_y = obj.get_y()
    pixel_h = obj.get_verts()[2][1] - obj.get_verts()[0][1]
    print("current position = ", current_y)
    print("current pixel height = ", pixel_h)

    # (A) Use ratio of pixels to height-units to calculate desired height
    h = desired_h / (pixel_h/current_h)
    obj.set_height(h)
    pixel_h = obj.get_verts()[2][1] - obj.get_verts()[0][1]
    print("now pixel height = ", pixel_h)

    # (B) Move the rectangle so it still aligns with labels and error bars
    y_diff = current_h - h # height is same units as y
    new_y = current_y + y_diff/2
    obj.set_y(new_y)
    print("now position = ", obj.get_y())
plt.show()
Run Code Online (Sandbox Code Playgroud)

Part A calculates pixel_h/current_h to get a conversion between pixels and height-units. Then we can divide desired_h (pixels) by that ratio to obtain desired_h in height-units. This sets the bar width to 5 px, but the bottom of the bar stays in the same place, so it's no longer aligned with the labels and error bars.

Part B calculates the new y position. Since y and height are in the same units, we can just add half the height difference (y_diff) to get the new position. This keeps the bar centered around whatever original y-position it had.

Note that this only sets the initial size. If you resize the plot, for example, the bars will still scale - you'd have to override that event to resize the bars appropriately.