1

I am creating a figure in Matplotlib with the following code and I hope it is not too messy:

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import locale
locale.setlocale(locale.LC_ALL, 'German')

#the data to be plotted
CSB = [9205.0, 8845.0, 19740.0, 410.0, 11560.0, 11632.0, 14368.0,
       11556.0, 9846.0, 14544.0]
DOC = [np.nan, 1853.0, 4172.0, 259.0, np.nan, np.nan, np.nan, np.nan,
       np.nan, np.nan]
NH3N = [3593.5, 3318.8, 5208.0, 306.4, 2708.2, 2682.1, 2812.3, 3033.1,
        3098.4, 3815.9]
x = np.linspace(1, 10, 10)
Daten = ['09.05.2017', '16.05.2017', '23.05.2017', '06.06.2017', '28.08.2017',
    '31.08.2017', '04.09.2017', '07.09.2017', '14.09.2017', '18.09.2017']

#setting the font and font size globally
font = {'family' : 'Arial',
        'size'   : 12}
matplotlib.rc('font', **font)

fig, ax1 = plt.subplots()

#first plot
l1, = ax1.plot(x, NH3N, 'k+', label='NH$_3$-N-Konzentration', ms=8)
ax1.set_ylabel(r'NH$_3$-N-Konzentration $[\frac{mg}{L}]$')
ax1.set_xlabel('Datum Probenahme')
ax1.set_ylim([0,10000])
ax1.get_yaxis().set_major_formatter(plt.FuncFormatter(lambda x,
             loc: "{0:n}".format(float(x))))

#getting the x tick lables fitted to dates
plt.xticks(x, Daten, rotation=30)
fig.autofmt_xdate()
plt.subplots_adjust(bottom=0.3)

#second plot as a parasite of the first
ax2 = ax1.twinx()
l2, = ax2.plot(x, CSB, 'k.', label='CSB-Konzentration', ms=8)
ax2.set_ylabel(r'CSB-Konzentration $[\frac{mg}{L}]$')
ax2.set_ylim([0,25000])
ax2.get_yaxis().set_major_formatter(plt.FuncFormatter(lambda x,
             loc: "{0:n}".format(float(x))))

#third plot as a parasite of the first
ax3 = ax1.twinx()
l3, = ax3.plot(x, DOC, 'k^', label='DOC-Konzentration', ms=8)
ax3.set_ylabel(r'DOC-Konzentration $[\frac{mg}{L}]$')
ax3.set_ylim([0,5000])
ax3.get_yaxis().set_major_formatter(plt.FuncFormatter(lambda x,
             loc: "{0:n}".format(float(x))))

#manipulating the position of the third y axis
ax3.tick_params(direction='in', pad=-50)
ax3.spines['left'].set_position(('axes', 1.3))
ax3.yaxis.set_ticks_position('left')
ax3.yaxis.set_label_coords(1.48,0.5)

fig.legend((l1,l2,l3),('NH$_3$-N-Konzentration','CSB-Konzentration',
           'DOC-Konzentration'), loc=9, ncol = 3, bbox_to_anchor=(0.75, 0.15))

plt.savefig('CSB_NH3N_DOC_unbehandeltes_Abwasser.eps', format='eps',
            dpi=1000, bbox_inches='tight')

plt.show()

The code is resulting in this graph, which is supposed to be part of a Latex file. I am pretty new to Python, so I might have overlooked something or there might be other issues, besides the one I am asking for: I am sorry for that.
Now to my problem: the legend is supposed to be below the figure, which I can accomplish with the bbox_to_anchor attribute. This results in a cut off legend, a problem I tried to solve with

plt.margins(0.2)
plt.subplots_adjust(bottom=0.30)

and/or

plt.tight_layout()

and/or

bbox_inches='tight'

In the end, all of this is either scaling the hole figure in an awkward way or at least scaling the font size, an issue that gets obvious, when the figure is added to the Latex file. I think the whole twinx() thing is making everything quite unstable, but for me as a newbie it seemed to be an easier solution than this. Reading a lot of questions like this to find an answer it seems there is no easy way like "just add some space to the bottom manually without scaling", but I have to try and ask anyway.
Is there a way to put the legend to the very bottom of the figure and keeping the font size and the look of the figure as it is i.e. without any scaling commands?

Dave09
  • 15
  • 3
  • 1
    Welcome to SO. Matplotlib may take some time to become proficient in. Maybe look through [the Gallery](https://matplotlib.org/gallery/index.html#gallery) and see if there is a something similar to what you need. Failing that, you need to dig further into the docs and perhaps the [Tutorials](https://matplotlib.org/tutorials/index.html) to get an understanding of how it all works, then it is just a matter of playing around with parameters till you get what you want. – wwii Oct 12 '17 at 15:01
  • Please take the time to read [ask] and the links it contains. – wwii Oct 12 '17 at 15:02
  • Since the plot is already full without the legend, you obviously need to scale *something* in order to have the legend fit to the figure. Imagine your figure to be a sheet of paper; now you want to fit a legend into it. You can either take a bigger sheet of paper, or you draw everything on the paper smaller. Btw., fontsize will not change (this is just an effect of having a bigger figure included into the same space in latex). I would invite you to read [this answer](https://stackoverflow.com/a/43439132/4124317), especially the section about Postprocessing. – ImportanceOfBeingErnest Oct 12 '17 at 20:01
  • All solutions you have mentionned *will* help. But in order to help you, one would first need to know which route you want to go, changing figure size or plotting everything inside the figure smaller. – ImportanceOfBeingErnest Oct 12 '17 at 20:02

2 Answers2

0

Use

loc="lower center", bbox_to_anchor=(0.5, 0.)

where "lower" means that the anchor point's y coordinate (0.) refers to the lower border of the legend and "center" that the x coordinate (0.5) refers to the center of the legend. Both coordinates have a range of 0 to 1. This is illustrated in How to specify legend position in matplotlib in graph coordinates

Rainald62
  • 706
  • 12
  • 19
  • Although I thought this is already in my code and did not work properly in this context, simple copying and pasting it solved my problem. – Dave09 Oct 13 '17 at 11:04
0

I now followed the general example and with some help I was able to get to the result I aimed at. Here is the code with a proper positioning of the legend:

import matplotlib.pyplot as plt
import numpy as np

CSB = [9205.0, 8845.0, 19740.0, 410.0, 11560.0, 11632.0, 14368.0,
       11556.0, 9846.0, 14544.0]
DOC = [np.nan, 1853.0, 4172.0, 259.0, np.nan, np.nan, np.nan, np.nan,
       np.nan, np.nan]
NH3N = [3593.5, 3318.8, 5208.0, 306.4, 2708.2, 2682.1, 2812.3, 3033.1,
        3098.4, 3815.9]
x = np.linspace(1, 10, 10)
Daten = ['09.05.2017', '16.05.2017', '23.05.2017', '06.06.2017', '28.08.2017',
    '31.08.2017', '04.09.2017', '07.09.2017', '14.09.2017', '18.09.2017']

font = {'family' : 'Arial',
        'size'   : 12}
plt.rc('font', **font)

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

par1 = host.twinx()
par2 = host.twinx()

host.get_yaxis().set_major_formatter(plt.FuncFormatter(lambda x,
             loc: "{0:n}".format(float(x))))
par1.get_yaxis().set_major_formatter(plt.FuncFormatter(lambda x,
             loc: "{0:n}".format(float(x))))
par2.get_yaxis().set_major_formatter(plt.FuncFormatter(lambda x,
             loc: "{0:n}".format(float(x))))

offset = 85
par2.spines['right'].set_position(('outward', offset))  

host.set_xlim(0, 11)
host.set_ylim(0, 10000)

host.set_xlabel("Datum Probenahme")
host.set_ylabel(r'NH$_3$-N-Konzentration $[\frac{mg}{L}]$')
par1.set_ylabel(r'CSB-Konzentration $[\frac{mg}{L}]$')
par2.set_ylabel(r'DOC-Konzentration $[\frac{mg}{L}]$')

host.set_xticks(x)
host.set_xticklabels(Daten)


p1, = host.plot(x, NH3N, '+k', label='NH$_3$-N-Konzentration')
p2, = par1.plot(x, CSB, '.k', label=r'CSB-Konzentration')
p3, = par2.plot(x, DOC, '^k', label=r'DOC-Konzentration')

par1.set_ylim(0, 25000)
par2.set_ylim(0, 5000)

host.legend((p1,p2,p3),('NH$_3$-N-Konzentration','CSB-Konzentration','DOC-Konzentration'), loc='lower center', ncol = 3, bbox_to_anchor=(0.5, -0.5))

fig.autofmt_xdate()
plt.tight_layout()
plt.show()

Next time I will take an even closer look at the gallery. Thanks a lot for your time!

Dave09
  • 15
  • 3