'How to detect if a twin axis has been generated for a matplotlib axis

How can one detect if an axis has a twin axis written on top of it? For example, if given ax below, how can I discover that ax2 exists?

import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3])
ax2 = ax.twinx()


Solution 1:[1]

I don't think there's any built-in to do this, but you could probably just check whether any other axes in the figure has the same bounding box as the axes in question. Here's a quick snippet that will do this:

def has_twin(ax):
    for other_ax in ax.figure.axes:
        if other_ax is ax:
            continue
        if other_ax.bbox.bounds == ax.bbox.bounds:
            return True
    return False

# Usage:

fig, ax = plt.subplots()
print(has_twin(ax))  # False

ax2 = ax.twinx()
print(has_twin(ax))  # True

Solution 2:[2]

You may check if the axes has a shared axes. This would not necessarily be a twin though. But together with querying the position this will suffice.

import matplotlib.pyplot as plt

fig, axes = plt.subplots(2,2)
ax5 = axes[0,1].twinx()

def has_twinx(ax):
    s = ax.get_shared_x_axes().get_siblings(ax)
    if len(s) > 1:
        for ax1 in [ax1 for ax1 in s if ax1 is not ax]:
            if ax1.bbox.bounds == ax.bbox.bounds:
                return True
    return False

print has_twinx(axes[0,1])
print has_twinx(axes[0,0])

Solution 3:[3]

I would like to propose a get_twin -> Axes function rather than has_twin -> bool, which has more applications. You can still check whether an ax has a twin by checking if get_twin(...) is None instead of if has_twin(...) so functionnality is not lost.

This is built on @ImportanceOfBeingErnest's answer. @jakevdp's is slightly less robust because it does not check explicitly for siblings.

def get_twin(ax, axis):
    assert axis in ("x", "y")        
    siblings = getattr(ax, f"get_shared_{axis}_axes")().get_siblings(ax)
    for sibling in siblings:
        if sibling.bbox.bounds == ax.bbox.bounds and sibling is not ax:
            return sibling 
    return None

fig, ax = plt.subplots()
print(get_twin(ax, "x") is None) # True
ax2 = ax.twinx()
print(get_twin(ax, "x") is ax2)  # True
print(get_twin(ax2, "x") is ax)  # True

Solution 4:[4]

One-liner for @Guimoute's answer as a lambda:

hasTwin = lambda ax, axis: \
          np.sum([np.logical_and(sib != ax,\
          sib.bbox.bounds==ax.bbox.bounds) for sib in \
          getattr(ax, f'get_shared_{}_axes'.format(axis))().get_siblings(ax)])

where the input axis should be a string 'x' or 'y'. This will return something nonzero (probably 1) if a twin exists:

hasXtwin = hasTwin(ax, 'x')
hasXtwin = hasTwin(ax, 'y')

if not hasXtwin:
    doSomething()
if not hasXtwin:
    doSomethingElse()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 jakevdp
Solution 2 ImportanceOfBeingErnest
Solution 3
Solution 4