Pandas: Period Index plotting method, non standard intervals

Created on 29 Nov 2016  路  7Comments  路  Source: pandas-dev/pandas

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)

Bug Period Visualization

Most helpful comment

@Casyfill pleas add a thumbs up to the issue description itself, by clicking the 'reaction' icon and selecting 'thumbs up'. That is the same as +1, but actually helps to add meaningful metadata to the thread.

All 7 comments

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

this would require a community pull request to fix

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

@Casyfill pleas add a thumbs up to the issue description itself, by clicking the 'reaction' icon and selecting 'thumbs up'. That is the same as +1, but actually helps to add meaningful metadata to the thread.

Any news on this?

@scootty1 pandas is completely volunteer

you are welcome to make a PE for any issue

@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...

Was this page helpful?
0 / 5 - 0 ratings