Matplotlib的'bar'与'barh'图形绘制的区别。 'barh'存在问题。

6

我刚刚注意到一些奇怪的事情,想知道有没有人可以解释一下是怎么回事?

我有一个字典,正在绘制水平和垂直条形图。

plt.bar(food.keys(), food.values()) #works fine, but:
plt.barh(food.keys(), food.values() #gives "unhashable type: 'dict_keys'" error.

如果字典不可哈希,为什么我可以绘制普通的条形图?这是barh函数的奇怪之处还是我的操作有误?

这是我的测试数据集:

food = {'blueberries':2, 'pizza':3.50, 'apples':0.50}

感谢您的选择。
2个回答

11
当然,另一个答案是正确的解决方法 - 只需将其转换为列表即可。之所以一开始不起作用是因为涉及到深入探究Matplotlib源代码。
在幕后,barbarh都会调用matplotlib.axes.Axes.bar。当你绘制一个bar时,x是条形图的x位置,而height是y值。
但是,如果调用barh,则y值(即['blueberries', 'pizza', 'apples'])实际上被设置为参数bottom,条形图的长度由width给出。
下面的代码行在源代码中被调用:
func(ax, *map(sanitize_sequence, args), **kwargs)

其中sanitize_sequence是:

def sanitize_sequence(data):
    """
    Convert dictview objects to list. Other inputs are returned unchanged.
    """
    return (list(data) if isinstance(data, collections.abc.MappingView)
            else data)

问题在于参数 bottom 是关键字参数,因此未传递给sanitize_sequence并且从未转换为列表。然而在普通的bar中,x通过sanitize_sequence被转换为列表。因此问题出在 barh 的具体实现方式上。

我原以为这是一个“底层”的问题。谢谢你的确认!我的解决方法是将它放到链接中再进行绘图。 - Noobcoder

7
很好的MWE。看起来你遇到了一个微妙的Python 2转Python 3的转换错误。
在Python 2中,dict.keys() (等等)返回值列表。在Python 3中,dict.keys() 返回与键对应的数据视图。这个视图是不可哈希的(我认为原则上可以哈希,但是这种行为还没有实现)。将你的键视图强制转换为列表可以规避此问题:
plt.barh(list(food.keys()), food.values())

编辑

真正的问题是为什么plt.bar有效,因为plt.barplt.barh都在幕后调用matplotlib.axes.Axes.barbarh的定义就是这样:

def barh(self, y, width, height=0.8, left=None, *, align="center",
         **kwargs):
    kwargs.setdefault('orientation', 'horizontal')
    patches = self.bar(x=left, height=height, width=width, bottom=y,
                       align=align, **kwargs)
    return patches

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接