9

Suppose I'm looking at an n x n grid and on each axis I have labels of say, animals. But I'm also interested at looking at the relationship between groups, subgroups, etc. of the animals. So for example, I may have vertebrates and invertebrates, within vertebrates I may have mammals and reptiles and so forth. (If it matters, I'm particularly interested in a correlation matrix and am actually using a heatmap via seaborn...)

I'd like to plot this in matplotlib but have hierarchical labeling along the axes. So using my above example, I would have labels like dog, cat, horse, lizard, crocodile, etc. and then the first group of dog through horse would have a label of mammal and the second group of lizard, crocodile, etc. would have reptiles, and those two together would have a further label of vertebrates...

How would I do this?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
C S
  • 1,363
  • 1
  • 17
  • 26
  • Possible duplicate of [How to add group labels for bar charts in matplotlib?](https://stackoverflow.com/questions/19184484/how-to-add-group-labels-for-bar-charts-in-matplotlib) – pms Oct 03 '17 at 11:21

2 Answers2

15

Unfortunately I can't figure out how to disable minor ticks:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from mpl_toolkits.axes_grid.parasite_axes import SubplotHost

fig1 = plt.figure()
ax1 = SubplotHost(fig1, 111)
fig1.add_subplot(ax1)

# Some data
x = np.arange(1,6)
y = np.random.random(len(x))

# First X-axis
ax1.plot(x, y)
ax1.set_xticks(x)
ax1.set_xticklabels(['dog', 'cat', 'horse', 'lizard', 'crocodile'])
#ax1.xaxis.set_label_text('First X-axis') # Uncomment to label axis
ax1.yaxis.set_label_text("Sample data")

# Second X-axis
ax2 = ax1.twiny()
offset = 0, -25 # Position of the second axis
new_axisline = ax2.get_grid_helper().new_fixed_axis
ax2.axis["bottom"] = new_axisline(loc="bottom", axes=ax2, offset=offset)
ax2.axis["top"].set_visible(False)

ax2.set_xticks([0.0, 0.6, 1.0])
ax2.xaxis.set_major_formatter(ticker.NullFormatter())
ax2.xaxis.set_minor_locator(ticker.FixedLocator([0.3, 0.8]))
ax2.xaxis.set_minor_formatter(ticker.FixedFormatter(['mammal', 'reptiles']))

# Third X-axis
ax3 = ax1.twiny()
offset = 0, -50
new_axisline = ax3.get_grid_helper().new_fixed_axis
ax3.axis["bottom"] = new_axisline(loc="bottom", axes=ax3, offset=offset)
ax3.axis["top"].set_visible(False)

ax3.set_xticks([0.0, 1.0])
ax3.xaxis.set_major_formatter(ticker.NullFormatter())
ax3.xaxis.set_minor_locator(ticker.FixedLocator([0.5]))
ax3.xaxis.set_minor_formatter(ticker.FixedFormatter(['vertebrates']))

ax1.grid(1)
plt.show()

enter image description here


EDIT:

  1. Disabling minor ticks could be done by setting ticksize to 0 (thanks to @arnsholt): ax2.axis["bottom"].minor_ticks.set_ticksize(0).

  2. In latest matplotlib version (3.0.0 or higher) SubplotHost has to be imported as:

    from mpl_toolkits.axisartist.parasite_axes import SubplotHost

Vadim Shkaberda
  • 2,807
  • 19
  • 35
  • Thanks Vadim! Works great. – C S Jun 22 '16 at 23:16
  • This is awesome! But do you know how this can be done across subplots? I have time series which can be grouped into different types and I would like to have an additional y axis that labels just the groups among the line plots. – Stefan Falk Jun 11 '17 at 13:16
  • @displayname I think, the suitable way for your case is to make an independent axis for big subplot. Check the first part of https://stackoverflow.com/a/6981055/5510499. There is a big axis named `ax` with turned off axis lines and ticks. To move it left use `ax.spines["left"].set_position(("axes", -0.1))`. – Vadim Shkaberda Jun 11 '17 at 14:16
  • Hi! Looks like something one could do but I'm not sure if that would work for [my example](https://stackoverflow.com/questions/44484391/how-to-add-hierarchical-axis-across-subplots-in-order-to-label-groups) .. – Stefan Falk Jun 11 '17 at 15:53
  • 1
    @displayname Hi, looks like [ImportanceOfBeingErnest's](https://stackoverflow.com/a/44486955/5510499) solution is the best way to do it. – Vadim Shkaberda Jun 11 '17 at 18:55
  • @alexei Everything works fine in `matplotlib` version 2.0.2. – Vadim Shkaberda Aug 06 '17 at 08:14
  • Thanks to @arnsholt who mentioned a way to disable minor ticks by setting ticksize to 0: `ax2.axis["bottom"].minor_ticks.set_ticksize(0)` – Vadim Shkaberda Nov 18 '19 at 18:38
  • 1
    Running this with `matplotlib` version 3.0.2 renders error `AttributeError: 'AxesSubplot' object has no attribute 'get_grid_helper'`. Any workaround? – BeardedDork May 13 '20 at 01:26
  • 2
    @BeardedDork You're using wrong import. Try `from mpl_toolkits.axisartist.parasite_axes import SubplotHost`. – Vadim Shkaberda May 13 '20 at 15:51
  • Found a way to skip SubplotHost, which I needed, so I contributed the code in a separate answer. Thanks for yours! – creanion Sep 16 '21 at 08:48
8

Here is a slightly updated version of @Vadim's answer, because I found a way to do this without SubplotHost. Then you can do this even if you're not creating subplots (for example when working with seaborn figure-level functions).

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

fig1, ax1 = plt.subplots(1)
# Some data
x = np.arange(1,6)
y = np.random.random(len(x))

# First X-axis
ax1.plot(x, y)
ax1.set_xticks(x)
ax1.set_xticklabels(['dog', 'cat', 'horse', 'lizard', 'crocodile'])
ax1.yaxis.set_label_text("Sample data")

# Second X-axis
ax2 = ax1.twiny()

ax2.spines["bottom"].set_position(("axes", -0.10))
ax2.tick_params('both', length=0, width=0, which='minor')
ax2.tick_params('both', direction='in', which='major')
ax2.xaxis.set_ticks_position("bottom")
ax2.xaxis.set_label_position("bottom")

ax2.set_xticks([0.0, 0.6, 1.0])
ax2.xaxis.set_major_formatter(ticker.NullFormatter())
ax2.xaxis.set_minor_locator(ticker.FixedLocator([0.3, 0.8]))
ax2.xaxis.set_minor_formatter(ticker.FixedFormatter(['mammal', 'reptiles']))

# Third X-axis
ax3 = ax1.twiny()

ax3.spines["bottom"].set_position(("axes", -0.20))
ax3.tick_params('both', length=0, width=0, which='minor')
ax3.tick_params('both', direction='in', which='major')
ax3.xaxis.set_ticks_position("bottom")
ax3.xaxis.set_label_position("bottom")

ax3.set_xticks([0.0, 1.0])
ax3.xaxis.set_major_formatter(ticker.NullFormatter())
ax3.xaxis.set_minor_locator(ticker.FixedLocator([0.5]))
ax3.xaxis.set_minor_formatter(ticker.FixedFormatter(['vertebrates']))

ax1.grid(True)
plt.show()

line plot with hierarchical x axis

creanion
  • 2,319
  • 2
  • 13
  • 17