3

I try to set the line style of matplotlib plot spines, but for some reason, it does not work. I can set them invisible or make them thinner, but I cannot change the line style.

no dotted spines

My aim is to present one plot cut up into two to show outliers at the top. I would like to set the respective bottom/top spines to dotted so they clearly show that there is a break.

import numpy as np
import matplotlib.pyplot as plt

# Break ratio of the bottom/top plots respectively
ybreaks = [.25, .9]

figure, (ax1, ax2) = plt.subplots(
    nrows=2, ncols=1,
    sharex=True, figsize=(22, 10),
    gridspec_kw = {'height_ratios':[1 - ybreaks[1], ybreaks[0]]}
)

d = np.random.random(100)

ax1.plot(d)
ax2.plot(d)

# Set the y axis limits
ori_ylim = ax1.get_ylim()
ax1.set_ylim(ori_ylim[1] * ybreaks[1], ori_ylim[1])
ax2.set_ylim(ori_ylim[0], ori_ylim[1] * ybreaks[0]) 

# Spine formatting
# ax1.spines['bottom'].set_visible(False)  # This works
ax1.spines['bottom'].set_linewidth(.25)  # This works
ax1.spines['bottom'].set_linestyle('dashed')  # This does not work

ax2.spines['top'].set_linestyle('-')  # Does not work
ax2.spines['top'].set_linewidth(.25)  # Works

plt.subplots_adjust(hspace=0.05)

I would expect the above code to draw the top plot's bottom spine and the bottom plot's top spine dashed.

What do I miss?

nocibambi
  • 2,065
  • 1
  • 16
  • 22

1 Answers1

8

First one should mention that if you do not change the linewidth, the dashed style shows fine.

ax1.spines['bottom'].set_linestyle("dashed")

enter image description here

However the spacing may be a bit too tight. This is due to the capstyle being set to "projecting" for spines by default.

One can hence set the capstyle to "butt" instead (which is also the default for normal lines in plots),

ax1.spines['bottom'].set_linestyle('dashed')
ax1.spines['bottom'].set_capstyle("butt")

enter image description here

Or, one can separate the dashes further. E.g.

ax1.spines['bottom'].set_linestyle((0,(4,4)))

enter image description here

Now, if you also set the linewidth so something smaller, then you would need proportionally more spacing. E.g.

ax1.spines['bottom'].set_linewidth(.2)  
ax1.spines['bottom'].set_linestyle((0,(16,16))) 

enter image description here

Note that the line does not actually become thinner on screen due to the antialiasing in use. It just washes out, such that it becomes lighter in color. So in total it may make sense to keep the lineswidth at some 0.72 points (0.72 points = 1 pixel at 100 dpi) and change the color to light gray instead.

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • How come that the dashed linestyle is so tight when applied to the spine, as you say? If you plot a line in the same plot, its linestyle is a lot looser. Is there even a way to get the dash spacing from a line, e.g. in order to apply it to the spine? (Simply trying `line.get_linestyle()` doesn't work, as it just returns `'--'`.) – Erlend Magnus Viggen May 22 '19 at 12:51
  • 1
    @ErlendM Very good question. I updated the answer accordingly. So say you have a `line` in the plot, and a `spine` to make equally styled, you would go for something like `spine.set_linestyle(line.get_linestyle()); spine.set_linewidth(line.get_linewidth()); spine.set_capstyle(line.get_dash_capstyle())` – ImportanceOfBeingErnest May 22 '19 at 13:46