Search code examples
pythonmatplotlibplotgraphics

`matplotlib` figure text automatically adjusting position to the top left


When I run the following code, I have no issue.

import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.scatter([1, 2, 3], [4, 5, 6])
fig.text(
    0, 
    1,
    s = "ABCD",
    ha = "left",
    va = "bottom",
    transform = fig.transFigure
)
fig.patch.set_linewidth(5)
fig.patch.set_edgecolor("green")
plt.show()
plt.close()

My goal is to have that ABCD in the top left, higher that everything else in the figure and to the left of everything else in the figure, and that is exactly what happens.

correct plot

When I run this, there is a problem.

import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.scatter([1, 2, 3], [4, 5, 6])
ax.set_ylabel("1\n2\n3\n4\n5\n6\n7")
ax.set_title("a\nb\nc\nd\ne\nf")
fig.text(
    0, 
    1,
    s = "ABCD",
    ha = "left",
    va = "bottom",
    transform = fig.transFigure
)
fig.patch.set_linewidth(5)
fig.patch.set_edgecolor("green")
plt.show()
plt.close()

incorrect plot

The goal is to have the ABCD higher than everything else in the figure, but the title displays higher than the ABCD. Likewise, the ABCD is supposed to be the the left of everything else in the figure, yet the y-axis label is to the left of the ABCD.

Curiously, the border has no issues adjusting to how wide or how tall the figure gets, just this text label.

How can I get the ABCD text label to display in the upper left corner, no matter what I do with the axis labels or main titles? I need the bottom of the text to be above everything else in the figure and the right of the text to be to the left of everything else in the figure like I have in the first image.


Solution

  • I assume you are using a notebook or something else that draws the figure with the bbox_inches="tight" option. That resizes the figure to fit what is on it. In that case, the text gets placed before the resizing is done, so it's slightly above the top left for the original size of the figure, but then doesn't move when the figure is expanded to fit around everything.

    If you use annotate instead of text, you get a lot more control and you can place the text relative to any existing artist on the figure using the xycoords keyword. Here I place it relative to your y-label for its x coordinate and relative to your title for its y coordinate:

    import matplotlib.pyplot as plt
    
    fig, ax = plt.subplots()
    ax.scatter([1, 2, 3], [4, 5, 6])
    yl = ax.set_ylabel("1\n2\n3\n4\n5\n6\n7")
    t = ax.set_title("a\nb\nc\nd\ne\nf")
    ax.annotate(
        "ABCD", 
        (0, 1),
        ha="left",
        va="bottom",
        xycoords=(yl, t)
    )
    fig.patch.set_linewidth(5)
    fig.patch.set_edgecolor("green")
    plt.savefig("test.png", bbox_inches="tight")
    

    enter image description here