1

I am having trouble getting the \phantom and \quad LaTeX commands to work in a legend label in matplotlib.

enter image description here

Ideally I would like the 'PREV. < 15%' to be aligned with the label below such that the 'PREV.' line up. I can do this in a LaTeX document using the \phantom or \quad commands. However the following code produces the image seen above and I cannot figure out why these commands do not have any effect.

import matplotlib
matplotlib.rcParams['hatch.color'] = '#787878'
import matplotlib.pyplot as plt
from matplotlib.patches import Patch

CB = {'OrRd': ['#fef0d9', '#fdcc8a', '#fc8d59', '#e34a33', '#b30000']}

# Plot legend
legend_labels = [r'$\phantom{LONG TEST PHRASE} \textsc{Prev.} < %i\%%$' % (15),
                 r'$%i\%% \leq \textsc{Prev.} < %i\%%$' % (15, 25),
                 r'$%i\%% \leq \textsc{Prev.} < %i\%%$' % (25, 35),
                 r'$%i\%% \leq \textsc{Prev.} < %i\%%$' % (35, 45),
                 r'$%i\%% \leq \textsc{Prev.}$' % (45)]

legend_elements = [Patch(facecolor='#86838C', label='$\\textsc{N} < 20$',)] \
                    + [Patch(facecolor='#ffffff', hatch='//', label='$20 \\leq \\textsc{N} \\leq 50$')] \
                    + [Patch(facecolor=color, label=label) for color, label in zip(CB['OrRd'], legend_labels)]
plt.rc('text', usetex=True)
plt.rc('font', family='serif')
leg = plt.legend(handles=legend_elements, loc=2, fontsize=21,
                 frameon=False, 
                 title=r'\textsc{LEGEND}', title_fontsize=24)

plt.show()
derNincompoop
  • 672
  • 11
  • 22
  • "However the following code produces the image seen above"..... No, it doesn't. Try copy pasting it in a new python window/terminal and see for yourself – Sheldore Jan 16 '19 at 20:06
  • Sorry, edited in code that should produce the image shown. – derNincompoop Jan 16 '19 at 20:47
  • Try going through [this](https://github.com/matplotlib/matplotlib/issues/4335/) and see if you can find something – Sheldore Jan 16 '19 at 20:52

2 Answers2

2

The following solution is not perfect but I still think it's worth sharing with you. I adapted it from this more or less similar problem although the link problem is slightly different. As you can see, the alignment is not perfect. You might need to tweak it a little bit. I had to remove the title_fontsize from plt.legend() as it seems it is not compatible with matplotlib 2.2.2.

matplotlib.rcParams['text.usetex'] = True
matplotlib.rcParams['text.latex.preview'] = True

# Plot legend
legend_labels = [r'$\quad \quad \quad  \textsc{Prev.} < %i\%%$' % (15),
                 r'$%i\%% \leq \textsc{Prev.} < %i\%%$' % (15, 25),
                 r'$%i\%% \leq \textsc{Prev.} < %i\%%$' % (25, 35),
                 r'$%i\%% \leq \textsc{Prev.} < %i\%%$' % (35, 45),
                 r'$%i\%% \leq \textsc{Prev.}$' % (45)]

enter image description here

Sheldore
  • 37,862
  • 7
  • 57
  • 71
  • Actually if I put a space before the `\phantom` command (i.e., `r$ \phantom{TEST TEXT HERE} \textsc{......` it seems to actually work! – derNincompoop Jan 16 '19 at 21:46
  • But now when I save the figure as a pdf it saves as the original legend without the spacing (saving as a png saves just fine). Rats! – derNincompoop Jan 16 '19 at 21:47
  • This seems to be a known issue. See [here](https://stackoverflow.com/questions/48465471/save-matplotlib-pyplot-while-using-tex-as-pdf) and [here](https://stackoverflow.com/questions/39151477/matplotlib-not-saving-pdf-w-latex) – Sheldore Jan 16 '19 at 21:54
0

The below solution sets position of the text block manually by specifying the pixel; thus, the alignment can be done perfectly. However, in order to find the required width, a few tries and checks are required.

The required position can be approximated as font size multiplied by number of characters and few more pixels to align. The question can be answered by adding following line to the code after setting up the legend.

font_size = 21
number_of_chars = 4
leg.texts[2].set_position((font_size*number_of_chars + 3, 0))

Picture of the solution

zukunft
  • 16
  • 5