|
5 | 5 |
|
6 | 6 | Broken axis example, where the y-axis will have a portion cut out.
|
7 | 7 | """
|
8 |
| -import matplotlib.pyplot as plt |
9 |
| -import numpy as np |
10 | 8 |
|
| 9 | +import numpy as np |
| 10 | +import matplotlib.pyplot as plt |
11 | 11 |
|
12 |
| -# 30 points between [0, 0.2) originally made using np.random.rand(30)*.2 |
13 |
| -pts = np.array([ |
14 |
| - 0.015, 0.166, 0.133, 0.159, 0.041, 0.024, 0.195, 0.039, 0.161, 0.018, |
15 |
| - 0.143, 0.056, 0.125, 0.096, 0.094, 0.051, 0.043, 0.021, 0.138, 0.075, |
16 |
| - 0.109, 0.195, 0.050, 0.074, 0.079, 0.155, 0.020, 0.010, 0.061, 0.008]) |
| 12 | +np.random.seed(19680801) |
17 | 13 |
|
| 14 | +pts = np.random.rand(30)*.2 |
18 | 15 | # Now let's make two outlier points which are far away from everything.
|
19 | 16 | pts[[3, 14]] += .8
|
20 | 17 |
|
21 | 18 | # If we were to simply plot pts, we'd lose most of the interesting
|
22 | 19 | # details due to the outliers. So let's 'break' or 'cut-out' the y-axis
|
23 |
| -# into two portions - use the top (ax) for the outliers, and the bottom |
| 20 | +# into two portions - use the top (ax1) for the outliers, and the bottom |
24 | 21 | # (ax2) for the details of the majority of our data
|
25 |
| -f, (ax, ax2) = plt.subplots(2, 1, sharex=True) |
| 22 | +fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True) |
| 23 | +fig.subplots_adjust(hspace=0.05) # adjust space between axes |
26 | 24 |
|
27 | 25 | # plot the same data on both axes
|
28 |
| -ax.plot(pts) |
| 26 | +ax1.plot(pts) |
29 | 27 | ax2.plot(pts)
|
30 | 28 |
|
31 | 29 | # zoom-in / limit the view to different portions of the data
|
32 |
| -ax.set_ylim(.78, 1.) # outliers only |
| 30 | +ax1.set_ylim(.78, 1.) # outliers only |
33 | 31 | ax2.set_ylim(0, .22) # most of the data
|
34 | 32 |
|
35 | 33 | # hide the spines between ax and ax2
|
36 |
| -ax.spines['bottom'].set_visible(False) |
| 34 | +ax1.spines['bottom'].set_visible(False) |
37 | 35 | ax2.spines['top'].set_visible(False)
|
38 |
| -ax.xaxis.tick_top() |
39 |
| -ax.tick_params(labeltop=False) # don't put tick labels at the top |
| 36 | +ax1.xaxis.tick_top() |
| 37 | +ax1.tick_params(labeltop=False) # don't put tick labels at the top |
40 | 38 | ax2.xaxis.tick_bottom()
|
41 | 39 |
|
42 |
| -# This looks pretty good, and was fairly painless, but you can get that |
43 |
| -# cut-out diagonal lines look with just a bit more work. The important |
44 |
| -# thing to know here is that in axes coordinates, which are always |
45 |
| -# between 0-1, spine endpoints are at these locations (0, 0), (0, 1), |
46 |
| -# (1, 0), and (1, 1). Thus, we just need to put the diagonals in the |
47 |
| -# appropriate corners of each of our axes, and so long as we use the |
48 |
| -# right transform and disable clipping. |
49 |
| - |
50 |
| -d = .015 # how big to make the diagonal lines in axes coordinates |
51 |
| -# arguments to pass to plot, just so we don't keep repeating them |
52 |
| -kwargs = dict(transform=ax.transAxes, color='k', clip_on=False) |
53 |
| -ax.plot((-d, +d), (-d, +d), **kwargs) # top-left diagonal |
54 |
| -ax.plot((1 - d, 1 + d), (-d, +d), **kwargs) # top-right diagonal |
| 40 | +# Now, let's turn towards the cut-out slanted lines. |
| 41 | +# We create line objects in axes coordinates, in which (0,0), (0,1), |
| 42 | +# (1,0), and (1,1) are the four corners of the axes. |
| 43 | +# The slanted lines themselves are markers at those locations, such that the |
| 44 | +# lines keep their angle and position, independent of the axes size or scale |
| 45 | +# Finally, we need to disable clipping. |
55 | 46 |
|
56 |
| -kwargs.update(transform=ax2.transAxes) # switch to the bottom axes |
57 |
| -ax2.plot((-d, +d), (1 - d, 1 + d), **kwargs) # bottom-left diagonal |
58 |
| -ax2.plot((1 - d, 1 + d), (1 - d, 1 + d), **kwargs) # bottom-right diagonal |
| 47 | +d = .5 # proportion of vertical to horizontal extent of the slanted line |
| 48 | +kwargs = dict(marker=[(-1, -d), (1, d)], markersize=12, |
| 49 | + linestyle="none", color='k', mec='k', mew=1, clip_on=False) |
| 50 | +ax1.plot([0, 1], [0, 0], transform=ax1.transAxes, **kwargs) |
| 51 | +ax2.plot([0, 1], [1, 1], transform=ax2.transAxes, **kwargs) |
59 | 52 |
|
60 |
| -# What's cool about this is that now if we vary the distance between |
61 |
| -# ax and ax2 via f.subplots_adjust(hspace=...) or plt.subplot_tool(), |
62 |
| -# the diagonal lines will move accordingly, and stay right at the tips |
63 |
| -# of the spines they are 'breaking' |
64 | 53 |
|
65 | 54 | plt.show()
|
0 commit comments