Changing seaborn style in subplots - python

I'm trying to change the styles of two plots that are in the same figure:
import numpy as np
from numpy.random import randn
import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(15,6))
data = randn(7500);
sns.set_style("whitegrid");
ax1.hist(data, bins=8);
sns.set_style("darkgrid");
ax2.hist(data, bins=8);
This does not work, both plots end up having the "darkgrid" background. I have also trying tinkering with axes_style() with no success.

The way matplotlib Axes work is that the style parameters become a property of the Axes object at the time it is created, not at the time something is drawn onto it. So while it's not possible to make a figure that has different subplot styles using plt.subplots, you can do it with one of the ways where you independently create the Axes:
fig = plt.figure()
with sns.axes_style("whitegrid"):
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
Note also that I'm using a context manager to style the first Axes, so the second Axes will have the default style. It's possible to use sns.set_style before each add_subplot command like you do in the question, but using the context manager to create the non-default plot feels a little bit more Pythonic.

Related

Understanding of fig, ax, and plt when combining Matplotlib and Pandas

I'm trying to get a better understanding of how figure, axes, and plt all fit together when combining Matplotlib and Pandas for plotting. The accepted answer here helped me connect Matplotlib and Pandas in an object oriented way I understand through this line:
fig, ax = plt.suplots()
df.plot(ax=ax)
But as I'm diving deeper the answer here threw me off. Specifically, I still have methods I need to call directly off plt, that don't apply to either a figure or an axis. Example:
fig, ax = plt.subplots()
df[['realgdp','trend']]["2000-03-31":].plot(figsize=(8,8), ax=ax)
ax.set_title('Real GDP & Trend')
ax.set_ylabel('Readl GDP')
plt.xticks(rotation=45)
If I try to call xticks(rotation=45) off ax or fig I get an error that neither ax nor fig have an xticks method. The solution I have above works, but I don't understand why.
When I type plt.xticks(rotations=45), where does that information get sent? Why does the comment in the answer here that "when you use the functions available on the module pyplot you are plotting to the 'current figure' and 'current axes'" not apply in this case? Why do I need to call off plt directly?
plt.xticks() only works on the "current" ax. You should use ax.set_xticks(), ax.set_xticklabels() and ax.tick_params() instead.
plt.xticks() is a rather old function that is still supported, mimicking similar matlab code, born in a time when people were only plotting onto a single plot. The newer functions are more general with more options.
In short: you don't need to call plt directly, you are invited to use the ax functions instead. When calling plt.xticks(), it gets rerouted to the currently active ax (often the last one created).

importing seaborn stops set rc_params from working

When I run the below code it works perfectly fine if the line importing seaborn is commented out, one can set the fontsize in the function and it sets it throughout the plot (I am using it for a more complicated function with several subplots and axes and want a universal font setting). Why is seaborn stopping my with plt.rc_context({'font.size': fontsize,}): from working and how might I stop it doing as such while still being able to make use of seaborn's functionality? (I don't need it's styling defaults though if the solution involves removing those)
import matplotlib.pyplot as plt
import numpy as np
import seaborn
def plotthing(x, y, fontsize=8):
with plt.rc_context({'font.size': fontsize,}):
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set_xlabel("x")
ax.set_xlabel("y")
return fig, ax
x = np.arange(0, 10)
y = 2*x**2
fig, ax = plotthing(x, y, fontsize=2)
fig.savefig("test.pdf")
If you don't want seaborn to make any style changes, you may import the seaborn API alone:
import seaborn.apionly as sns
This also works fine in the case from the question.
I solved this by adding
# reset RC params to original
sns.reset_orig()
After I imported seaborn to undo it's changes to matplotlib's rc params

How to suppress seaborn output when recalling figure object with regplot

I am trying to plot data to a figure and respective axis in matplotlib and as new work comes up, recall the figure with the additional plot on the axis:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
x=np.arange(0,20)
y=2*x
fig,ax=plt.subplots()
ax.scatter(x,x)
ax.scatter(x,y)
fig
Which works fine with matplotlib, if I however use seaborn's regplot:
fig2,ax2=plt.subplots()
sns.regplot(x,x,ax=ax2,fit_reg=False)
sns.regplot(x,y,ax=ax2,fit_reg=False)
fig2
fig2 generates the figure that I want but the regplot command generates an empty figure. Is there a way to suppress the regplot's empty output or have it display the updated ax2 without recalling fig2?
It seems you are using the jupyter notebook with the inline backend. In some circumstances regplot triggers the creation of a new figure even if the artists are being added to the previous one and this messes up the output. I don't know why this happens but I found a workaround that might help you, using plt.ioff to temporarily disable automatic display of figures.
plt.ioff()
fig, ax = plt.subplots()
sns.regplot(x, x, ax=ax)
fig
sns.regplot(x, 2 * x, ax=ax)
fig
You have to call plt.ioff before creating the figure for this to work. After that you have to explicitly display the figure. Then you can call plt.ion to restore the default behaviour.
regplot does not generate an empty figure. According to the documentation:
Understanding the difference between regplot() and lmplot() can be a
bit tricky. In fact, they are closely related, as lmplot() uses
regplot() internally and takes most of its parameters. However,
regplot() is an axes-level function, so it draws directly onto an axes
(either the currently active axes or the one provided by the ax
parameter), while lmplot() is a figure-level function and creates its
own figure, which is managed through a FacetGrid.
When I do the following:
fig2,ax2 = plt.subplots()
same_fig2 = sns.regplot(x,x,ax=ax2,fit_reg=False)
same_fig2.figure is fig2
>>> True

How modules know each other

I can plot data from a CSV file with the following code:
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('test0.csv',delimiter='; ', engine='python')
df.plot(x='Column1', y='Column3')
plt.show()
But I don't understand one thing. How plt.show() knows about df? I'll make more sense to me seeing, somewhere, an expression like:
plt = something(df)
I have to mention I'm just learning Python.
Matplotlib has two "interfaces": a Matlab-style interface and an object-oriented interface.
Plotting with the Matlab-style interface looks like this:
import matplotlib.pyplot as plt
plt.plot(x, y)
plt.show()
The call to plt.plot implicitly creates a figure and an axes on which to draw.
The call to plt.show displays all figures.
Pandas is supporting the Matlab-style interface by implicitly creating a figure and axes for you when df.plot(x='Column1', y='Column3') is called.
Pandas can also use the more flexible object-oriented interface, in which case
your code would look like this:
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('test0.csv',delimiter='; ', engine='python')
fig, ax = plt.subplots()
df.plot(ax=ax, x='Column1', y='Column3')
plt.show()
Here the axes, ax, is explicitly created and passed to df.plot, which then
calls ax.plot under the hood.
One case where the object-oriented interface is useful is when you wish to use
df.plot more than once while still drawing on the same axes:
fig, ax = plt.subplots()
df.plot(ax=ax, x='Column1', y='Column3')
df2.plot(ax=ax, x='Column2', y='Column4')
plt.show()
From the pandas docs on plotting:
The plot method on Series and DataFrame is just a simple wrapper
around :meth:plt.plot() <matplotlib.axes.Axes.plot>
So as is, the df.plot method is an highlevel call to plt.plot (using a wrapper), and thereafter, calling plt.show will simply:
display all figures and block until the figures have been closed
as it would with for all figures plotted with plt.plot.
Therefore, you don't see plt = something(df) as you would expect, because matpotlib.pyplot.plot is being called behind the scene by df.plot.
According to http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.show , the plt.show() itself doesn't know about the data, you need to pass the data as parameters.
What you are seeing should be the plot of pandas library, according to the usage http://pandas.pydata.org/pandas-docs/stable/visualization.html#basic-plotting-plot.
Hope this solves your question.

How to create a plot in matplotlib without using pyplot

I've been using matplotlib for five months now on a daily basis, and I still find creation of new figures confusing.
Usually I create a figure with 2x2 subplots using, for example, somthing like:
import matplotlib.pyplot as plt
import itertools as it
fig,axes = plt.subplots(2,2)
axit = (ax for ax in it.chain(*axes))
for each of four data series I want to plot:
ax = next(axit)
ax.plot(...)
The question I have now is: how can operate completely independently of pyplot, ie, how can I create a figure, populate it with plots, make style changes, and only tell that figure to appear at the exact moment I want it to appear. Here is what I am having trouble with:
import matplotlib as mpl
gs = gridspec.GridSpec(2,2)
fig = mpl.figure.Figure()
ax1 = fig.add_subplot(gs[0])
ax1.plot([1,2,3])
ax2 = fig.add_subplot(gs[1])
ax2.plot([3,2,1])
After running the above, the only thing that comes to mind would be to use:
plt.draw()
But this does not work. What is missing to make the figure with the plots appear? Also, is
fig = mpl.figure.Figure()
all I have to do to create the figure without pyplot?
This works for me without matplotlib.pyplot
import sys
from PyQt5 import QtWidgets
from matplotlib.backends.backend_qt5agg import (
FigureCanvasQTAgg as FigureCanvas)
from matplotlib.figure import Figure
import numpy as np
fig=Figure()
canvas=FigureCanvas(fig)
ax=canvas.figure.add_subplot(111)
x=np.arange(-5,5,0.1)
y=np.sin(x)
ax.plot(x,y)
canvas.show()
app=QtWidgets.QApplication(sys.argv)
app.exec()
You could attach a suitable backend to your figure manually and then show it:
from matplotlib.backends import backend_qt4agg # e.g.
backend_qt4agg.new_figure_manager_given_figure(1, fig)
fig.show()
... but why not use pyplot?

Categories

Resources