28

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

Mdhale
  • 815
  • 2
  • 15
  • 22

7 Answers7

48

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)
Abu Shoeb
  • 4,747
  • 2
  • 40
  • 45
  • 1
    It works but is less object-oriented than [fig.text](https://matplotlib.org/3.1.0/api/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.text). +1 – mins Dec 05 '20 at 11:46
31

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.

tacaswell
  • 84,579
  • 22
  • 210
  • 199
  • 2
    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. – mins Dec 05 '20 at 11:44
  • 6
    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) https://github.com/matplotlib/matplotlib/pull/17524 – tacaswell Dec 05 '20 at 20:31
17

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.

Scott Mermelstein
  • 15,174
  • 4
  • 48
  • 76
  • 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`. – mr.adam Jun 11 '22 at 19:23
6

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

Tomas G.
  • 3,784
  • 25
  • 28
3

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

Nicky V
  • 31
  • 1
  • 1
    Yeah, after using `figtext`, I also came to know that `\n` in `xlabel` works. Thanks for posting it here. – Abu Shoeb Oct 30 '18 at 05:26
1

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

Ayan Mitra
  • 497
  • 1
  • 8
  • 20
0

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

akopty
  • 15
  • 1
  • 6