The matplotlib legend is column-based. You cannot have legend entries spanning multiple columns. That said, it is of course possible to "center" an odd number of entries in a legend with an odd number of rows and similarly "center" an even number of entries in a legend with an even number of rows. This would be done by using empty artists at the outer positions.
import matplotlib.pyplot as plt
import numpy as np
ncols = 3 # or 4
nlines = 7 # or 10
x = np.linspace(0,19)
f = lambda x,p: np.sin(x*p)
h = [plt.plot(x,f(x,i/10.), label="Line {}".format(i))[0] for i in range(nlines)]
h.insert(nlines//ncols, plt.plot([],[],color=(0,0,0,0), label=" ")[0])
plt.legend(handles=h, ncol=ncols, framealpha=1)
plt.show()
If the number of columns is odd and the number of legend entries is even or vice versa the above is not possible. An option may be to use two different legends and position them below one another such that it looks like the entries of the last line are centered.
import matplotlib.pyplot as plt
import numpy as np
ncols = 3
nlines = 8
x = np.linspace(0,19)
f = lambda x,p: np.sin(x*p)
h = [plt.plot(x,f(x,i/10.), label="Line {}".format(i))[0] for i in range(nlines)]
kw = dict(framealpha=1, borderaxespad=0, bbox_to_anchor=(0.5,0.2), edgecolor="w")
leg1 = plt.legend(handles=h[:nlines//ncols*ncols], ncol=ncols, loc="lower center", **kw)
plt.gca().add_artist(leg1)
leg2 = plt.legend(handles=h[nlines//ncols*ncols:], ncol=nlines-nlines//ncols*ncols,
loc="upper center", **kw)
plt.show()
The drawback here is that the legend has no border and both legends need to be positionned individually. To overcome this one would need to dig a little deeper and use some private attributed of the legend (caution, those may change from version to version without notice).
The idea can then be to remove the second legend once created and get its _legend_handle_box
to place into the first legend.
import matplotlib.pyplot as plt
import numpy as np
ncols = 3
nlines = 8
x = np.linspace(0,19)
f = lambda x,p: np.sin(x*p)
h = [plt.plot(x,f(x,i/10.), label="Line {}".format(i))[0] for i in range(nlines)]
kw = dict(framealpha=1, bbox_to_anchor=(0.5,0.2))
leg1 = plt.legend(handles=h[:nlines//ncols*ncols], ncol=ncols, loc="lower center", **kw)
plt.gca().add_artist(leg1)
leg2 = plt.legend(handles=h[nlines//ncols*ncols:], ncol=nlines-nlines//ncols*ncols)
leg2.remove()
leg1._legend_box._children.append(leg2._legend_handle_box)
leg1._legend_box.stale = True
plt.show()