Custom Tick Formatter for Time Series

PythonPythonBeginner
Practice Now

This tutorial is from open-source community. Access the source code

Introduction

When plotting daily data such as financial time series, one often wants to leave out days on which there is no data, such as weekends. This allows the data to be plotted at regular intervals without extra spaces for the days with no data. In this lab, we will learn how to use an 'index formatter' to achieve the desired plot.

VM Tips

After the VM startup is done, click the top left corner to switch to the Notebook tab to access Jupyter Notebook for practice.

Sometimes, you may need to wait a few seconds for Jupyter Notebook to finish loading. The validation of operations cannot be automated because of limitations in Jupyter Notebook.

If you face issues during learning, feel free to ask Labby. Provide feedback after the session, and we will promptly resolve the problem for you.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("`Python`")) -.-> python/BasicConceptsGroup(["`Basic Concepts`"]) python(("`Python`")) -.-> python/FileHandlingGroup(["`File Handling`"]) matplotlib(("`Matplotlib`")) -.-> matplotlib/BasicConceptsGroup(["`Basic Concepts`"]) matplotlib(("`Matplotlib`")) -.-> matplotlib/PlottingDataGroup(["`Plotting Data`"]) matplotlib(("`Matplotlib`")) -.-> matplotlib/PlotCustomizationGroup(["`Plot Customization`"]) python(("`Python`")) -.-> python/ControlFlowGroup(["`Control Flow`"]) python(("`Python`")) -.-> python/DataStructuresGroup(["`Data Structures`"]) python(("`Python`")) -.-> python/FunctionsGroup(["`Functions`"]) python(("`Python`")) -.-> python/ModulesandPackagesGroup(["`Modules and Packages`"]) python(("`Python`")) -.-> python/ObjectOrientedProgrammingGroup(["`Object-Oriented Programming`"]) python(("`Python`")) -.-> python/ErrorandExceptionHandlingGroup(["`Error and Exception Handling`"]) python(("`Python`")) -.-> python/PythonStandardLibraryGroup(["`Python Standard Library`"]) python(("`Python`")) -.-> python/DataScienceandMachineLearningGroup(["`Data Science and Machine Learning`"]) python/BasicConceptsGroup -.-> python/comments("`Comments`") python/FileHandlingGroup -.-> python/with_statement("`Using with Statement`") matplotlib/BasicConceptsGroup -.-> matplotlib/importing_matplotlib("`Importing Matplotlib`") matplotlib/BasicConceptsGroup -.-> matplotlib/figures_axes("`Understanding Figures and Axes`") matplotlib/PlottingDataGroup -.-> matplotlib/line_plots("`Line Plots`") matplotlib/PlotCustomizationGroup -.-> matplotlib/legend_config("`Legend Configuration`") python/ControlFlowGroup -.-> python/for_loops("`For Loops`") python/DataStructuresGroup -.-> python/lists("`Lists`") python/DataStructuresGroup -.-> python/tuples("`Tuples`") python/FunctionsGroup -.-> python/function_definition("`Function Definition`") python/FunctionsGroup -.-> python/default_arguments("`Default Arguments`") python/ModulesandPackagesGroup -.-> python/importing_modules("`Importing Modules`") python/ModulesandPackagesGroup -.-> python/standard_libraries("`Common Standard Libraries`") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("`Classes and Objects`") python/ObjectOrientedProgrammingGroup -.-> python/constructor("`Constructor`") python/ObjectOrientedProgrammingGroup -.-> python/polymorphism("`Polymorphism`") python/ObjectOrientedProgrammingGroup -.-> python/encapsulation("`Encapsulation`") python/ErrorandExceptionHandlingGroup -.-> python/catching_exceptions("`Catching Exceptions`") python/PythonStandardLibraryGroup -.-> python/date_time("`Date and Time`") python/DataScienceandMachineLearningGroup -.-> python/numerical_computing("`Numerical Computing`") python/DataScienceandMachineLearningGroup -.-> python/data_visualization("`Data Visualization`") python/FunctionsGroup -.-> python/build_in_functions("`Build-in Functions`") subgraph Lab Skills python/comments -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/with_statement -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} matplotlib/importing_matplotlib -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} matplotlib/figures_axes -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} matplotlib/line_plots -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} matplotlib/legend_config -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/for_loops -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/lists -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/tuples -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/function_definition -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/default_arguments -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/importing_modules -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/standard_libraries -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/classes_objects -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/constructor -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/polymorphism -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/encapsulation -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/catching_exceptions -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/date_time -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/numerical_computing -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/data_visualization -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} python/build_in_functions -.-> lab-48654{{"`Custom Tick Formatter for Time Series`"}} end

Import Required Libraries and Data

We first need to import the required libraries, which are matplotlib, numpy, and matplotlib.cbook. We also need to load a numpy record array from yahoo csv data with fields date, open, high, low, close, volume, adj_close from the mpl-data/sample_data directory. The record array stores the date as an np.datetime64 with a day unit ('D') in the date column. We will use this data to plot the financial time series.

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.cbook as cbook

## Load data from sample_data directory
r = cbook.get_sample_data('goog.npz')['price_data'].view(np.recarray)
r = r[:9]  ## get the first 9 days

Plotting Data with Default Gaps on Weekends

We will first plot the data with the default gaps on weekends using the plot function in matplotlib. We will also highlight gaps in the daily data using white dashed lines.

## Plot data with gaps on weekends
fig, ax1 = plt.subplots(figsize=(6, 3))
ax1.plot(r.date, r.adj_close, 'o-')

## Highlight gaps in daily data
gaps = np.flatnonzero(np.diff(r.date) > np.timedelta64(1, 'D'))
for gap in r[['date', 'adj_close']][np.stack((gaps, gaps + 1)).T]:
    ax1.plot(gap.date, gap.adj_close, 'w--', lw=2)
ax1.legend(handles=[ml.Line2D([], [], ls='--', label='Gaps in daily data')])

ax1.set_title("Plotting Data with Default Gaps on Weekends")
ax1.xaxis.set_major_locator(DayLocator())
ax1.xaxis.set_major_formatter(DateFormatter('%a'))

Creating Custom Index Formatter

To plot the data against an index that goes from 0, 1, ... len(data), we will create a custom index formatter. This formatter will format the tick marks as times instead of integers.

## Create custom index formatter
fig, ax2 = plt.subplots(figsize=(6, 3))
ax2.plot(r.adj_close, 'o-')

## Format x-axis as times
def format_date(x, _):
    try:
        ## convert datetime64 to datetime, and use datetime's strftime:
        return r.date[round(x)].item().strftime('%a')
    except IndexError:
        pass

ax2.set_title("Creating Custom Index Formatter")
ax2.xaxis.set_major_formatter(format_date)  ## internally creates FuncFormatter

Using a Callable for Formatter

Instead of passing a function into .Axis.set_major_formatter, we can use any other callable, such as an instance of a class that implements __call__. In this step, we will create a class MyFormatter that formats the tick marks as times.

## Use a callable for formatter
class MyFormatter(Formatter):
    def __init__(self, dates, fmt='%a'):
        self.dates = dates
        self.fmt = fmt

    def __call__(self, x, pos=0):
        """Return the label for time x at position pos."""
        try:
            return self.dates[round(x)].item().strftime(self.fmt)
        except IndexError:
            pass

ax2.xaxis.set_major_formatter(MyFormatter(r.date, '%a'))

Displaying the Plot

We will now display the plot using the show function in matplotlib.

plt.show()

Summary

In this lab, we learned how to use a custom index formatter to plot financial time series without gaps on weekends. We also learned how to use a callable for the formatter instead of a function. By using these techniques, we can create more visually appealing plots of daily data.

Other Python Tutorials you may like