Skip to content

TypeError when plotting stacked bar chart with decimal #10788

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
GrishaVar opened this issue Mar 14, 2018 · 4 comments · Fixed by #13738
Closed

TypeError when plotting stacked bar chart with decimal #10788

GrishaVar opened this issue Mar 14, 2018 · 4 comments · Fixed by #13738

Comments

@GrishaVar
Copy link

GrishaVar commented Mar 14, 2018

Bug report

Bug summary

When plotting bar graph, TypeError exception is raised if values for bar bases are Decimal types.

Code for reproduction

from decimal import Decimal
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x = list(range(10))
y = [Decimal(x) for x in range(10)]
bar1 = ax.bar(x, y)
bar2 = ax.bar(x, y, bottom=y)

Actual outcome

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/user/dev/venv/lib/python3.6/site-packages/matplotlib/__init__.py", line 1717, in inner
    return func(ax, *args, **kwargs)
  File "/home/user/dev/venv/lib/python3.6/site-packages/matplotlib/axes/_axes.py", line 2137, in bar
    self.autoscale_view()
  File "/home/user/dev/venv/lib/python3.6/site-packages/matplotlib/axes/_base.py", line 2351, in autoscale_view
    'minposy', self.yaxis, self._ymargin, y_stickies, self.set_ybound)
  File "/home/user/dev/venv/lib/python3.6/site-packages/matplotlib/axes/_base.py", line 2330, in handle_single_axis
    do_lower_margin = not np.any(np.isclose(x0, stickies))
  File "/home/user/dev/venv/lib/python3.6/site-packages/numpy/core/numeric.py", line 2333, in isclose
    yfin = isfinite(y)
TypeError: ufunc 'isfinite' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

Expected outcome

No exception is raised, bar2 line behaves the same way it would if y contained ints or floats.

Matplotlib version

  • Operating system: Ubuntu 16.04
  • Matplotlib version: 2.1.1
  • Matplotlib backend: TkAgg
  • Python version: 3.6.4
  • Decimal version: 1.70

python installed via pyenv, matplotlib via pip

@jklymak
Copy link
Member

jklymak commented Mar 14, 2018

First, this can be made even simpler:

from decimal import Decimal
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x = [Decimal(x) for x in range(10)]
y = [Decimal(x) for x in range(10)]
bar1 = ax.bar(x, y)

yields:

 File "/Users/jklymak/matplotlib/lib/matplotlib/axes/_axes.py", line 2250, in bar
    left = x - width / 2
TypeError: unsupported operand type(s) for -: 'decimal.Decimal' and 'float'

The problem is that we have to do some math on x to make bars, and we can't do mixed math on Decimal. (this is unlike other functions like plot or scatter, which don't seem to mind iterating over the values in x, be they decimal or numpy...)

We can't just force it to float, because other values of x need not be floats (i.e. datetime, categorical).

A possible solution is to write a unit converter for decimal and add to the default converters. Of course this would just make everything np.float, in which case, why not just cast it to float at the user level.

@Stigjb
Copy link

Stigjb commented Jun 13, 2018

This also affects datetime.time values on the x axis. Here's an example that gives a similar error:

import datetime
import matplotlib.pyplot as plt
xs = [datetime.time(x) for x in range(10)]
ys = list(range(10))
plt.bar(xs, ys)

yields

/usr/local/envs/py3env/lib/python3.5/site-packages/matplotlib/axes/_axes.py in bar(self, *args, **kwargs)
   2064         if align == 'center':
   2065             if orientation == 'vertical':
-> 2066                 left = x - width / 2
   2067                 bottom = y
   2068             elif orientation == 'horizontal':

TypeError: unsupported operand type(s) for /: 'list' and 'int'

But it works fine using plt.plot.

Matplotlib version 2.1.2

@ImportanceOfBeingErnest
Copy link
Member

@Stigjb Your issue is different. You are trying to plot time values which matplotlib in general does not support. For a workaround, use datetime instead of time values.

In case you want matplotlib to add support for time values natively, I would suggest you open a new issue with this feature request.

@tacaswell tacaswell modified the milestones: v3.0, v3.1 Aug 11, 2018
@sasoripathos
Copy link
Contributor

sasoripathos commented Mar 3, 2019

I am a student and is new to Matplotlib. For the problem about Decimal and float type error, I think it is because the height and width in bar(...) function in _axes.py (based on my current understanding) are always of type float (or integer). Further more, when creating patches, the width and height may be used in constructors. Here is my thought about how to (kind of) fix it:
Right before we compute alignment, we add a check:

if orientation=='vertical' and x.size > 0 and isInstance(x[0], Decimal):
     width = np.array([Decimal(itm) for itm in width ])
elif orientation=='horizontal' and y.size > 0 and isInstance(y[0], Decimal):
     height = np.array([Decimal(itm) for itm in height])

# Insert above code right before the following code
if align == 'center':
    if orientation == 'vertical':
        try:
             left = x - width / 2
# the rest code unchanged

Here are some bar plots I can produced with Decimal x and y after the above change:
image
Since I am quite new to Matplotlib, I am wondering whether this can be a fix and whether it has any side effect.
Please do pardon my beginner knowledge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants