Jupyter notebook inline plotting breaks, when loading matplotlib rcparams from matplotlibrc file - python

I want to write a module with a function to set default style parameters from a matplotlibrc file. A minimal example of the module style.py:
import matplotlib as mpl
def set_default():
mpl.rc_file('./matplotlibrc')
If I want to use the module in a jupyter notebook with inline plotting, the inline plot is not shown, when I call style.set_default() before having plotted anything before.
So If I call
%matplotlib inline
style.set_default()
plt.plot()
the output is an empty list and no plot is shown. If I call e.g.
plt.plot()
after enabling inline plotting and before calling the set_default function, the output of both plot calls is shown inline.
This even happens, when the matplotlibrc file is empty as it is in my minimal example.
Does anyone understand why this happens and has an idea how to solve this problem or another way how to set a default style in a module using a matplotlibrc file?
Here are also two images of both cases in a jupyter notebook:
inline broken
inline working
Extra question: Why is the second plot in the second case larger, when the loaded matplotlibrc is empty?

Short version: Use mpl.style.use instead of mpl.rc_file.
Long version:
You may print out the backend in use to see what is going on.
import matplotlib as mpl
def set_default():
mpl.rc_file('matplotlibrc.txt') # this is an empty file
import matplotlib.pyplot as plt
print mpl.get_backend()
# This prints u'TkAgg' (in my case) the default backend in use
# due to my rc Params
%matplotlib inline
print mpl.get_backend()
# This prints "module://ipykernel.pylab.backend_inline", because inline has been set
set_default()
print mpl.get_backend()
# This prints "agg", because this is the default backend reset by setting the empty rc file
plt.plot()
# Here, no plot is shown because agg (a non interactive backend) is used.
Until here no supprise.
Now the second case.
import matplotlib as mpl
def set_default():
mpl.rc_file('matplotlibrc.txt') # this is an empty file
import matplotlib.pyplot as plt
print mpl.get_backend()
# This prints u'TkAgg' (in my case) the default backend in use, same as above
%matplotlib inline
print mpl.get_backend()
# This prints "module://ipykernel.pylab.backend_inline", because inline has been set
plt.plot()
# This shows the inline plot, because the inline backend is active.
set_default()
print mpl.get_backend()
# This prints "agg", because this is the default backend reset by setting the new empty rc file
plt.plot()
# Here comes the supprise: Although "agg" is the backend, still, an inline plot is shown.
# This is due to the inline backend being the one registered in pyplot
# when doing the first plot. It cannot be changed afterwards.
The main point is, you may still change the backend, until the first plot is produced, not after.
The same argument goes for the figure size. The default matplotlib figure size is (6.4,4.8), while the one being set with the inline backend is (6.0,4.0). Also the figure dpi is different, it is 100 in the default rcParams, but 72. in the inline configuration. This makes the plot appear much smaller.
Now to the actual problem. I suppose the use of a stylesheet is meant here to set some styles for plots, not to change the backend. Hence you would rather only set the style from the rc file. This can be done in the usual way,using matplotlib.style.use
def set_default():
mpl.style.use('matplotlibrc.txt')
When this is used, it will not overwrite the backend in use, but only update those parameters, specified in the file itself.

Related

Change default matplotlib value in jupyter notebook

I was wondering how you can change the matplotlib.rcParams permanently instead of indicating it every time when you open jupyter notebook. (like change in the profile as the default value).
Thanks
A short answer, also for personal reference...
I would still prefer indicating it in the code though. However it doesn't have to be done for every single parameters as it can be done in one line calling for a style file for instance.
Anyway, here are several ways of doing so, from the most permanent change to the most soft and subtle way of changing the parameters:
1. Change default config file
Change the default parameters in matplotlibrc file, you can find its location with:
import matplotlib as mpl
mpl.get_configdir()
2. Modify iPython profile
Change how matplotlib default options are loaded in iPython by modifying options in ~/.ipython/profile_default/ipython_kernel_config.py, or wherever your configuraiton file is (ipython locate profile to find out) (see this answer for details)
3. Create your own styles
You can create your own custom rcParams files and store them in .config/matplotlib/stylelib/*.mplstyle or wherever os.path.join(matplotlib.get_configdir(), stylelib) is.
The file should be in the following format, similar to matplotlibrc, e.g. my_dark.mplstyle:
axes.labelcolor: white
axes.facecolor : 333333
axes.edgecolor : white
ytick.color : white
xtick.color : white
text.color : white
figure.facecolor : 0D0D0D
This is an example of a style for a dark background with white axes, and white tick labels...
To use it, before your plots, call:
import matplotlib.pyplot as plt
plt.style.use("my_dark")
# Note that matplotlib has already several default styles you can chose from
You can overlay styles by using several ones at once, the last one overwrites its predecessors' parameters. This can be useful to keep e.g. font variables from a certain style (paper-like, presentation) but simultaneously toggle a dark mode on top, by overwriting all color-related parameters.
With something like plt.style.use(["paper_fonts", "my_dark"])
More info in their documentation.
4. Context manager for rc_params
You can also use a context manager, this option is great in my opinion, to do something like:
import matplotlib as mpl
import matplotlib.pyplot as plt
with mpl.rc_context(rc={'text.usetex': True}, fname='screen.rc'):
plt.plot(x, a)
plt.plot(x, b)
Here we used a first set of parameters as defined in the file screen.rc then tweak the text.usetex value solely for the plt.plot(x,a) while plt.plot(x, b) will be using another (default here) set of rc parameters.
Again, see the doc for more information.

How do pyplot functions (show, savefig, etc) work without "object" being passed in?

How do pyplot functions such as show() and savefig() not require a "plot object" to work?
For example, the following code works, but somehow I expect to use a file handler or pass a "plot object" into plt.show() and plot.savefig("venn3.pdf").
from matplotlib import pyplot as plt
from matplotlib_venn import venn3, venn3_circles
# Subset sizes
s = (2,3,4,3,1,0.5,4)
v = venn3(subsets=s, set_labels=('A', 'B', 'C'))
# Subset labels
v.get_label_by_id('100').set_text('Abc')
v.get_label_by_id('010').set_text('aBc')
v.get_label_by_id('110').set_text('ABc')
v.get_label_by_id('001').set_text('Abc')
v.get_label_by_id('101').set_text('aBc')
v.get_label_by_id('011').set_text('ABc')
v.get_label_by_id('111').set_text('ABC')
# Subset colors
v.get_patch_by_id('100').set_color('c')
v.get_patch_by_id('010').set_color('#993333')
v.get_patch_by_id('110').set_color('blue')
# Subset alphas
v.get_patch_by_id('101').set_alpha(0.4)
v.get_patch_by_id('011').set_alpha(1.0)
v.get_patch_by_id('111').set_alpha(0.7)
# Border styles
c = venn3_circles(subsets=s, linestyle='solid')
c[0].set_ls('dotted') # Line style
c[1].set_ls('dashed')
c[2].set_lw(1.0) # Line width
plt.show() # For show() to work without using variable v seems counter-intuitive to me.
plt.savefig("venn3.pdf") # For savefig() to work without using variable v seems counter-intuitive to me.
2[]
matplotlib.pyplot is often called "statemachine". This means that the function it provides do certain things depending on the internal state of pyplot.
In your code, you have created a figure and this is stored as an object; pyplot knows it has one figure.
If you then call other commands, it is assumend that they apply to that one figure which has been created previously, like plt.savefig.
plt.show() would work on all previously created figures (all of them would be shown).
Pyplot uses a global variable to hold the figure object. All pyplot functions work with that variable(s). If you are working interactively, pyplot is perfectly fine since only you will modify that variable. If you are writing multi-threaded or multi-user code pyplot will not work, and you would have to use the layer benath it, which needs the figure object passed in (and is a terrible interface).

Why is Jupyter Notebook creating duplicate plots when making updating plots

I'm trying to make plots in a Jupyter Notebook that update every second or so. Right now, I just have a simple code which is working:
%matplotlib inline
import time
import pylab as plt
import numpy as np
from IPython import display
for i in range(10):
plt.close()
a = np.random.randint(100,size=100)
b = np.random.randint(100,size=100)
fig, ax = plt.subplots(2,1)
ax[0].plot(a)
ax[0].set_title('A')
ax[1].plot(b)
ax[1].set_title('B')
display.clear_output(wait=True)
display.display(plt.gcf())
time.sleep(1.0)
Which updated the plots I have created every second. However, at the end, there is an extra copy of the plots:
Why is it doing this? And how can I make this not happen? Thank you in advance.
The inline backend is set-up so that when each cell is finished executing, any matplotlib plot created in the cell will be displayed.
You are displaying your figure once using the display function, and then the figure is being displayed again automatically by the inline backend.
The easiest way to prevent this is to add plt.close() at the end of the code in your cell.
Another alternative would be to add ; at the end of the line! I am experiencing the same issue with statsmodels methods to plot the autocorrelation of a time series (statsmodels.graphics.tsaplot.plot_acf()):
from statsmodels.graphics.tsaplots import plot_acf
plot_acf(daily_outflow["count"]);
Despite using %matplotlib inline, it's not working for some libraries, such as statsmodels. I recommend always use plt.show() at the end of your code.

What does the matplotlib `hold` keyword argument do?

The hold argument appears in many plotting functions, for example plt.fill_between, plt.arrow, plt.axhline (for import matplotlib.pyplot as plt). I can't find any documentation on it, however. Docs seem to only state:
Additional kwargs: hold = [True|False] overrides default hold state
This is also a difficult keyword argument to google for...
It appears to be based on MATLAB's default plotting, which requires hold on to be called in order to add more than one plot on the same graph. The default behaviour for matplotlib appears to be for this to be true, consider
import numpy as np
import matplotlib.pyplot as plt
x=np.linspace(0,np.pi*2,1000)
plt.plot(x,np.sin(x),hold=True)
plt.plot(x,np.sin(x)**2,hold=True)
plt.show()
which plots both lines on the same graph. If hold is set to false, the next call to plot overwrite the previous. For example,
import numpy as np
import matplotlib.pyplot as plt
x=np.linspace(0,np.pi*2,1000)
plt.plot(x,np.sin(x),hold=True)
plt.plot(x,np.sin(x)**2,hold=False)
plt.show()
From the documentation:
When hold is True, subsequent plot commands will be added to the current axes. When hold is False, the current axes and figure will be cleared on the next plot command
The value of hold determines if future plots are drawn over the previous one(s), or if the figure is cleared before drawing.

How to disable xkcd in a matplotlib figure?

You turn on xkcd style by:
import matplotlib.pyplot as plt
plt.xkcd()
But how to disable it?
I try:
self.fig.clf()
But it won't work.
In a nutshell, either use the context manager as #Valentin mentioned, or call plt.rcdefaults() afterwards.
What's happening is that the rc parameters are being changed by plt.xkcd() (which is basically how it works).
plt.xkcd() saves the current rc params returns a context manager (so that you can use a with statement) that resets them at the end. If you didn't hold on to the context manager that plt.xkcd() returns, then you can't revert to the exact same rc params that you had before.
In other words, let's say you had done something like plt.rc('lines', linewidth=2, color='r') before calling plt.xkcd(). If you didn't do with plt.xkcd(): or manager = plt.xkcd(), then the state of rcParams after calling plt.rc will be lost.
However, you can revert back to the default rcParams by calling plt.rcdefaults(). You just won't retain any specific changes you made before calling plt.xkcd().
I see this in the doc, does it help?
with plt.xkcd():
# This figure will be in XKCD-style
fig1 = plt.figure()
# ...
# This figure will be in regular style
fig2 = plt.figure()
If not, you can look at matplotlib.pyplot.xkcd's code and see how they generate the context manager that allows reversing the config
You could try
manager = plt.xkcd()
# my xkcd plot here
mpl.rcParams.update(manager._rcparams)
this should reset the previous state, emulating the context manager. Obviously, it has not all the features for a context manager, e.g., the reset in case of exceptions, etc.
Or, w/o messing with the internals of the context manager
saved_state = mpl.rcParams.copy()
mpl.xkcd()
# my xkcd plot here
mpl.rcParams.update(saved_state)
Just use that
import matplotlib.pyplot as plt
plt.rcdefaults()
# before showing the plot
add this to the beginning of your code
import matplotlib as mpl
mpl.rcParams.update(mpl.rcParamsDefault)
you can simply use plt.rcdefaults or pyplot.rcdefaults before plt.show().
it will surely reset the rcparams to defaults.
I tried and it worked.
simply use ,
plt.xkcd(False)
that worked good for me

Categories