pythonmatplotlibplot-annotations

Use data coords for x, axis coords for y for text annotations


From the documentation:

The default transform specifies that text is in data coords, alternatively, you can specify text in axis coords (0,0 is lower-left and 1,1 is upper-right). The example below places text in the center of the axes:

>>> text(0.5, 0.5, 'matplotlib', horizontalalignment='center',
...      verticalalignment='center', transform=ax.transAxes)

Can I instead use both data and axis coords? For x and y respectively.

Example code:

import random
import matplotlib.pyplot as plt

values = [random.randint(2,30) for _ in range(15)]
plt.violinplot(values, positions=[1])

# This might place the text outside the figure
plt.gca().text(1, 30, "Text")
# I would like to use axis coords for y instead of data coords. Example call
# would be something like this:
# text_mixed_coords(xdata=1, yaxis=0.9, "Text")

plt.savefig("plot.png")

Potential results:
plot.png plot.png

See also: Putting text in top left corner of matplotlib plot


Solution

  • This is known as a "blended transformation"

    you can create a blended transformation that uses the data coordinates for the x axis and axes coordinates for the y axis, like so:

    import matplotlib.transforms as transforms
    trans = transforms.blended_transform_factory(ax.transData, ax.transAxes)
    

    In your minimal example:

    import random
    import matplotlib.pyplot as plt
    import matplotlib.transforms as transforms
    
    fig, ax = plt.subplots()
    
    values = [random.randint(2,30) for _ in range(15)]
    ax.violinplot(values, positions=[1])
    
    # the x coords of this transformation are data, and the
    # y coord are axes
    trans = transforms.blended_transform_factory(
        ax.transData, ax.transAxes)
    
    ax.text(1, 0.9, "Text", transform=trans)
    
    plt.savefig("plot.png")
    

    enter image description here

    Also worth noting this from the matplotlib tutorial which makes it a little easier in this particular case:

    Note:

    The blended transformations where x is in data coords and y in axes coordinates is so useful that we have helper methods to return the versions mpl uses internally for drawing ticks, ticklabels, etc. The methods are matplotlib.axes.Axes.get_xaxis_transform() and matplotlib.axes.Axes.get_yaxis_transform(). So in the example above, the call to blended_transform_factory() can be replaced by get_xaxis_transform:

    trans = ax.get_xaxis_transform()