Reputation: 2115
I have access to the figure instance fig = pylab.gcf()
. I know that in this figure there is a legend and I can access it via myLegend = fig.gca().legend_
. Now I want to change the properties of the legend. Some of them I have access via setters like myLegend.set_frame_on(True)
.
When the legend is created it accepts a number of keyword arguments:
class matplotlib.legend.Legend(parent, handles, labels, loc=None, numpoints=None, markerscale=None, scatterpoints=None, scatteryoffsets=None, prop=None, fontsize=None, borderpad=None, labelspacing=None, handlelength=None, handleheight=None, handletextpad=None, borderaxespad=None, columnspacing=None, ncol=1, mode=None, fancybox=None, shadow=None, title=None, framealpha=None, bbox_to_anchor=None, bbox_transform=None, frameon=None, handler_map=None)
How can I modify all the keyword arguments in the legend after the legend is created?
One of the problematic ones is numpoints
(number of markers in a legend, default is 2). Below is the example how I want to change it:
This shows how I want to program it
import pylab
pylab.plot(0,0,'ro', label = 'one point')
pylab.legend(loc = "lower left")
# no modifications above this line
setattr(pylab.gcf().gca().legend_, 'numpoints',1)
pylab.show()
This shows how I want it to look like
import pylab
pylab.plot(0,0,'ro', label = 'one point')
pylab.legend(numpoints = 1, loc = "lower left")
pylab.show()
I have cheked the source code, there is a numpoint variable that is changed, but the upper case is not updated to screen. What am I missing?
Upvotes: 13
Views: 10475
Reputation: 111
If this were me I would put it into another text file in order to do this, since it will be easier to change and keep track of, especially if you have a lot of code before and after this.
To open a file for writing we set the second parameter to "w" instead of "r".(of fobj = open("ad_lesbiam.txt", "r")
) To actually write the data into this file, we use the method write() of the file handle object.
Let's start with a very simple and straightforward example:
fh = open("example.txt", "w")
fh.write("To write or not to write\nthat is the question!\n")
fh.close()
Especially if you are writing to a file, you should never forget to close the file handle again. Otherwise you will risk to end up in a non consistent state of your data.
You will often find the with statement for reading and writing files. The advantage is that the file will be automatically closed after the indented block after the with has finished execution:
with open("example.txt", "w") as fh:
fh.write("To write or not to write\nthat is the question!\n")
Our first example can also be rewritten like this with the with statement:
with open("ad_lesbiam.txt") as fobj:
for line in fobj:
print(line.rstrip())
Example for simultaneously reading and writing:
fobj_in = open("ad_lesbiam.txt")
fobj_out = open("ad_lesbiam2.txt","w")
i = 1
for line in fobj_in:
print(line.rstrip())
fobj_out.write(str(i) + ": " + line)
i = i + 1
fobj_in.close()
fobj_out.close()
FYI. Every line of the input text file is prefixed by its line number
Upvotes: -1
Reputation: 127
I've written a function modify_legend
which modifies a legend after it has been created. It basically reads all the parameters from the already created legend, updates it with the key-value parameters you provided and calls legend(...)
with all possible parameters again.
Your problem would then be solved with:
import pylab
pylab.plot(0,0,'ro', label = 'one point')
pylab.legend(loc = "lower left")
modify_legend(numpoints = 1)
pylab.show()
Here's the code for modify_legend
:
def modify_legend(**kwargs):
import matplotlib as mpl
l = mpl.pyplot.gca().legend_
defaults = dict(
loc = l._loc,
numpoints = l.numpoints,
markerscale = l.markerscale,
scatterpoints = l.scatterpoints,
scatteryoffsets = l._scatteryoffsets,
prop = l.prop,
# fontsize = None,
borderpad = l.borderpad,
labelspacing = l.labelspacing,
handlelength = l.handlelength,
handleheight = l.handleheight,
handletextpad = l.handletextpad,
borderaxespad = l.borderaxespad,
columnspacing = l.columnspacing,
ncol = l._ncol,
mode = l._mode,
fancybox = type(l.legendPatch.get_boxstyle())==mpl.patches.BoxStyle.Round,
shadow = l.shadow,
title = l.get_title().get_text() if l._legend_title_box.get_visible() else None,
framealpha = l.get_frame().get_alpha(),
bbox_to_anchor = l.get_bbox_to_anchor()._bbox,
bbox_transform = l.get_bbox_to_anchor()._transform,
frameon = l._drawFrame,
handler_map = l._custom_handler_map,
)
if "fontsize" in kwargs and "prop" not in kwargs:
defaults["prop"].set_size(kwargs["fontsize"])
mpl.pyplot.legend(**dict(defaults.items() + kwargs.items()))
Notes on the code:
Legend
object, others (like title
, fancybox
) required some 'artistics'. You could check matplotlib.legend.Legend.__init__
to see how and why it's done.fontsize
parameter is used for overriding the font size when the legend was originally created with a prop
, as prop
usually overwrites fontsize
.bbox_to_anchor
and bbox_transform
-parameters), so feel free to try out and improve the code :)Upvotes: 7
Reputation: 2180
You can use the command pylab.legend
again with the correct keywords/arguments. This will modify the existent legend instead of creating a new one. Bellow you find your example, slightly modified.
import pylab
pylab.plot(0,0,'ro', label = 'one point')
pylab.legend(loc = "lower left")
# Change the number of markers shown in the legend
pylab.legend(numpoints = 1, loc = "lower left")
pylab.show()
Hope it helps.
Upvotes: 3
Reputation: 13668
What you see in the legend actually is a Line2D
. Altering numpoints
after that line has been created won't update said line, so you will have to get a handle on the Line2D
object and remove one of the points manually:
import pylab
pylab.plot(0,0,'ro', label = 'one point')
legend = pylab.legend(loc = "lower left")
markers = legend.get_children()[0].get_children()[1].get_children()[0].get_children()[0].get_children()[0].get_children()[1]
markers.set_data(map(pylab.mean, markers.get_data()))
pylab.show()
The get_children()
chain is required because matplotlib wraps the line in several layers of horizontal and vertical packs. The above snippet should suffice to give you the general idea, but in a real-world application, a preferable way to obtain the handle would be to follow the legend guide's hint on legend handlers and use a customized HandlerLine2D
that stores the line in some fashion.
Upvotes: 1