def setup_datetime(self, index=None): """ Setup the int based matplotlib x-index to translate to datetime Separated out here to share between plot and candlestick """ if index is None: index = self.index is_datetime = self.is_datetime() if self.formatter is None and self.skip_na and is_datetime: self.locator = TimestampLocator(index) self.formatter = TimestampFormatter(index, self.locator) self.set_formatter() # reupdate index self.locator.index = index self.formatter.index = index
class Grapher(object): def __init__(self, ax, skip_na=True, sharex=None): self.index = None self.formatter = None self.locator = None self.ax = ax self.skip_na = skip_na self.sharex = sharex self.styler = cstyler.styler() self.yaxes = {} @property def right_ax(self): return self.yaxes.get('right', None) def is_datetime(self): return self.index.inferred_type in ('datetime', 'date', 'datetime64') def find_ax(self, secondary_y, kwargs): """ multiple y-axis support. stay backward compatible with secondary_y Note: we take in the actual kwargs because we want to pop('yax') to affect the callers kwargs """ yax = kwargs.pop('yax', None) if yax and secondary_y: raise Exception('yax and secondary_y should not both be set') if secondary_y: yax = 'right' ax = self.ax if yax: ax = self.get_yax(yax) return ax def plot(self, label, series, index=None, method=None, secondary_y=False, **kwargs): # use default styler if one is not passed in styler = kwargs.pop('styler', self.styler) if styler: style_dict = next(styler) # note we do it this way so explicit args passed in kwargs # override style_dict kwargs = dict(style_dict.items() + kwargs.items()) plot_index = self.index if self.sharex is not None: plot_index = self.sharex series = process_series(series, plot_index, series_index=index, method=method) # first plot, set index if self.index is None: self.index = series.index is_datetime = self.is_datetime() if is_datetime: self.setup_datetime(self.index) plot_series = series if label is not None: kwargs['label'] = label xax = self.index if self.skip_na and is_datetime: xax = np.arange(len(self.index)) self.formatter.index = self.index ax = self.find_ax(secondary_y, kwargs) ax.plot(xax, plot_series, **kwargs) # generate combined legend lines, labels = self.consolidate_legend() self.ax.legend(lines, labels, loc=0) if is_datetime: # plot empty space for leading NaN and trailing NaN # not sure if I should only call this for is_datetime plt.xlim(0, len(self.index)-1) def consolidate_legend(self): """ consolidate the legends from all axes and merge into one """ lines, labels = self.ax.get_legend_handles_labels() for k, ax in self.yaxes.iteritems(): new_lines, new_labels = ax.get_legend_handles_labels() lines = lines + new_lines labels = labels + new_labels return lines, labels def get_right_ax(self): return self.get_yax('right') def get_yax(self, name): """ Get a yaxis keyed by name. Returns a newly generted twinx if it doesn't exist """ def make_patch_spines_invisible(ax): ax.set_frame_on(True) ax.patch.set_visible(False) for sp in ax.spines.itervalues(): sp.set_visible(False) size = len(self.yaxes) if name not in self.yaxes: ax = self.ax.twinx() self.yaxes[name] = ax # set spine ax.spines["right"].set_position(("outward", 50 * size)) make_patch_spines_invisible(ax) ax.spines["right"].set_visible(True) ax.set_ylabel(name) self.set_formatter() return self.yaxes[name] def setup_datetime(self, index=None): """ Setup the int based matplotlib x-index to translate to datetime Separated out here to share between plot and candlestick """ if index is None: index = self.index is_datetime = self.is_datetime() if self.formatter is None and self.skip_na and is_datetime: self.locator = TimestampLocator(index) self.formatter = TimestampFormatter(index, self.locator) self.set_formatter() # reupdate index self.locator.index = index self.formatter.index = index def set_index(self, index): if self.index is not None: raise Exception("Cannot set index if index already exists") self.index = index def set_formatter(self): """ quick call to reset locator/formatter when lost. i.e. boxplot """ if self.formatter: # set to xaxis ax = self.ax ax.xaxis.set_major_locator(self.locator) ax.xaxis.set_major_formatter(self.formatter.ticker_func) ax.xaxis.grid(True) def set_xticks(self, xticks): # freq if isinstance(xticks, basestring): self.locator.freq = xticks self.locator.set_xticks(None) else: self.locator.set_xticks(xticks) self.locator.freq = None def plot_markers(self, label, series, yvalues=None, xindex=None, **kwargs): if yvalues is not None: series = process_signal(series, yvalues) props = {} props['linestyle'] = 'None' props['marker'] = 'o' props['markersize'] = 10 props.update(kwargs) if xindex is not None: series = series.copy() series.index = xindex self.plot(label, series, **props) def line(self, val, *args, **kwargs): """ print horizontal line """ self.plot(None, val, *args, **kwargs)
class Grapher(object): def __init__(self, ax, skip_na=True, sharex=None): self.index = None self.formatter = None self.locator = None self.ax = ax self.skip_na = skip_na self.sharex = sharex self.styler = cstyler.styler() self.yaxes = {} @property def right_ax(self): return self.yaxes.get('right', None) def is_datetime(self): return self.index.inferred_type in ('datetime', 'date', 'datetime64') def find_ax(self, secondary_y, kwargs): """ multiple y-axis support. stay backward compatible with secondary_y Note: we take in the actual kwargs because we want to pop('yax') to affect the callers kwargs """ yax = kwargs.pop('yax', None) if yax and secondary_y: raise Exception('yax and secondary_y should not both be set') if secondary_y: yax = 'right' ax = self.ax if yax: ax = self.get_yax(yax) return ax def plot(self, label, series, index=None, method=None, secondary_y=False, **kwargs): # use default styler if one is not passed in styler = kwargs.pop('styler', self.styler) if styler: style_dict = next(styler) # note we do it this way so explicit args passed in kwargs # override style_dict kwargs = dict(style_dict.items() + kwargs.items()) plot_index = self.index if self.sharex is not None: plot_index = self.sharex series = process_series(series, plot_index, series_index=index, method=method) # first plot, set index if self.index is None: self.index = series.index is_datetime = self.is_datetime() if is_datetime: self.setup_datetime(self.index) plot_series = series if label is not None: kwargs['label'] = label xax = self.index if self.skip_na and is_datetime: xax = np.arange(len(self.index)) self.formatter.index = self.index ax = self.find_ax(secondary_y, kwargs) ax.plot(xax, plot_series, **kwargs) # generate combined legend lines, labels = self.consolidate_legend() self.ax.legend(lines, labels, loc=0) if is_datetime: # plot empty space for leading NaN and trailing NaN # not sure if I should only call this for is_datetime plt.xlim(0, len(self.index) - 1) def consolidate_legend(self): """ consolidate the legends from all axes and merge into one """ lines, labels = self.ax.get_legend_handles_labels() for k, ax in self.yaxes.iteritems(): new_lines, new_labels = ax.get_legend_handles_labels() lines = lines + new_lines labels = labels + new_labels return lines, labels def get_right_ax(self): return self.get_yax('right') def get_yax(self, name): """ Get a yaxis keyed by name. Returns a newly generted twinx if it doesn't exist """ def make_patch_spines_invisible(ax): ax.set_frame_on(True) ax.patch.set_visible(False) for sp in ax.spines.itervalues(): sp.set_visible(False) size = len(self.yaxes) if name not in self.yaxes: ax = self.ax.twinx() self.yaxes[name] = ax # set spine ax.spines["right"].set_position(("outward", 50 * size)) make_patch_spines_invisible(ax) ax.spines["right"].set_visible(True) ax.set_ylabel(name) self.set_formatter() return self.yaxes[name] def setup_datetime(self, index=None): """ Setup the int based matplotlib x-index to translate to datetime Separated out here to share between plot and candlestick """ if index is None: index = self.index is_datetime = self.is_datetime() if self.formatter is None and self.skip_na and is_datetime: self.locator = TimestampLocator(index) self.formatter = TimestampFormatter(index, self.locator) self.set_formatter() # reupdate index self.locator.index = index self.formatter.index = index def set_index(self, index): if self.index is not None: raise Exception("Cannot set index if index already exists") self.index = index def set_formatter(self): """ quick call to reset locator/formatter when lost. i.e. boxplot """ if self.formatter: # set to xaxis ax = self.ax ax.xaxis.set_major_locator(self.locator) ax.xaxis.set_major_formatter(self.formatter.ticker_func) ax.xaxis.grid(True) def set_xticks(self, xticks): # freq if isinstance(xticks, basestring): self.locator.freq = xticks self.locator.set_xticks(None) else: self.locator.set_xticks(xticks) self.locator.freq = None def plot_markers(self, label, series, yvalues=None, xindex=None, **kwargs): if yvalues is not None: series = process_signal(series, yvalues) props = {} props['linestyle'] = 'None' props['marker'] = 'o' props['markersize'] = 10 props.update(kwargs) if xindex is not None: series = series.copy() series.index = xindex self.plot(label, series, **props) def line(self, val, *args, **kwargs): """ print horizontal line """ self.plot(None, val, *args, **kwargs)