|
| 1 | +""" |
| 2 | +========================= |
| 3 | +Multilevel (nested) ticks |
| 4 | +========================= |
| 5 | +
|
| 6 | +Sometimes we want another level of tick labels on an axis, perhaps to indicate |
| 7 | +a grouping of the ticks. |
| 8 | +
|
| 9 | +Matplotlib does not provide an automated way to do this, but it is relatively |
| 10 | +straightforward to annotate below the main axis. |
| 11 | +
|
| 12 | +These examples use `.Axes.secondary_xaxis`, which is one approach. |
| 13 | +It has the advantage that we can use Matplotlib Locators and Formatters on |
| 14 | +the axis that does the grouping if we want. |
| 15 | +
|
| 16 | +This first example creates a secondary xaxis and |
| 17 | +manually adds the ticks and labels using `.Axes.set_xticks`. |
| 18 | +
|
| 19 | +""" |
| 20 | + |
| 21 | +import matplotlib.pyplot as plt |
| 22 | +import numpy as np |
| 23 | + |
| 24 | +import matplotlib.dates as mdates |
| 25 | + |
| 26 | +rng = np.random.default_rng(19680801) |
| 27 | + |
| 28 | +fig, ax = plt.subplots(layout='constrained', figsize=(4, 4)) |
| 29 | + |
| 30 | +ax.plot(np.arange(30)) |
| 31 | + |
| 32 | +sec = ax.secondary_xaxis(location=0) |
| 33 | +sec.set_xticks([5, 15, 25], labels=['\nOughts', '\nTeens', '\nTwenties']) |
| 34 | + |
| 35 | +# %% |
| 36 | +# This example adds a second level of annotation to a categorical axis. Note |
| 37 | +# that here we need to note that each animal (category) is assigned an integer, |
| 38 | +# so ``cats`` is at x=0, ``dogs`` at x=1 etc. Then we place the ticks on the |
| 39 | +# second level on an x that is at the middle of the animal class we are trying |
| 40 | +# to delineate. |
| 41 | +# |
| 42 | +# This example also adds tick marks between the classes by adding a second |
| 43 | +# secondary xaxis, and placing long, wide ticks at the boundaries between the |
| 44 | +# animal classes. |
| 45 | + |
| 46 | +fig, ax = plt.subplots(layout='constrained', figsize=(7, 4)) |
| 47 | + |
| 48 | +ax.plot(['cats', 'dogs', 'pigs', 'snakes', 'lizards', 'chickens', |
| 49 | + 'eagles', 'herons', 'buzzards'], |
| 50 | + rng.normal(size=9)) |
| 51 | + |
| 52 | +sec = ax.secondary_xaxis(location=0) |
| 53 | +sec.set_xticks([1, 3.5, 6.5], labels=['\n\nMammals', '\n\nReptiles', '\n\nBirds']) |
| 54 | +sec.tick_params('x', length=0) |
| 55 | + |
| 56 | +# lines between the |
| 57 | +sec2 = ax.secondary_xaxis(location=0) |
| 58 | +sec2.set_xticks([-0.5, 2.5, 4.5, 8.5], labels=[]) |
| 59 | +sec2.tick_params('x', length=40, width=1.5) |
| 60 | +ax.set_xlim(-0.6, 8.6) |
| 61 | + |
| 62 | +# %% |
| 63 | +# Dates are another common place where we may want to have a second level of tick |
| 64 | +# labels. Here we take advantage of the ability to add an automatic locator and |
| 65 | +# formatter to the secondary xaxis, which means we do not need to set the |
| 66 | +# ticks manually. |
| 67 | +# |
| 68 | +# This example also differs from the above, in that we placed it at a |
| 69 | +# location below the main axes ``location=-0.075`` and then we hide the spine |
| 70 | +# by setting the line width to zero. That means that our formatter no longer |
| 71 | +# needs the carriage returns of the previous two examples. |
| 72 | + |
| 73 | +fig, ax = plt.subplots(layout='constrained', figsize=(7, 4)) |
| 74 | + |
| 75 | +time = np.arange(np.datetime64('2020-01-01'), np.datetime64('2020-03-31'), |
| 76 | + np.timedelta64(1, 'D')) |
| 77 | + |
| 78 | +ax.plot(time, rng.random(size=len(time))) |
| 79 | + |
| 80 | +# just format the days: |
| 81 | +ax.xaxis.set_major_formatter(mdates.DateFormatter('%d')) |
| 82 | + |
| 83 | +# label the months: |
| 84 | +sec = ax.secondary_xaxis(location=-0.075) |
| 85 | +sec.xaxis.set_major_locator(mdates.MonthLocator(bymonthday=1)) |
| 86 | +sec.xaxis.set_major_formatter(mdates.DateFormatter(' %b')) |
| 87 | +sec.tick_params('x', length=0) |
| 88 | +sec.spines['bottom'].set_linewidth(0) |
| 89 | + |
| 90 | +# label the xaxis, but note for this to look good, it needs to be on the |
| 91 | +# secondary xaxis. |
| 92 | +sec.set_xlabel('Dates (2020)') |
| 93 | + |
| 94 | +plt.show() |
0 commit comments