I've just noticed something strange and was wondering if someone could explain what's going on?
I have a dictionary i'm plotting a horizontal and vertical bar charts from.
plt.bar(food.keys(), food.values()) #works fine, but:
plt.barh(food.keys(), food.values() #gives "unhashable type: 'dict_keys'" error.
if dictionary is unhashable, why does it let me plot a normal bar graph? Is this just a quirk of the barh function or am I doing something wrong?
Here's my test dataset:
food = {'blueberries':2, 'pizza':3.50, 'apples':0.50}
Thanks.
What is difference between bar and barh graph? BAR Graph - plots vertical rectangles with constant width. BARH Graph- plots horizontal rectangles with constant heights.
barh() Function. The Axes. barh() function in axes module of matplotlib library is used to make a horizontal bar plot. Syntax: Axes.barh(self, y, width, height=0.8, left=None, *, align='center', **kwargs)
By using ylim() method ylim() method is also used to invert axes of a plot in Matplotlib. Generally, this method is used to set limits for the axes.
The other answer is of course correct in how to solve the problem - simply convert to a list. The reason why it doesn't work in the first place involves digging into the matplotlib source code.
bar
and barh
do both call matplotlib.axes.Axes.bar
under the hood. When you plot a bar
, x
is the x position of the bars and height
is the y value.
However, calling a barh
, the y values (i.e. ['blueberries', 'pizza', 'apples']
) is actually set to the argument bottom
and the length of the bars is given by width
.
The following line is called within the source code
func(ax, *map(sanitize_sequence, args), **kwargs)
Where sanitize_sequence
is:
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)
The problem is that the argument bottom
is a kwarg, and is therefore not passed to sanitize_sequence
and is never converted to a list. Whereas in a normal bar
, x
is converted to a list by sanitize_sequence
So the issue is with how barh
is implemented under the hood.
Nice MWE. Looks like you ran into a subtle python 2 to python 3 conversion bug.
Under python 2, dict.keys()
(etc.) used to return lists of values. Under python 3, dict.keys()
returns a view of the data corresponding to the keys. This view is not hashable (I think in principle, it would be; however, that behaviour is not implemented (yet)). Coercing your key view to a list circumvents the issue:
plt.barh(list(food.keys()), food.values())
The real question is why plt.bar
works, as both, plt.bar
and plt.barh
call matplotlib.axes.Axes.bar
under the hood.
The definition of barh
is literally:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With