简体   繁体   中英

Matplotlib fails with ValueError: cannot convert float NaN to integer

I encountered a strange issue with the seaborn library. When generating barplot for data with ranging from very low to very high values, eg:

       job      duration type
    0    1  83066.639344    A
    1    2    820.700000    B

it fails with:

ValueError: cannot convert float NaN to integer

This looks like a bug in matplotlib and a duplicate of "pyplot.savefig fails with ValueError: cannot convert float NaN to integer" . The latter has not been fixed yet. Is there a workaround for it?

Here's the minimal working example to reproduce the issue:

#!/usr/bin/env python3

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

d = {'job': [1, 2]),
     'duration': [83066.639344, 820.700000],
     'type': ['A', 'B']}

df = pd.DataFrame(d)

plot = sns.catplot(x="duration", y="job", data=df, hue='type',
                   color="b", kind="bar", height=3, aspect=4)

ax = plot.axes.flat[0]
for p in plt.gca().patches:
    ax.text(p.get_width(),
            p.get_y() + p.get_height() / 2,
            p.get_width())

plot.savefig("barplot.png")

Some observations:

  1. The problem does not occur when I do not differentiate between 'type' (no use of hue='type' ).

Here's the full stacktrace:

posx and posy should be finite values
posx and posy should be finite values
/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/numpy/core/fromnumeric.py:83: RuntimeWarning: invalid value encountered in reduce
  return ufunc.reduce(obj, axis, dtype, out, **passkwargs)
posx and posy should be finite values
posx and posy should be finite values
posx and posy should be finite values
Traceback (most recent call last):
  File "/Users/dzieciou/projects/example/gocd/reproduce.py", line 31, in <module>
    plot.savefig("barplot.png")
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/seaborn/axisgrid.py", line 37, in savefig
    self.fig.savefig(*args, **kwargs)
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/figure.py", line 2094, in savefig
    self.canvas.print_figure(fname, **kwargs)
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/backend_bases.py", line 2075, in print_figure
    **kwargs)
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/backends/backend_agg.py", line 510, in print_png
    FigureCanvasAgg.draw(self)
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/backends/backend_agg.py", line 402, in draw
    self.figure.draw(self.renderer)
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/artist.py", line 50, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/figure.py", line 1649, in draw
    renderer, self, artists, self.suppressComposite)
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/image.py", line 138, in _draw_list_compositing_images
    a.draw(renderer)
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/artist.py", line 50, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/axes/_base.py", line 2610, in draw
    mimage._draw_list_compositing_images(renderer, self, artists)
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/image.py", line 138, in _draw_list_compositing_images
    a.draw(renderer)
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/artist.py", line 50, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/axis.py", line 1185, in draw
    ticks_to_draw = self._update_ticks(renderer)
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/axis.py", line 1023, in _update_ticks
    tick_tups = list(self.iter_ticks())  # iter_ticks calls the locator
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/axis.py", line 967, in iter_ticks
    majorLocs = self.major.locator()
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/ticker.py", line 1985, in __call__
    return self.tick_values(vmin, vmax)
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/ticker.py", line 1993, in tick_values
    locs = self._raw_ticks(vmin, vmax)
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/ticker.py", line 1932, in _raw_ticks
    nbins = np.clip(self.axis.get_tick_space(),
  File "/Users/dzieciou/virtualenvs/seaborn/lib/python3.7/site-packages/matplotlib/axis.py", line 2543, in get_tick_space
    return int(np.floor(length / size))
ValueError: cannot convert float NaN to integer

Note that this is neither really a bug, nor is it related to the linked bug, which is indeed fixed.

One could argue that there should be a better error message when plotting text at nan coordinates though.

Before looking at the error, it seems you have another problem in your code, which is that you set the x coordinate of the text to the width of the bar. They are usually unrelated and you might have meant to use p.get_x() instead.

Now two options:

1. Don't position text at invalid coordinates.

    import numpy as np
    import matplotlib.pyplot as plt
    import pandas as pd
    import seaborn as sns

    d = {'job': list(range(1, 3)),
         'duration': [83066.639344, 820.700000],
         'type': ['A', 'B']}

    df = pd.DataFrame(d)

    plot = sns.catplot(x="duration", y="job", data=df, hue='type',
                       color="b", kind="bar", height=3, aspect=4)

    ax = plot.axes.flat[0]
    for p in plt.gca().patches:
        height = np.nan_to_num(p.get_height(), 0)
        ax.text(p.get_x(), p.get_y() + height/2., "My text")

    plot.savefig("barplot.png")
    plt.show()

2. Don't use bbox_inches="tight" .

If you want to keep your code as it is, you may workaround this by not setting the bbox_inches="tight" options in seaborn's savefig .

plot.savefig("barplot.png", bbox_inches=None)

Or use matplotlib's savefig option

plot.fig.savefig("barplot.png")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM