Skip to content

Plot mixed freq #1519

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
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 31 additions & 14 deletions pandas/tools/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,13 +485,16 @@ def plt(self):

_need_to_set_index = False

def _get_xticks(self):
def _get_xticks(self, convert_period=False):
index = self.data.index
is_datetype = index.inferred_type in ('datetime', 'date',
'datetime64')

if self.use_index:
if index.is_numeric() or is_datetype:
if convert_period and isinstance(index, PeriodIndex):
index = index.to_timestamp()
x = index._mpl_repr()
elif index.is_numeric() or is_datetype:
"""
Matplotlib supports numeric values or datetime objects as
xaxis values. Taking LBYL approach here, by the time
Expand Down Expand Up @@ -594,30 +597,41 @@ class LinePlot(MPLPlot):
def __init__(self, data, **kwargs):
MPLPlot.__init__(self, data, **kwargs)

@property
def has_ts_index(self):
def _index_freq(self):
from pandas.core.frame import DataFrame
if isinstance(self.data, (Series, DataFrame)):
freq = (getattr(self.data.index, 'freq', None)
or getattr(self.data.index, 'inferred_freq', None))
return (freq is not None) and self._has_dynamic_index_freq(freq)
return False
return freq

def _has_dynamic_index_freq(self, freq):
def _is_dynamic_freq(self, freq):
if isinstance(freq, DateOffset):
freq = freq.rule_code
else:
freq = get_base_alias(freq)
freq = get_period_alias(freq)
return freq is not None

def _use_dynamic_x(self):
freq = self._index_freq()

ax, _ = self._get_ax_and_style(0)
ax_freq = getattr(ax, 'freq', None)
if freq is None: # convert irregular if axes has freq info
freq = ax_freq
else: # do not use tsplot if irregular was plotted first
if (ax_freq is None) and (len(ax.get_lines()) > 0):
return False

return (freq is not None) and self._is_dynamic_freq(freq)

def _make_plot(self):
# this is slightly deceptive
if self.use_index and self.has_ts_index:
if self.use_index and self._use_dynamic_x():
data = self._maybe_convert_index(self.data)
self._make_ts_plot(data)
else:
x = self._get_xticks()
x = self._get_xticks(convert_period=True)

plotf = self._get_plot_function()

Expand Down Expand Up @@ -646,14 +660,17 @@ def _maybe_convert_index(self, data):
if (isinstance(data.index, DatetimeIndex) and
isinstance(data, DataFrame)):
freq = getattr(data.index, 'freqstr', None)
if isinstance(freq, DateOffset):
freq = freq.rule_code

freq = get_period_alias(freq)

if freq is None and hasattr(data.index, 'inferred_freq'):
freq = data.index.inferred_freq
if freq is None:
freq = getattr(data.index, 'inferred_freq', None)

if isinstance(freq, DateOffset):
freq = freq.rule_code
if freq is None:
ax, _ = self._get_ax_and_style(0)
freq = getattr(ax, 'freq', None)

data = DataFrame(data.values,
index=data.index.to_period(freq=freq),
Expand Down Expand Up @@ -692,7 +709,7 @@ def _post_plot_logic(self):
else:
self.axes[0].legend(loc='best')

condition = (not self.has_ts_index
condition = (not self._use_dynamic_x
and df.index.is_all_dates
and not self.subplots
or (self.subplots and self.sharex))
Expand Down
86 changes: 62 additions & 24 deletions pandas/tseries/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,31 +43,36 @@ def tsplot(series, plotf, **kwargs):

"""
# Used inferred freq is possible, need a test case for inferred
freq = getattr(series.index, 'freq', None)
if freq is None and hasattr(series.index, 'inferred_freq'):
freq = series.index.inferred_freq
if 'ax' in kwargs:
ax = kwargs.pop('ax')
else:
import matplotlib.pyplot as plt
ax = plt.gca()

if isinstance(freq, DateOffset):
freq = freq.rule_code
freq = _get_freq(ax, series)
# resample against axes freq if necessary
if freq is None: # pragma: no cover
raise ValueError('Cannot use dynamic axis without frequency info')
else:
freq = frequencies.get_base_alias(freq)
ax_freq = getattr(ax, 'freq', None)
if (ax_freq is not None) and (freq != ax_freq):
if frequencies.is_subperiod(freq, ax_freq): # downsample
how = kwargs.pop('how', 'last')
series = series.resample(ax_freq, how=how)
elif frequencies.is_superperiod(freq, ax_freq):
series = series.resample(ax_freq)
else: # one freq is weekly
how = kwargs.pop('how', 'last')
series = series.resample('D', how=how, fill_method='pad')
series = series.resample(ax_freq, how=how, fill_method='pad')
freq = ax_freq

freq = frequencies.get_period_alias(freq)
# Convert DatetimeIndex to PeriodIndex
if isinstance(series.index, DatetimeIndex):
series = series.to_period(freq=freq)

if freq != series.index.freq:
series = series.asfreq(freq)

style = kwargs.pop('style', None)

if 'ax' in kwargs:
ax = kwargs.pop('ax')
else:
import matplotlib.pyplot as plt
ax = plt.gca()

# Specialized ts plotting attributes for Axes
ax.freq = freq
xaxis = ax.get_xaxis()
Expand All @@ -78,26 +83,59 @@ def tsplot(series, plotf, **kwargs):
ax.date_axis_info = None

# format args and lot
args = _maybe_mask(series)

if style is not None:
args.append(style)

plotf(ax, *args, **kwargs)

format_dateaxis(ax, ax.freq)

left, right = _get_xlim(ax.get_lines())
ax.set_xlim(left, right)

return ax

def _maybe_mask(series):
mask = isnull(series)
if mask.any():
masked_array = np.ma.array(series.values)
masked_array = np.ma.masked_where(mask, masked_array)
args = [series.index, masked_array]
else:
args = [series.index, series]
return args

if style is not None:
args.append(style)
def _get_freq(ax, series):
# get frequency from data
freq = getattr(series.index, 'freq', None)
if freq is None:
freq = getattr(series.index, 'inferred_freq', None)

plotf(ax, *args, **kwargs)
ax_freq = getattr(ax, 'freq', None)

format_dateaxis(ax, ax.freq)
# use axes freq if no data freq
if freq is None:
freq = ax_freq

left = series.index[0] #get_datevalue(series.index[0], freq)
right = series.index[-1] #get_datevalue(series.index[-1], freq)
ax.set_xlim(left, right)
# get the period frequency
if isinstance(freq, DateOffset):
freq = freq.rule_code
else:
freq = frequencies.get_base_alias(freq)

return ax
freq = frequencies.get_period_alias(freq)

return freq

def _get_xlim(lines):
left, right = np.inf, -np.inf
for l in lines:
x = l.get_xdata()
left = min(x[0].ordinal, left)
right = max(x[-1].ordinal, right)
return left, right

def get_datevalue(date, freq):
if isinstance(date, Period):
Expand Down
Loading