5

The question:

I'm trying to grasp the concept of monkey patching and at the same time make a function to produce the perfect time-series plot. How can I include the following matplotlib functionality in pandas pandas.DataFrame.plot()?

ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)

complete code at the end of the question


The details:


I think the default settings in df.plot() is pretty neat, especially if you're running a Jupyter Notebook with a dark theme like chesterish from dunovank:

df.plot default

And I'd like to use it for as much of my data analysis workflow as possible, but I'd really like to remove the frame (or what's called spines) like this:

df.plot no frames

Arguably, this is a perfect time-series plot. But df.plot() doesn't have a built-in argument for this. The closest thing seems to be grid = False, but that takes away the whole grid in the same run:

enter image description here


What I've tried


I know I can wrap the spine snippet in a function along with df.plot() so I end up with this:

Snippet 1:

def plotPerfect(df, spline):

    ax = df.plot()

    if not spline:
        ax.spines['top'].set_visible(False)
        ax.spines['right'].set_visible(False)
        ax.spines['bottom'].set_visible(False)
        ax.spines['left'].set_visible(False)

    return(ax)

plotPerfect(df = df, spline = False)

Output 1:

enter image description here

But is that the "best" way to do it with regards to flexibilty and readability for future amendments? Or even the fastest with regards to execution time if we're talking hundreds of plots?

I know how I can get the df.plot() source, but everything from there leaves me baffled. So how do I include those settings in df.plot? And perhaps the wrapped function approach is just as good as monkey patching?


Snippet with full code and sample data:


To reproduce the example 100%, paste this into a Jupyter Notebook cell with the chesterish theme activated:

# imports
import pandas as pd
import numpy as np
from jupyterthemes import jtplot

# Sample data
np.random.seed(123)
rows = 50
dfx = pd.DataFrame(np.random.randint(90,110,size=(rows, 1)), columns=['Variable Y'])
dfy = pd.DataFrame(np.random.randint(25,68,size=(rows, 1)), columns=[' Variable X'])
df = pd.concat([dfx,dfy], axis = 1)
jtplot.style()

# Plot with default settings
df.plot()

# Wrap df.plot() and matplotlib spine in a function
def plotPerfect(df, spline):

    ax = df.plot()

    if not spline:
        ax.spines['top'].set_visible(False)
        ax.spines['right'].set_visible(False)
        ax.spines['bottom'].set_visible(False)
        ax.spines['left'].set_visible(False)

    return(ax)

# Plot the perfect time-series plot
plotPerfect(df = df, spline = False)
2
  • 2
    Are you interested in monkey patching or customizong the plots? Seem quite unrelated issues to me. Commented Feb 1, 2019 at 8:37
  • @Goyo My main objective is to efficiently modify a bunch of plots. And I was under the (vague) impression that this could be a good use case for monkey patching. Commented Feb 1, 2019 at 14:07

3 Answers 3

2

This seems like an xyproblem.

Monkey patching (The Y)

The question asks for monkey patching pandas plot function to add additional features. This can in this case be done by replacing the pandas.plotting._core.plot_frame function with a custom version of it.

import pandas as pd
import pandas.plotting._core
orginal = pandas.plotting._core.plot_frame

def myplot(*args, **kwargs):
    spline = kwargs.pop("spline", True)
    ax = orginal(*args, **kwargs)
    ax.set_frame_on(spline)
    ax.grid(not spline)
    ax.tick_params(left=spline, bottom=spline)
    return ax

pandas.plotting._core.plot_frame = myplot

Then use it as

df = pd.DataFrame([[0.1, 0.1], [0.9, 0.9]]).set_index(0)
df.plot()             ## Normal Plot
df.plot(spline=False) ## "Despined" Plot 

enter image description here

Note that if in jupyter notebook, the cell with the monkey patching cannot be run more than once, else it would end up in recursion.

Styling (The X)

The above is pretty overkill for changing the style of a plot. One should rather use the style options of matplotlib.

mystyle = {"axes.spines.left" : False,
           "axes.spines.right" : False,
           "axes.spines.bottom" : False,
           "axes.spines.top" : False,
           "axes.grid" : True,
           "xtick.bottom" : False,
           "ytick.left" : False,}

Then to apply this for some plots in the notebook, use the plt.style.context manager,

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame([[0.1, 0.1], [0.9, 0.9]]).set_index(0)

df.plot()             ## Normal Plot

with plt.style.context(mystyle):
    df.plot()         ## "Despined" Plot 

enter image description here

Or, if you want to apply this style globally, update the rcParams.

plt.rcParams.update(mystyle)
Sign up to request clarification or add additional context in comments.

5 Comments

@ ImportanceOfBeingErnest Thank you! You also illustrate a few other aspects that I've been curious about so this is even more than I was hoping for! I'm also humoured by how "this seems like an xyproblem" and the wording in your link is an elegant and subtle way of calling someone an ignorant n00b. At first I thought you were talking abut Python(x,y) which I had only barely heard of but was looking forward to learn more about.
Personally I look at it another way: I'm trying to find a solution (spline, or X) that I think would be possible to solve in another (perhaps even more "pythonic") way (monkey patching, or Y). And I think this is a great way to learn new things. I sort of feel that I'm working mith multiple pieces of a puzzle at the same time. Anyway, thanks again!
Image
You have enough details about what you want to do in your question (the X), so no worries - as you can see one can perfectly answer this.
I just realized how I should have asked the question: Is it possible to take df.plot() and include spines=False as an argument?Just like box or kind is an argument of their own?And replace MY df.plot() with the original df.plot()? Perhaps using monkey patching?
It's been a year since you answered this question, but would you mind a follow-up question? I'm trying to apply what you've shown here on other scenarios. So what I would like to do is raise another question where I ask about the specifics in your suggested solution here. Would that be OK?
1

I would partly answer your customizing part of the question: Instead of hiding each spline by separate command, you can put them in a for loop as follows.

def plotPerfect(df, spline):
    ax = df.plot()

    if not spline:
        for i in ['top', 'right', 'bottom', 'left']:
            ax.spines[i].set_visible(False)

    return(ax)

# Plot the perfect time-series plot
plotPerfect(df = df, spline = False) 

If you want to hide all four spines and don't want to specify the top, right, etc. manually, you can do it in a more automated way as follows. The first one allows you to choose which ones to hide.

def plotPerfect(df, spline):
    ax = df.plot()

    if not spline:
        for i in ax.spines.values():
            i.set_visible(False)

    return(ax)

Comments

1

Alternative solutions to the other answer would be to use either plt.box(False) or ax.set_frame_on(False), both of these hides the axes rectangle patch.

def plotPerfect(df, spline):
    ax = df.plot()

    if not spline:
        ax.set_frame_on(False)
        # plt.box(False)  # another possible option

    return ax

Note that set_frame_on(False) removes the background making it transparent, which might not be desired

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.