Skip to content Skip to sidebar Skip to footer

Matplotlib: How Can I Add An Alternating Background Color When I Have Dates On The X-axis?

I've recently started using the dark chesterish theme from dunovank, and I love how good a simple pandas.DataFrame.plot() looks like out of the box: Snippet 1: # Theme from dunova

Solution 1:

Gridlines are by default shown at the positions of the major ticks. You can get those ticks via ax.get_xticks(). The problem will be that it is not guaranteed that the edges of the plot coincide with those ticks, in fact they are most often dissimilar. So in order to have a consistent shading over the range of the axes, the first shade should start at the edge of the plot and end at the first gridline, then the following shades can go in between gridlines, up to the last, which will again be between the last gridline and the edge of the axes.

Another problem is that the limits of the plot and hence the automatically generated gridlines may change over the lifetime of the plot, e.g. because you decide to have different limits or zoom or pan the plot. So ideally one would recreate the shading each time the axis limits change. This is what the following does:

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# time series
ts = pd.Series(np.random.randn(1000),index=pd.date_range('1/1/2000', periods=1000)).cumsum()
# numeric series#ts = pd.Series(np.random.randn(1000),index=np.linspace(25,800,1000)).cumsum()
ax = ts.plot(x_compat=True)

ax.grid()

classGridShader():
    def__init__(self, ax, first=True, **kwargs):
        self.spans = []
        self.sf = first
        self.ax = ax
        self.kw = kwargs
        self.ax.autoscale(False, axis="x")
        self.cid = self.ax.callbacks.connect('xlim_changed', self.shade)
        self.shade()
    defclear(self):
        for span in self.spans:
            try:
                span.remove()
            except:
                passdefshade(self, evt=None):
        self.clear()
        xticks = self.ax.get_xticks()
        xlim = self.ax.get_xlim()
        xticks = xticks[(xticks > xlim[0]) & (xticks < xlim[-1])]
        locs = np.concatenate(([[xlim[0]], xticks, [xlim[-1]]]))

        start = locs[1-int(self.sf)::2]  
        end = locs[2-int(self.sf)::2]

        for s, e inzip(start, end):
            self.spans.append(self.ax.axvspan(s, e, zorder=0, **self.kw))

gs = GridShader(ax, facecolor="lightgrey", first=False, alpha=0.7)

plt.show()

enter image description here

Solution 2:

Use an axis vertical span with datetime values for the x-values:

from jupyterthemes import jtplot
import pandas as pd
import numpy as np
from datetime import datetime

jtplot.style()
ts = pd.Series(np.random.randn(1000),index=pd.date_range('1/1/2000', periods=1000)).cumsum()
ax = ts.plot()

# or an appropriate for-loop
ax.axvspan(datetime(1999, 12, 15), datetime(2000, 1, 15), facecolor='red', alpha=0.25)
ax.axvspan(datetime(2000, 12, 15), datetime(2001, 1, 15), facecolor='red', alpha=0.25)

timeseries graph with shaded vertical areas

Post a Comment for "Matplotlib: How Can I Add An Alternating Background Color When I Have Dates On The X-axis?"