Nras
Nras

Reputation: 4311

Align legend rows in matplotlib

I am doing a plot with matplotlib and creating a legend for this (see code below). I want the legends rows be aligned horizontally such that the relations > and < are aligned. Trying to adapt this and this code of similar problems, i got stuck.

I understand the basic idea: use \makebox[width][alignment]{math expression before aligment}<math expression after alignment as label, such that the space used by that epsilon-expression always uses the same space and is aligned to the right, hence there is free space to the left.

But the \hfill-methods used in the links only work if there is text before it the hfill, or if the alignment is standard (left). The solution must be quite near and any help would be appreciated. This is how the text of the legend should look like enter image description here

import numpy
from matplotlib import pyplot as plt

plt.rc('text', usetex=True)  # needed for interpeting tex strings, but changes appearence of axis-tick labels also
fig = plt.figure(1,figsize=(12.0, 8.0))
plt.ion()

# does not align the '<', '<' and '>' in the legend
# plt.plot(numpy.random.rand(10), label=r'\makebox[2cm][r]{$\varepsilon_i$}$< -\xi$')
# plt.plot(numpy.random.rand(10), label=r'\makebox[2cm][r]{$|\varepsilon_i|$}$< \xi$')
# plt.plot(numpy.random.rand(10), label=r'\makebox[2cm][r]{$\varepsilon_i$}$ > \xi$')

# \hfill doesnt change anything
# plt.plot(numpy.random.rand(10), label=r'\makebox[2cm][r]{\hfill$\varepsilon_i$}$< -\xi$')
# plt.plot(numpy.random.rand(10), label=r'\makebox[2cm][r]{\hfill$|\varepsilon_i|$}$< \xi$')
# plt.plot(numpy.random.rand(10), label=r'\makebox[24cm][r]{\hfill$\varepsilon_i$}$ > \xi$')

# the relations are aligned, but i do not want to plot the 'bla' for this
plt.plot(numpy.random.rand(10), label=r'\makebox[2cm][r]{bla\hfill$\varepsilon_i$}$< -\xi$')
plt.plot(numpy.random.rand(10), label=r'\makebox[2cm][r]{bla\hfill$|\varepsilon_i|$}$< \xi$')
plt.plot(numpy.random.rand(10), label=r'\makebox[2cm][r]{bla\hfill$\varepsilon_i$}$ > \xi$')
plt.legend(loc='upper right')
plt.show()

Upvotes: 2

Views: 1878

Answers (1)

Adobe
Adobe

Reputation: 13467

Here's a solution where LaTeX perfectly alignes math, but the user has to take the pain to position it inside the legend. The idea is to

  • draw legend box in a given position with a placeholder
  • put an amsmath's array into it manually

Here's the code:

#!/usr/bin/python3

from numpy import arange

import matplotlib
from matplotlib import pyplot as plt
custom_preamble = {
    "text.usetex": True,
    "text.latex.preamble": [
        r"\usepackage{amsmath}", # for the array macros
        ],
    }
matplotlib.rcParams.update(custom_preamble)

x = arange(5)
y = arange(5)

fig = plt.figure()
ax = fig.add_subplot(111)

l1, = ax.plot(x, y)
l2, = ax.plot(x * 2, y)
l3, = ax.plot(x * 3, y)

leg = ax.legend(
    [l1, l2, l3],
    ["", "", ""],
    bbox_to_anchor = (0.98, 0.25),
    handletextpad = 4,  # space between lines and text -- used here as a placeholder
    labelspacing = 0.1, # space between lines in a legend
    )
leg.set_zorder(1)

ax.text(0.955, 0.21,

    r"\begin{array}{rcl}"
    r"     \varepsilon_i & < & -\xi"
    r"\\ |\varepsilon_i| & < & \xi"
    r"\\   \varepsilon_i & > & \xi"
    r"\end{array}",

    transform = ax.transAxes,
    horizontalalignment = 'right',
    verticalalignment = 'top',
    zorder = 5,
        )

fig.savefig("mwe.png")

Result:

enter image description here

You might want to compile it twice: on the first compilation it might give You error, but all other tries would go fine.

As to a space between < sign in a legend -- it might be reduced with say:

ax.text(0.94, 0.21,

    r"\begin{array}{r@{}c@{}l}"
    r"     \varepsilon_i \,\,& < &\,\, -\xi"
    r"\\ |\varepsilon_i| \,\,& < &\,\, \xi"
    r"\\   \varepsilon_i \,\,& > &\,\, \xi"
    r"\end{array}",

(everything else the same). This gives:

enter image description here

Upvotes: 2

Related Questions