Skip to content

Period Index plotting method, non standard intervals #14763

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
quebbs opened this issue Nov 29, 2016 · 7 comments · Fixed by #26241
Closed

Period Index plotting method, non standard intervals #14763

quebbs opened this issue Nov 29, 2016 · 7 comments · Fixed by #26241
Labels
Bug Period Period data type Visualization plotting
Milestone

Comments

@quebbs
Copy link

quebbs commented Nov 29, 2016

Code Sample, a copy-pastable example if possible

import numpy as np, pandas as pd
idx = pd.period_range('2000-01-01', '2000-01-05', freq='6H')
s = pd.Series(np.random.randn(len(idx)), index=idx)
s.plot()

Problem description

Plotting a period index with 'D' or 'H' works fine, but changing the interval to 6-hr throws an error: IncompatibleFrequency: Input has different freq=6H from PeriodIndex(freq=H)

Expected Output

Should make a nice plot. Using the same example above with alternative frequencies of 'D' or 'H' plots as expected, but 3H, 6H, and even 24H throw an error.

Output

--------------------------------------------------------------------------- TypeError Traceback (most recent call last) C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\pandas\tseries\period.py in _from_arraylike(cls, data, freq, tz) 263 try: --> 264 data = com._ensure_int64(data) 265 except (TypeError, ValueError):

pandas\src\generated.pyx in pandas.algos.ensure_int64 (pandas\algos.c:64659)()

pandas\src\generated.pyx in pandas.algos.ensure_int64 (pandas\algos.c:64550)()

TypeError: int() argument must be a string, a bytes-like object or a number, not 'pandas._period.Period'

During handling of the above exception, another exception occurred:

IncompatibleFrequency Traceback (most recent call last)
in ()
2 idx = ps.period_range('2000-01-01', '2000-01-05', freq='6H')
3 s = ps.Series(np.random.randn(len(idx)), index=idx)
----> 4 s.plot()

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\pandas\tools\plotting.py in call(self, kind, ax, figsize, use_index, title, grid, legend, style, logx, logy, loglog, xticks, yticks, xlim, ylim, rot, fontsize, colormap, table, yerr, xerr, label, secondary_y, **kwds)
3564 colormap=colormap, table=table, yerr=yerr,
3565 xerr=xerr, label=label, secondary_y=secondary_y,
-> 3566 **kwds)
3567 call.doc = plot_series.doc
3568

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\pandas\tools\plotting.py in plot_series(data, kind, ax, figsize, use_index, title, grid, legend, style, logx, logy, loglog, xticks, yticks, xlim, ylim, rot, fontsize, colormap, table, yerr, xerr, label, secondary_y, **kwds)
2643 yerr=yerr, xerr=xerr,
2644 label=label, secondary_y=secondary_y,
-> 2645 **kwds)
2646
2647

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\pandas\tools\plotting.py in _plot(data, x, y, subplots, ax, kind, **kwds)
2439 plot_obj = klass(data, subplots=subplots, ax=ax, kind=kind, **kwds)
2440
-> 2441 plot_obj.generate()
2442 plot_obj.draw()
2443 return plot_obj.result

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\pandas\tools\plotting.py in generate(self)
1026 self._compute_plot_data()
1027 self._setup_subplots()
-> 1028 self._make_plot()
1029 self._add_table()
1030 self._make_legend()

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\pandas\tools\plotting.py in _make_plot(self)
1705 stacking_id=stacking_id,
1706 is_errorbar=is_errorbar,
-> 1707 **kwds)
1708 self._add_legend_handle(newlines[0], label, index=i)
1709

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\pandas\tools\plotting.py in _ts_plot(cls, ax, x, data, style, **kwds)
1743 ax._plot_data.append((data, cls._kind, kwds))
1744
-> 1745 lines = cls._plot(ax, data.index, data.values, style=style, **kwds)
1746 # set date formatter, locators and rescale limits
1747 format_dateaxis(ax, ax.freq)

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\pandas\tools\plotting.py in _plot(cls, ax, x, y, style, column_num, stacking_id, **kwds)
1720 cls._initialize_stacker(ax, stacking_id, len(y))
1721 y_values = cls._get_stacked_values(ax, stacking_id, y, kwds['label'])
-> 1722 lines = MPLPlot._plot(ax, x, y_values, style=style, **kwds)
1723 cls._update_stacker(ax, stacking_id, y)
1724 return lines

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\pandas\tools\plotting.py in _plot(cls, ax, x, y, style, is_errorbar, **kwds)
1340 else:
1341 args = (x, y)
-> 1342 return ax.plot(*args, **kwds)
1343
1344 def _get_index_name(self):

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\matplotlib_init_.py in inner(ax, *args, **kwargs)
1816 warnings.warn(msg % (label_namer, func.name),
1817 RuntimeWarning, stacklevel=2)
-> 1818 return func(ax, *args, **kwargs)
1819 pre_doc = inner.doc
1820 if pre_doc is None:

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\matplotlib\axes_axes.py in plot(self, *args, **kwargs)
1381
1382 for line in self._get_lines(*args, **kwargs):
-> 1383 self.add_line(line)
1384 lines.append(line)
1385

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\matplotlib\axes_base.py in add_line(self, line)
1701 line.set_clip_path(self.patch)
1702
-> 1703 self._update_line_limits(line)
1704 if not line.get_label():
1705 line.set_label('_line%d' % len(self.lines))

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\matplotlib\axes_base.py in _update_line_limits(self, line)
1723 Figures out the data limit of the given line, updating self.dataLim.
1724 """
-> 1725 path = line.get_path()
1726 if path.vertices.size == 0:
1727 return

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\matplotlib\lines.py in get_path(self)
936 """
937 if self._invalidy or self._invalidx:
--> 938 self.recache()
939 return self._path
940

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\matplotlib\lines.py in recache(self, always)
619 def recache(self, always=False):
620 if always or self._invalidx:
--> 621 xconv = self.convert_xunits(self._xorig)
622 if ma.isMaskedArray(self.xorig):
623 x = ma.asarray(xconv, np.float
).filled(np.nan)

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\matplotlib\artist.py in convert_xunits(self, x)
188 if ax is None or ax.xaxis is None:
189 return x
--> 190 return ax.xaxis.convert_units(x)
191
192 def convert_yunits(self, y):

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\matplotlib\axis.py in convert_units(self, x)
1442 return x
1443
-> 1444 ret = self.converter.convert(x, self.units, self)
1445 return ret
1446

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\pandas\tseries\converter.py in convert(values, units, axis)
120 return values.map(lambda x: get_datevalue(x, axis.freq))
121 if com.is_period_arraylike(values):
--> 122 return PeriodIndex(values, freq=axis.freq).values
123 if isinstance(values, (list, tuple, np.ndarray, Index)):
124 return [get_datevalue(x, axis.freq) for x in values]

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\pandas\tseries\period.py in new(cls, data, ordinal, freq, start, end, periods, copy, name, tz, **kwargs)
188 freq, kwargs)
189 else:
--> 190 ordinal, freq = cls._from_arraylike(data, freq, tz)
191 data = np.array(ordinal, dtype=np.int64, copy=False)
192

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\pandas\tseries\period.py in _from_arraylike(cls, data, freq, tz)
265 except (TypeError, ValueError):
266 data = com._ensure_object(data)
--> 267 data = _get_ordinals(data, freq)
268
269 return data, freq

C:\Users\jaq\AppData\Local\Continuum\Anaconda\envs\py35\lib\site-packages\pandas\tseries\period.py in _get_ordinals(data, freq)
42 f = lambda x: Period(x, freq=freq).ordinal
43 if isinstance(data[0], Period):
---> 44 return period.extract_ordinals(data, freq)
45 else:
46 return lib.map_infer(data, f)

pandas\src\period.pyx in pandas._period.extract_ordinals (pandas\src\period.c:7030)()

IncompatibleFrequency: Input has different freq=6H from PeriodIndex(freq=H)

@jorisvandenbossche jorisvandenbossche added Bug Period Period data type Visualization plotting labels Nov 29, 2016
jeffcarey added a commit to jeffcarey/pandas that referenced this issue Dec 7, 2016
jeffcarey added a commit to jeffcarey/pandas that referenced this issue Dec 10, 2016
@jreback jreback added this to the Next Major Release milestone Dec 16, 2016
@brylie
Copy link

brylie commented Oct 11, 2017

Would it be possible to include this in the 0.20.4 release? It is making it difficult to plot timeseries data:

https://stackoverflow.com/questions/43206554/typeerror-float-argument-must-be-a-string-or-a-number-not-period

I am getting Type error even when the time series period is set to 'D':
screenshot from 2017-10-11 14-26-35

@jreback
Copy link
Contributor

jreback commented Oct 11, 2017

this would require a community pull request to fix

@brylie
Copy link

brylie commented Oct 11, 2017

OK, I am just not sure of the underlying cause. Would you help point me in the right direction?

@brylie

This comment has been minimized.

@JoElfner
Copy link
Contributor

Any news on this?

@jreback
Copy link
Contributor

jreback commented Apr 26, 2019

@scootty1 pandas is completely volunteer

you are welcome to make a PE for any issue

@JoElfner
Copy link
Contributor

@jreback I know. I tried to find where the bug is coming from and since it is my first "guess" on a matter like this, it took me quite some time...
One thing I found confusing was somewhere between pandas/plotting/_timeseries._maybe_resample and/or pandas/plotting/_core._ts_plot. The frequency of the x-axis ax.xaxis.freq seems to be set to the rule code, for example H for a frequency of the index of 6H. But this also seems to be the case for plotting DateTimeIndex with a freq of 6H, so I guess this is ok.

Imho the bug is located in pandas/plotting/_timeseries._maybe_convert_index. Line 266 only checks for DatetimeIndex. It is assumed that the PeriodIndex already has the correct ax freq set. I changed the code in _maybe_convert_index to:

def _maybe_convert_index(ax, data):
    # tsplot converts automatically, but don't want to convert index
    # over and over for DataFrames
    if isinstance(data.index, (ABCDatetimeIndex, ABCPeriodIndex)):
        freq = getattr(data.index, 'freq', None)

        if freq is None:
            freq = getattr(data.index, 'inferred_freq', None)
        if isinstance(freq, DateOffset):
            freq = freq.rule_code

        if freq is None:
            freq = _get_ax_freq(ax)

        if freq is None:
            raise ValueError('Could not get frequency alias for plotting')

        freq = get_base_alias(freq)
        freq = frequencies.get_period_alias(freq)
        if isinstance(data.index, ABCDatetimeIndex):
            data = data.to_period(freq=freq)
        elif isinstance(data.index, ABCPeriodIndex):
            data.index = data.index.asfreq(freq)
    return data

Plotting some tests seems to be working...

I'll try to make a pull request. But since this is my first pull request, it may take me some time...

@jreback jreback modified the milestones: Contributions Welcome, 0.25.0 May 18, 2019
TomAugspurger pushed a commit that referenced this issue May 28, 2019
…olves #14763 (#26241)

* BUG: Enable plotting with PeriodIndex with arbitrary frequencies, resolves #14763
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment