I do a fair amount of plotting with pandas and matplotlib, and I've been looking for a way to help set the default scatter styles in my matplotlibrc file. I do not want to have to edit the matplotlib sourcecode to get default behaviors, because I need to be able to allow coworkers to reproduce work (and want to keep things consistent between machines/VEs, and update without worrying about busting things).
As an example, I want to be able to do:
plt.scatter(
df["dep_var"],
df["ind_var"],
alpha=0.5,
facecolors="none",
edgecolors="#444444",
linewidth=2,
)
With:
plt.scatter(
df["dep_var"],
df["ind_var"],
)
With overwritten alpha, face colors, edge colors, and linewidth in the matplotlibrc file (or some other non-source code location). But when I add
scatter.alpha : 0.5
to my matplotlibrc file, I get a Bad Key warning.
Is there a way to achieve this?
I think that a possible solution is use the RC parameter like:
plt.rcParams["scatter.edgecolors"] = "#444444"
plt.rcParams["lines.linewidth"]=2
You can check more about ket for Rc here
You only need to run this once! If you restart the kernel it will be reverted to the default.
Related
Is there a method in matplotlib to set the attributes of a plot BEFORE actually plotting it?
To the context: I have a config file in which I configure aspects of a plot. However, some keys may not be needed to be set (as e.g. the linewidth). I would like to have a function which says something like this :
if 'linethicknes' in config:
ax.set_linewidth = linethickness
I know that there is the possibility to give the linewidth property in the ax.plot(x, y ,linewidth = 1). But here the attribute is set in the plotting function, and I need to set it before the plotting.
Is it possibile to set the linewidth (and other attributes) before this plotting function?
You can set the runtime configuration rcparams at the start and this will set the configuration. As long as you don't overwrite it, this will be set as the configuration. A simple example is given below...
plt.rcParams['lines.linewidth'] = 6.0
x=[1,2,3,4,5]
y=[5,4,3,2,1]
plt.plot(x, y)
plt.show()
Even though I set the plotting style in every plotting script manually right at the beginning of the code, directly after importing the modules, the figure-plotting-style to seaborn-whitegrid, the resulting figures comprise the plain matplotlib.pyplot standard white background without grid display, e.g. like this graph:
Therefore, I assume that setting the style has no effect on my scripts, but I can't tell where it goes wrong, or where the style changes back to default. It'd be practical to print out the currently active plotting style at any given point, e.g. during a debugging session.
This is how I set my plotting style:
import .....
import matplotlib.pyplot as plt
import .....
# * Set the matplotlib.pyplot default template (plotting style and background) *
plt.style.use('seaborn-whitegrid')
# Start of script #
...
I even inserted the line plt.style.use('seaborn-whitegrid') in my plotting-submodules, which the main script calls, but it doesn't seem to have an effect on the output.
EDIT on additional trials:
According to what was suggested below by #Bernardo Trindade, I replaced plt.style.use with mpl.style.use:
# * Set the matplotlib.pyplot default template (plotting style and background) *
mpl_plt_default_template = 'seaborn-whitegrid'
import matplotlib as mpl
mpl.style.use(mpl_plt_default_template)
What I also tried is temporary styling, but with no avail either:
# Set plotting style
with plt.style.context('seaborn-whitegrid'):
# * Instantiate figure
fig, axs = plt.subplots(rows, cols, figsize=(width, height))
....
All of the above has not lead to any difference in the output graphs; still showing the plain standard matplotlib background without grid etc.
System specifics:
Lubuntu 20.04 LTS,
python 3.9x,
VS Code
I don't think there's a variable that stores the name of the stylesheet currently used, as all the style information is kept in a large dictionary whose values are simply updated when calling matplotlib.style.use().
Have you tried:
import matplotlib as mpl
mpl.style.use('seaborn-whitegrid')
Finally I found the culprit:
ax.grid(which='both', alpha=1)
Previously, this line never caused an issue, the grids were displayed just fine.
By contrast, now this setting the grid like this renders the grid invisible.
1) CONCLUSION (short answer)
The easiest way to make it work, i.e. safeguard that the grid appears, is setting either the parameter b to True like so:
ax.grid(b=True, which='both', alpha=1)
Alternatively, the kwarg visible can be set to True like so:
ax.grid(which='both', alpha=1, visible=True)
I recommend using visible=True since the naming of the kwarg is more intuitive as to what it actually does, compared to the optional parameter merely called b.
2) ELABORATE ANSWER
The following information is based on the official matplotlib docs:
The allegedly optional parameter b must be set manually to True or False, because otherwise, it'll be set to None in this context,
even though the current docs state:
b : bool or None, optional
Whether to show the grid lines. If any kwargs are supplied, it is assumed you want the grid on and b will be set to True.
If b is None and there are no kwargs, this toggles the visibility of the lines.
NOTE on boolean parameter "b":
I've tried both True and False to find out that even False delivers a proper grid, which is counterintuitive.
On the contrary, when put explicitely to None, which is equivalent to passing nothing, the grid won't appear:
ax.grid(b=None, which='both', alpha=1)
As mentioned before, the grid is invisible in this context also with the following, which was my original code line:
ax.grid(which='both', alpha=1)
Skimming through the **kwargs revealed an extra visibility kwarg called visible:
NOTE on naming of the visibility kwarg in its docs:
Artist.set_visible(self, b)[source]
It is called b, just like the
optional parameter
of ax.grid().
I confirmed that they actually do the same thing.
The grid appears, when putting the visible-kwarg to True:
ax.grid(b=None, which='both', alpha=1, visible=True)
When put to contradictory values, the following error is thrown:
ValueError: 'b' and 'visible' specify inconsistent grid visibilities
This error is caused when employing one of the following two possibilities:
ax.grid(b=False, which='both', alpha=1, visible=True)
ax.grid(b=True, which='both', alpha=1, visible=False)
Eventually, to make the grid appear successfully, set either the parameter b to True like so:
ax.grid(b=True, which='both', alpha=1)
Alternatively, the kwarg visible can be set to True like so:
ax.grid(which='both', alpha=1, visible=True)
I'm trying to write a simple immune system simulator. I'm modeling infected tissue as a simple grid of cells and various intracellular signals, and I'd like to animate movement of cells in one plot and the intensity of viral presence in another as the infection progresses. I'm doing so with the matshow function provided by matplotlib. However, when I plot the two next to each other, the full grid gets clipped unless I stretch out the window myself. I can't address the problem at all when saving to an mp4.
Here's the default view, which is identical to what I observe when saving to mp4:
And here's what it looks like after stretching out the viewer window
I'm running Python 2.7.9 with matplotlib 1.4.2 on OS X 10.10.2, using ffmpeg 2.5.2 (installed via Homebrew). Below is the code I'm using to generate the animation. I tried using plt.tight_layout() but it didn't affect the problem. If anyone has any advice as to how to solve this, I'd really appreciate it! I'd especially like to be able to save it without viewing with plt.show(). Thanks!
def animate(self, fname=None, frames=100):
fig, (agent_ax, signal_ax) = plt.subplots(1, 2, sharey=True)
agent_ax.set_ylim(0, self.grid.shape[0])
agent_ax.set_xlim(0, self.grid.shape[1])
signal_ax.set_ylim(0, self.grid.shape[0])
signal_ax.set_xlim(0, self.grid.shape[1])
agent_mat = agent_ax.matshow(self.display_grid(),
vmin=0, vmax=10)
signal_mat = signal_ax.matshow(self.signal_display(virus),
vmin=0, vmax=20)
fig.colorbar(signal_mat)
def anim_update(tick):
self.update()
self.diffuse()
agent_mat.set_data(self.display_grid())
signal_mat.set_data(self.signal_display(virus))
return agent_mat, signal_mat
anim = animation.FuncAnimation(fig, anim_update, frames=frames,
interval=3000, blit=False)
if fname:
anim.save(fname, fps=5, extra_args=['-vcodec', 'libx264'])
else:
plt.show()
According to the matplotlib documentation
Because of how matshow() tries to set the figure aspect ratio to be the one of the array, if you provide the number of an already existing figure, strange things may happen.
I think you're better off using imshow instead (which I believe is what matshow calls. That has an aspect keyword argument which you could use if it doesn't work automatically.
Also according to the matplotlib documentation,
Sets origin to ‘upper’, ‘interpolation’ to ‘nearest’ and ‘aspect’ to equal.
I think you want to do the first two, but leave aspect as auto.
Well, one simple solution would be to just specify the width of the figure when creating it:
fig, (agent_ax, signal_ax) = plt.subplots(1, 2, sharey=True,
figsize=(16,6))
I've kept a set of references to figures in a dictionary so that I could save them later if desired. I am troubled that the saved figures are blank if invoke a show() command and look at them first. Since the show() command blocks and I am not using a spyder-like interpreter, I have to close the figures before I get to savefig()
figures['myfig_1'] = figure()
...
figures['myfig_n'] = figure()
...
#show() #disabling this makes the problem go away
print "Saving:"
for fig in figures:
figure(figures[fig].number)
savefig(fig)
print "Figure " + str(figures[fig].number) + ": " + fig
The print statement here has given me the indication that the dictionary is still intact, which I think means that I have not lost the figure references (they are still returning meaningful numbers in their .number attribute.)
Another twist I have noticed is that when I have done a similar thing in a class, storing the dictionary as a member and dividing the store and save functions into their own methods, this does not happen. Is there something about the way I am closing the figures or storing the data which is making the figures loose their data?
Generally speaking, in cases like this don't use the interactive matlab-ish state machine interface to matplotlib. It's meant for interactive use.
You're trying to make a figure "active", and creating a new figure instead. It doesn't matter which figure is active, if you just retain the returned figure and/or axis objects and use them directly. (Also, don't use wildcard imports! You will regret it at some later point when you're maintaining your code!)
Just do something like this:
import matplotlib.pyplot as plt
figures = {}
figures['a'] = plt.figure()
ax = figures['a'].add_subplot(111)
ax.plot(range(10), 'ro-')
figures['b'] = plt.figure()
ax = figures['b'].add_subplot(111)
ax.plot(range(10), 'bo-')
plt.show()
for name, fig in figures.iteritems():
fig.savefig('figure-%s.png' % name)
From the documentation, whether or not the drawing elements are destroyed from show() depends on the backend, and the version of matplotlib. Not having the figures destroyed seems to be available with version 1.1.0. To figure out which backend is in use, use the get_backend() function. In my case, I was using the Qt4Agg backend. By invoking the TkAgg backend, with the call matplotlib.use('TkAgg') the figures were not destroyed before the save. Now to find out how to change the behavior of the Qt4Agg...
I would like to control the location of matplotlib clabels on a contour plot, but without utilizing the manual=True flag in clabel. For example, I would like to specify an x-coordinate, and have labels created at the points that pass through this line. I see that you can get the location of the individual labels using get_position(), but I am stuck at that. Any help would be greatly appreciated. Thanks!
Yes, there now is a way to control label locations!
https://github.com/matplotlib/matplotlib/pull/642
plt.figure()
CS = plt.contour(X, Y, Z)
manual_locations = [(-1, -1.4), (-0.62, -0.7), (-2, 0.5), (1.7, 1.2), (2.0, 1.4), (2.4, 1.7)]
plt.clabel(CS, inline=1, fontsize=10, manual=manual_locations)
No, there is no way built into matplotlib to do that. You are supposed to either live with the default locations or go fully interactive with manual and using the mouse.
You might want to file this as a bug report upstream so they can improve their algorithms.
There are multiple options to work around this. The first one is to programmatically place text on the contour figure. You will not be able to reliably remove the lines underneath the text this way. Assuming you have a contour c you can find the contour lines in c.collections. For every contour line invoke get_paths and place your text on that path.
The other option would be to replace the code for manual placement (in matplotlib.contour.BlockingContourLabeler) or tweak the code that finds the label positions (in matplotlib.contour.locate_label), but both functions are pretty dense. If you can come up with a working replacement for locate_label just overwrite the old method in your plotting macro
def your_locate_label(self, linecontour, labelwidth):
# some magic
pass
ar = np.array([[1,0], [0,1]]
c = matplotlib.contour(ar)
c.locate_label = your_locate_label
c.clabel()
Btw, if you use ipython you can easily view the function source from your interactive session with
%psource c.clabel
or directly invoke your $EDITOR on the file were it is defined with
%edit c.clabel