Skip to content

Commit e42513b

Browse files
committed
BUG/CLN: Refactoring tsplot
1 parent 8a9e643 commit e42513b

File tree

3 files changed

+164
-110
lines changed

3 files changed

+164
-110
lines changed

pandas/tools/plotting.py

+23-23
Original file line numberDiff line numberDiff line change
@@ -1665,21 +1665,35 @@ def __init__(self, data, **kwargs):
16651665

16661666
def _is_ts_plot(self):
16671667
# this is slightly deceptive
1668-
return not self.x_compat and self.use_index and self._use_dynamic_x()
1669-
1670-
def _use_dynamic_x(self):
1671-
from pandas.tseries.plotting import _use_dynamic_x
1672-
return _use_dynamic_x(self._get_ax(0), self.data)
1668+
from pandas.tseries.plotting import _use_dynamic_x, _get_freq
1669+
ax = self._get_ax(0)
1670+
freq, ax_freq = _get_freq(ax, self.data)
1671+
dynamic_x = _use_dynamic_x(ax, self.data)
1672+
return (not self.x_compat and self.use_index and
1673+
dynamic_x and freq is not None)
16731674

16741675
def _make_plot(self):
16751676
if self._is_ts_plot():
1677+
print('tsplot-path!!')
16761678
from pandas.tseries.plotting import _maybe_convert_index
16771679
data = _maybe_convert_index(self._get_ax(0), self.data)
16781680

1681+
from pandas.tseries.plotting import _maybe_resample
1682+
for ax in self.axes:
1683+
# resample data and replot if required
1684+
kwds = self.kwds.copy()
1685+
data = _maybe_resample(data, ax, kwds)
1686+
16791687
x = data.index # dummy, not used
16801688
plotf = self._ts_plot
16811689
it = self._iter_data(data=data, keep_index=True)
16821690
else:
1691+
print('xcompat-path!!')
1692+
from pandas.tseries.plotting import _replot_x_compat
1693+
for ax in self.axes:
1694+
# if ax holds _plot_data, replot them on the x_compat scale
1695+
_replot_x_compat(ax)
1696+
16831697
x = self._get_xticks(convert_period=True)
16841698
plotf = self._plot
16851699
it = self._iter_data()
@@ -1723,24 +1737,15 @@ def _plot(cls, ax, x, y, style=None, column_num=None,
17231737

17241738
@classmethod
17251739
def _ts_plot(cls, ax, x, data, style=None, **kwds):
1726-
from pandas.tseries.plotting import (_maybe_resample,
1727-
_decorate_axes,
1728-
format_dateaxis)
1740+
from pandas.tseries.plotting import _maybe_resample, format_dateaxis
17291741
# accept x to be consistent with normal plot func,
17301742
# x is not passed to tsplot as it uses data.index as x coordinate
17311743
# column_num must be in kwds for stacking purpose
1732-
freq, data = _maybe_resample(data, ax, kwds)
17331744

1734-
# Set ax with freq info
1735-
_decorate_axes(ax, freq, kwds)
1736-
# digging deeper
1737-
if hasattr(ax, 'left_ax'):
1738-
_decorate_axes(ax.left_ax, freq, kwds)
1739-
if hasattr(ax, 'right_ax'):
1740-
_decorate_axes(ax.right_ax, freq, kwds)
1745+
data = _maybe_resample(data, ax, kwds)
17411746
ax._plot_data.append((data, cls._kind, kwds))
1742-
17431747
lines = cls._plot(ax, data.index, data.values, style=style, **kwds)
1748+
17441749
# set date formatter, locators and rescale limits
17451750
format_dateaxis(ax, ax.freq)
17461751
return lines
@@ -1790,14 +1795,9 @@ def _update_stacker(cls, ax, stacking_id, values):
17901795
ax._stacker_neg_prior[stacking_id] += values
17911796

17921797
def _post_plot_logic(self, ax, data):
1793-
condition = (not self._use_dynamic_x() and
1794-
data.index.is_all_dates and
1795-
not self.subplots or
1796-
(self.subplots and self.sharex))
1797-
17981798
index_name = self._get_index_name()
17991799

1800-
if condition:
1800+
if not self._is_ts_plot():
18011801
# irregular TS rotated 30 deg. by default
18021802
# probably a better place to check / set this.
18031803
if not self._rot_set:

pandas/tseries/plotting.py

+100-62
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
from matplotlib import pylab
1111
from pandas.tseries.period import Period
12+
import numpy as np
13+
1214
from pandas.tseries.offsets import DateOffset
1315
import pandas.tseries.frequencies as frequencies
1416
from pandas.tseries.index import DatetimeIndex
@@ -41,18 +43,14 @@ def tsplot(series, plotf, ax=None, **kwargs):
4143
import matplotlib.pyplot as plt
4244
ax = plt.gca()
4345

44-
freq, series = _maybe_resample(series, ax, kwargs)
45-
46-
# Set ax with freq info
47-
_decorate_axes(ax, freq, kwargs)
46+
series = _maybe_resample(series, ax, kwargs)
4847
ax._plot_data.append((series, plotf, kwargs))
4948
lines = plotf(ax, series.index._mpl_repr(), series.values, **kwargs)
5049

5150
# set date formatter, locators and rescale limits
5251
format_dateaxis(ax, ax.freq)
5352
return lines
5453

55-
5654
def _maybe_resample(series, ax, kwargs):
5755
# resample against axes freq if necessary
5856
freq, ax_freq = _get_freq(ax, series)
@@ -75,11 +73,20 @@ def _maybe_resample(series, ax, kwargs):
7573
series = getattr(series.resample(ax_freq), how)().dropna()
7674
freq = ax_freq
7775
elif frequencies.is_subperiod(freq, ax_freq) or _is_sub(freq, ax_freq):
78-
_upsample_others(ax, freq, kwargs)
76+
_upsample_others(ax, freq)
7977
ax_freq = freq
8078
else: # pragma: no cover
8179
raise ValueError('Incompatible frequency conversion')
82-
return freq, series
80+
81+
# Set ax with freq info
82+
_decorate_axes(ax, freq)
83+
# digging deeper
84+
if hasattr(ax, 'left_ax'):
85+
_decorate_axes(ax.left_ax, freq)
86+
elif hasattr(ax, 'right_ax'):
87+
_decorate_axes(ax.right_ax, freq)
88+
89+
return series
8390

8491

8592
def _is_sub(f1, f2):
@@ -92,81 +99,112 @@ def _is_sup(f1, f2):
9299
(f2.startswith('W') and frequencies.is_superperiod(f1, 'D')))
93100

94101

95-
def _upsample_others(ax, freq, kwargs):
96-
legend = ax.get_legend()
97-
lines, labels = _replot_ax(ax, freq, kwargs)
98-
_replot_ax(ax, freq, kwargs)
102+
def _get_plot_func(plotf):
103+
""" get actual function when plotf is specified with str """
104+
# for tsplot
105+
if isinstance(plotf, compat.string_types):
106+
from pandas.tools.plotting import _plot_klass
107+
plotf = _plot_klass[plotf]._plot
108+
return plotf
109+
110+
111+
def _upsample_others(ax, freq):
99112

100-
other_ax = None
113+
def _replot(ax):
114+
data = getattr(ax, '_plot_data', None)
115+
if data is None:
116+
return
117+
118+
# preserve legend
119+
leg = ax.get_legend()
120+
handles, labels = ax.get_legend_handles_labels()
121+
122+
ax._plot_data = []
123+
ax.clear()
124+
_decorate_axes(ax, freq)
125+
126+
for series, plotf, kwds in data:
127+
series = series.copy()
128+
idx = series.index.asfreq(freq, how='s')
129+
series.index = idx
130+
ax._plot_data.append((series, plotf, kwds))
131+
132+
plotf = _get_plot_func(plotf)
133+
plotf(ax, series.index._mpl_repr(), series.values, **kwds)
134+
135+
136+
if leg is not None:
137+
ax.legend(handles, labels, title=leg.get_title().get_text())
138+
139+
_replot(ax)
101140
if hasattr(ax, 'left_ax'):
102-
other_ax = ax.left_ax
103-
if hasattr(ax, 'right_ax'):
104-
other_ax = ax.right_ax
141+
_replot(ax.left_ax)
142+
elif hasattr(ax, 'right_ax'):
143+
_replot(ax.right_ax)
105144

106-
if other_ax is not None:
107-
rlines, rlabels = _replot_ax(other_ax, freq, kwargs)
108-
lines.extend(rlines)
109-
labels.extend(rlabels)
110145

111-
if (legend is not None and kwargs.get('legend', True) and
112-
len(lines) > 0):
113-
title = legend.get_title().get_text()
114-
if title == 'None':
115-
title = None
116-
ax.legend(lines, labels, loc='best', title=title)
146+
def _replot_x_compat(ax):
117147

148+
def _replot(ax):
149+
data = getattr(ax, '_plot_data', None)
150+
if data is None:
151+
return
118152

119-
def _replot_ax(ax, freq, kwargs):
120-
data = getattr(ax, '_plot_data', None)
153+
# preserve legend
154+
leg = ax.get_legend()
155+
handles, labels = ax.get_legend_handles_labels()
121156

122-
# clear current axes and data
123-
ax._plot_data = []
124-
ax.clear()
157+
ax._plot_data = None
158+
ax.clear()
125159

126-
_decorate_axes(ax, freq, kwargs)
160+
_decorate_axes(ax, None)
127161

128-
lines = []
129-
labels = []
130-
if data is not None:
131162
for series, plotf, kwds in data:
132-
series = series.copy()
133-
idx = series.index.asfreq(freq, how='S')
163+
idx = series.index.to_timestamp(how='s')
134164
series.index = idx
135-
ax._plot_data.append((series, plotf, kwds))
136165

137-
# for tsplot
138-
if isinstance(plotf, compat.string_types):
139-
from pandas.tools.plotting import _plot_klass
140-
plotf = _plot_klass[plotf]._plot
166+
plotf = _get_plot_func(plotf)
167+
plotf(ax, series.index._mpl_repr(), series, **kwds)
141168

142-
lines.append(plotf(ax, series.index._mpl_repr(),
143-
series.values, **kwds)[0])
144-
labels.append(pprint_thing(series.name))
169+
if leg is not None:
170+
ax.legend(handles, labels, title=leg.get_title().get_text())
145171

146-
return lines, labels
172+
_replot(ax)
173+
if hasattr(ax, 'left_ax'):
174+
_replot(ax.left_ax)
175+
elif hasattr(ax, 'right_ax'):
176+
_replot(ax.right_ax)
147177

148178

149-
def _decorate_axes(ax, freq, kwargs):
179+
def _decorate_axes(ax, freq):
150180
"""Initialize axes for time-series plotting"""
151181
if not hasattr(ax, '_plot_data'):
152182
ax._plot_data = []
153183

154184
ax.freq = freq
155185
xaxis = ax.get_xaxis()
156186
xaxis.freq = freq
157-
if not hasattr(ax, 'legendlabels'):
158-
ax.legendlabels = [kwargs.get('label', None)]
159-
else:
160-
ax.legendlabels.append(kwargs.get('label', None))
161187
ax.view_interval = None
162188
ax.date_axis_info = None
163189

164190

165-
def _get_freq(ax, series):
191+
def _get_index_freq(data):
192+
freq = getattr(data.index, 'freq', None)
193+
if freq is None:
194+
freq = getattr(data.index, 'inferred_freq', None)
195+
if freq == 'B':
196+
weekdays = np.unique(data.index.dayofweek)
197+
if (5 in weekdays) or (6 in weekdays):
198+
freq = None
199+
return freq
200+
201+
202+
def _get_freq(ax, data):
166203
# get frequency from data
167-
freq = getattr(series.index, 'freq', None)
204+
freq = getattr(data.index, 'freq', None)
205+
168206
if freq is None:
169-
freq = getattr(series.index, 'inferred_freq', None)
207+
freq = getattr(data.index, 'inferred_freq', None)
170208

171209
ax_freq = getattr(ax, 'freq', None)
172210
if ax_freq is None:
@@ -175,17 +213,17 @@ def _get_freq(ax, series):
175213
elif hasattr(ax, 'right_ax'):
176214
ax_freq = getattr(ax.right_ax, 'freq', None)
177215

178-
# use axes freq if no data freq
179-
if freq is None:
180-
freq = ax_freq
216+
if freq is not None:
217+
# get the period frequency
218+
if isinstance(freq, DateOffset):
219+
freq = freq.rule_code
220+
else:
221+
freq = frequencies.get_base_alias(freq)
181222

182-
# get the period frequency
183-
if isinstance(freq, DateOffset):
184-
freq = freq.rule_code
185-
else:
186-
freq = frequencies.get_base_alias(freq)
223+
if freq is None:
224+
raise ValueError('Could not get frequency alias for plotting')
225+
freq = frequencies.get_period_alias(freq)
187226

188-
freq = frequencies.get_period_alias(freq)
189227
return freq, ax_freq
190228

191229

0 commit comments

Comments
 (0)