33

I am pretty new to python and to the matplotlib library. I have created a scatter plot using matplotlib and now I wish to add caption a little below the X-axis. This is my code:

from matplotlib import pyplot as plt
import numpy as np
from pylab import *

file = open('distribution.txt', 'r')

txt="I need the caption to be present a little below X-axis"

x=[]
y=[]
for line in file:
    new=line.rstrip()
    mystring=new.split("\t")
    x.append(mystring[0])
    y.append(mystring[1])


fig = plt.figure()
ax1 = fig.add_axes((0.1,0.4,0.8,0.5))
ax1.set_title("This is my title")
ax1.set_xlabel('X-axis')
ax1.set_ylabel('Y-axis')
ax1.scatter(x,y, c='r')
fig.text(.05,.05,txt)
plt.xlim(0, 1.05)
plt.ylim(0, 2.5)
plt.show()

As you can see in the image my caption is way below the scatter plot, is there a way to bring it exactly below the X-axis? Also my scatter plot looks rectangular, is there a way to make it square like?

enter image description here

7 Answers 7

57

You can simply use figtext. You can also change the value of x and y-axes as you want.

txt="I need the caption to be present a little below X-axis"
plt.figtext(0.5, 0.01, txt, wrap=True, horizontalalignment='center', fontsize=12)
Sign up to request clarification or add additional context in comments.

1 Comment

It works but is less object-oriented than fig.text. +1
37

Something like:

from matplotlib import pyplot as plt
import numpy as np

txt="I need the caption to be present a little below X-axis"

# make some synthetic data
x = np.linspace(0, 1, 512)
y = np.random.rand(512)*2.3 + .1

fig = plt.figure()
ax1 = fig.add_axes((0.1, 0.2, 0.8, 0.7))

ax1.set_title("This is my title")
ax1.set_xlabel('X-axis')
ax1.set_ylabel('Y-axis')

# make the edge colors match the facecolors
ax1.scatter(x,y, c='r', edgecolors='face')
# center text
fig.text(.5, .05, txt, ha='center')

# use OO interface    
ax1.set_xlim([0, 1.05])
ax1.set_ylim([0, 2.5])

# resize the figure to match the aspect ratio of the Axes    
fig.set_size_inches(7, 8, forward=True)

plt.show()

example result

might work. Making this easier to do is on the radar for mpl upstream, but we are still looking for someone to do it.

2 Comments

5 years later, it seems there is no better solution. +1. Note the y pos value can be negative to increase the space between the x label and the figure legend.
We just merged (as in 2 days ago) a PR to put functionality similar to this in main-line Matplotlib. Should be available in mpl3.4 (Jan21) github.com/matplotlib/matplotlib/pull/17524
20

First, I feel weird posting an answer against the co-lead developer of matplotlib. Obviously, @tacaswell knows matplotlib far better than I ever will. But at the same time, his answer wasn't dynamic enough for me. I needed a caption that would always be based on the position of the xlabel, and couldn't just use text annotations.

I considered simply changing the xlabel to add a newline and the caption text, but that wouldn't clearly differentiate the caption, and you can't do things like change the text size or make it italic in the middle of a text string.

I solved this by using matplotlib's TeX capabilities. Here's my solution:

from matplotlib import pyplot as plt
from matplotlib import rc
import numpy as np
from pylab import *

rc('text', usetex=True)

file = open('distribution.txt', 'r')

txt="I need the caption to be present a little below X-axis"

x=[]
y=[]
for line in file:
    new=line.rstrip()
    mystring=new.split("\t")
    x.append(mystring[0])
    y.append(mystring[1])


fig = plt.figure()
ax1 = fig.add_axes((0.1,0.4,0.8,0.5))
ax1.set_title("This is my title")
ax1.set_xlabel(r'\begin{center}X-axis\\*\textit{\small{' + txt + r'}}\end{center}')
ax1.set_ylabel('Y-axis')
ax1.scatter(x,y, c='r')
plt.xlim(0, 1.05)
plt.ylim(0, 2.5)
plt.show()

I did the same thing with the random scatter plot from tacaswell's answer, and here's my result:

Scatter plot with caption

One warning: if you tweak this to take input string variables, the strings may not be properly escaped for use with TeX. Escaping LaTeX code is already covered on Stack Overflow, at https://stackoverflow.com/a/25875504/1404311 . I used that directly, and then could take arbitrary xlabels and captions.

2 Comments

This is really nice, but I had to install some extra packages on ubuntu to get it to work (I've never used LaTex before): sudo apt install texlive texlive-latex-extra cm-super dvipng.
To give more context to the word dynamic, the significance of this answer is that it attaches the text to the Axes, not the Figure, and therefore works as you move the plot into figures with different subplot arrangements. The most common use (for me) is labeling (a) (b), ... in a paper, with the overall figure caption in the latex document. For more complex graphics, i.e. using GridSpec to have subfigures, obviously fig.text works for assigning a group of plots to (a), (b), ..., but I use subplots far more than I use gridspecs.
8

as @Nicky V mentioned.

plt.xlabel('''Butterfly contract traded

Note: c1-c2-c3 indicates the position: long 2 times c2 and short c1 anc c3''')

Result:

enter image description here

Comments

5

Another simple solution that I used when I couldn't figure out how to do it in a way that was designed...if that makes sense, was to just add some new lines to the xlabel plt.xlabel("xaxis label\n\n\n\ncaption") This just puts the caption a couple new lines under the x axis which makes it look like a caption even thought it's really part of the xaxis label

1 Comment

Yeah, after using figtext, I also came to know that \n in xlabel works. Thanks for posting it here.
0

I found another trick, using plt.text() you can set the y position below the plot canvas, and it can work as a caption. You can adjust fontsize, colour and even where the line breaks using \n. I am using this method

Comments

-1

You can simply use negative position for the y to extend the figure outside the current size.

fig.text(0.5, -0.05, 'some caption', ha='center')

The coordinate is of the figure, where 0,0 means the lower left end, and 1,1 corresponds to the top right.

An example -

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 1, 100)
y = np.random.rand(100)
plt.scatter(x, y)
fig = plt.gcf()
fig.text(0.5, -0.05, 'A scattered plot with caption', ha='center')

A scattered plot with caption

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.