3
votes

I'm trying to plot a selection of box-and-whisker subplots with a visible y axis only on the left hand plots. I'm looping through pandas dataframes to do this. However, the despine function I'm using to remove axes appears to apply to all plots whenever it is used. In this case, the final subplot should have no y axis, but the axis is also removed from the left hand side plots. Is there any way to get around this?

Is it possible to insulate each subplot from the despine function applied on the others? This only seems to happen for the despine function. Current code is below but I have also tried creating specific axes both before the loop (using [fig, axes = plt.subplots(ncols=3, nrows=4)]) and within the loop (ax = plt.subplot(4,3,q+1), then trying to call ax=ax in seaborn plotting functions).

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

sns.set(rc={"figure.figsize":(10,20)}, font_scale=1) # set size of plots and font
sns.set_style("ticks", {"font.sans-serif": "Arial"}) # set style for plots
sns.set_palette("cubehelix", n_colors=len(participants)) # set colour palette for individual datapoints

plt.figure
for q in range(11):
    plt.subplot(4,3,q+1)
    qplot = sns.swarmplot(data=eval("s" + str(q+1)), x="variable", y="value", hue="Participant", size=3) # plot individual participant data as points
    qplot.legend().set_visible(False) # hide the legend for individual participant data
    qplot = sns.boxplot(data=eval("s" + str(q+1)), x="variable", y="value", palette="Greys", linewidth=2, fliersize=0, width=0.85) # plot the boxplot
    qplot.set(ylim=(-3.5,3.5), xlabel="Condition", ylabel="Response") # set y axis values and label axes
    plt.title("S" + str(q+1)) # add a title
    if (q == 0) or (q == 3) or (q == 6):
        qplot.set(xticklabels=[], xlabel=None, xticks = []) # remove ticks and labels
        sns.despine(bottom = True, top=True, right=True, left=False, trim=True) # remove spines 
    if (q == 1) or (q == 2) or (q == 4) or (q == 5) or (q == 7):
        qplot.set(xticklabels=[], xlabel=None, xticks = [], yticklabels=[], ylabel = None, yticks = []) # remove ticks and labels
        sns.despine(bottom = True, top=True, right=True, left=True, trim=True) # remove spines
    if (q == 9):
        sns.despine(top=True, right = True,trim=True) # remove spines
    if (q == 8) or (q == 10):
        qplot.set(yticks = [], yticklabels=[], ylabel = None) # remove ticks and labels
        sns.despine(bottom=True, top=True, left=True, right=True, trim=True) # remove spines
    for axis in ["top","bottom","left","right"]: 
        qplot.spines[axis].set_linewidth(2) # set linewidth of axes
        qplot.tick_params(axis = "x", width=0) # set linewidth of x ticks to zero
        qplot.tick_params(axis = "y", width=2) # set linewidth of y ticks

Figure output

1
despine has an ax argument. Did you try to use it? Also, despine is just a compact way of calling matplotlib commands like ax.spines["left"].set_visible(False) or ax.tick_params(bottom=False, labelbottom=False) and similar.ImportanceOfBeingErnest
I was not aware of this, but your advice led me to this page which solved my issue for me. Thank you!Arran

1 Answers

3
votes

For future answer-seekers who would stumble upon this question, you could do something like the following to show/hide the spines in a grid of axes using sns.despine():

fig, axs = plt.subplots(4,3)
for ax in axs.flat:
    if ax.is_first_col():
        if ax.is_last_row():
            sns.despine(bottom=False, left=False, ax=ax)
        else:
            sns.despine(bottom=True, left=False, ax=ax)
    elif ax.is_last_row():
        sns.despine(bottom=False, left=True, ax=ax)
    else:
        sns.despine(bottom=True, left=True, ax=ax)

enter image description here

@PaulH was kind enough to refactor the code above. His version is much more compact and easier to read:

fig, axs = plt.subplots(4,3)
for ax in axs.flat:
    sns.despine(bottom=not ax.is_last_row(), left=not ax.is_first_col(), ax=ax)