How to set seaborn styles on existing matplotlib axes - python-3.x

I'm trying to use seaborn to set axes properties for a (potentially large) number of matplotlib subfigures. What I would like to be able to do is generate all the plots with a single call to plt.subplots, and then set the subplot style when each actual plot is generated. Unfortunately it seems that the sns style only matters when the subplot is generated.
The code below is a minimum (non)working example. Ideally the two subfigures would have two different styles, but they do not.
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
def makeplt(sub, dat):
sub.contour(dat)
def makepltwith(sub, dat, style):
with sns.axes_style(style) as sty:
sub.contour(dat)
dat = np.arange(100).reshape(10, 10)
with sns.axes_style('ticks'):
fig, subs = plt.subplots(ncols=2)
makeplt(subs[0], dat)
makepltwith(subs[1], dat, 'darkgrid')
plt.show()
Is there a way to ensure that the second plot has the formatting I want it to have? The best idea I have on my own is to make some use of the sty object to manually reformat the sub object, but I can't come up with a pithy way of running through the formatting.
seaborn.__version__=0.7,
matplotlib.__version__=1.5 if that matters.

I encountered a similar problem and solved it like this:
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
def add_sp_default(fig,pos):
ax = fig.add_subplot(pos)
return ax
def add_sp_image(fig,pos):
ax = fig.add_subplot(pos)
img=mpimg.imread('http://static.wixstatic.com/media/4afb41_998a1c7c0835c6eae5e159be3c2cfc07.png_1024')
ax.imshow(img)
ax.set_axis_off()
return ax
def add_sp_polar(fig,pos):
ax = fig.add_subplot(pos,projection='polar')
return ax
def add_sp_xkcd(fig,pos):
with plt.xkcd():
ax = fig.add_subplot(pos)
return ax
fig = plt.figure(figsize=(10,7))
ax1 = add_sp_default(fig,221)
ax2 = add_sp_image(fig,222)
ax3 = add_sp_polar(fig,223)
ax4 = add_sp_xkcd(fig,224)
plt.show()

No, it is not possible to do that. Axes styles are applied when the axes is created.
Of course, there are other ways to set up the subplots that don't involve making them all in one line of code, which would be more amenable to subplot-specific styles.

Related

Updating a plot in Python

My situation is this: I am developing a Jupyter-lab notebook to exemplify engineering topics. I find myself in the need of plotting something in an axes object within a figure, and then using a slider interact changing a value to update the plot.
Here is a MWE (or at least a shorter Working Example):
import ipywidgets as widgets
from ipywidgets import interact
import numpy as np
import matplotlib.pyplot as plt
global ax1
global fig
fig, (ax1) = plt.subplots(ncols=1, subplot_kw=dict(projection='polar'))
RAD = np.array([0.85, 0.85, 0.85])
ANG = np.array([np.pi/2, np.pi*(2/3+1/2), np.pi*(1/2-2/3)])
c = ax1.scatter(ANG, RAD)
ax1.set_ylim([0, 1])
ax1.set_yticklabels([])
def h(rh):
RADp = np.array([rh, rh, rh])
ANGp = np.array([-np.pi/2, np.pi*(2/3-1/2), np.pi*(-1/2-2/3)])
cp = ax1.scatter(ANGp, RADp)
ax1.add_artist(cp)
plt.show()
return (rh)
interact(h, rh = widgets.FloatSlider(min=0, max=1, step=0.001, value=1));
In this example I create the figure fig and its axes ax1 declared as global variables (so that they will be available within function h. Then using RAD and ANG I create a scatter plot c.
Afterwards using the interact widget I would like to have three crosses change position along the r axis by changing the value of rh with the slider.
I don't get any error, but neither get I any crosses at all.
In the actual code I use pcolormesh instead of scatter.
I hope I made myself clear. I had got ti working by creating the figure and ax1 each time the function is called, but then I added some more suff thath don't need to be plotted each time.
Thanks for taking the time to read!
A very limited answer is that you function should return fig not rh.
Also note that you don't need the lines with global, and plt.show()
import ipywidgets as widgets
from ipywidgets import interact
import numpy as np
import matplotlib.pyplot as plt
fig, (ax1) = plt.subplots(ncols=1, subplot_kw=dict(projection='polar'))
RAD = np.array([0.85, 0.85, 0.85])
ANG = np.array([np.pi/2, np.pi*(2/3+1/2), np.pi*(1/2-2/3)])
c = ax1.scatter(ANG, RAD)
ax1.set_ylim([0, 1])
ax1.set_yticklabels([])
def h(rh):
RADp = np.array([rh, rh, rh])
ANGp = np.array([-np.pi/2, np.pi*(2/3-1/2), np.pi*(-1/2-2/3)])
cp = ax1.scatter(ANGp, RADp)
ax1.add_artist(cp)
# plt.show()
return fig
interact(h, rh = widgets.FloatSlider(min=0, max=1, step=0.001, value=1));
I say limited because I think you want to update rather than add point?
A version which is hopefully more in line with what you want
the key point being the use of set_offsets method to update the positions.
import ipywidgets as widgets
from ipywidgets import interact
import numpy as np
import matplotlib.pyplot as plt
fig, (ax1) = plt.subplots(ncols=1, subplot_kw=dict(projection='polar'))
RAD = np.array([0.85, 0.85, 0.85])
ANG = np.array([np.pi/2, np.pi*(2/3+1/2), np.pi*(1/2-2/3)])
c = ax1.scatter(ANG, RAD)
ax1.set_ylim([0, 1])
ax1.set_yticklabels([])
def h(rh):
new = [
[-np.pi/2, rh],
[np.pi*(2/3-1/2), rh],
[np.pi*(-1/2-2/3), rh],
]
c.set_offsets(new)
return fig
interact(h, rh = widgets.FloatSlider(min=0, max=1, step=0.001, value=1));

Why is there unnecessary whitespace while plotting figures with pandas, matplotlib and seaborn? [duplicate]

This question already has answers here:
How can I change the x axis in matplotlib so there is no white space?
(2 answers)
Closed 3 years ago.
Whenever I plot figures using matplotlib or seaborn, there is always some whitespace remaining at the sides of the plot and the top and bottom of the plot. The (x_0,y_0) is not in the bottom left corner, x_0 is offset a little bit to the right and y_0 is offset a little bit upwards for some reason? I will demonstrate a quick example below with the 'ggplot' style so it is clear what I mean:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use('ggplot')
fig = plt.figure()
x = np.linspace(0,5,11)
ax = fig.add_axes([0.1,0.1,1,1])
ax.plot(x,x**2)
How do I get (0,0) to the bottom left corner and how do I get rid of the unnecessary space where y > 25, x >5?
Thank you.
The "whitespace" is caused by the plot margins. A better way to get rid of them without changing the axes limits explicitly is to set 0-margins
plt.style.use('ggplot')
fig = plt.figure()
x = np.linspace(0,5,11)
ax = fig.add_axes([0.1,0.1,1,1])
ax.margins(x=0,y=0)
ax.plot(x,x**2)
Alternatively:
x = np.linspace(0,5,11)
plt.xlim((0,5))
plt.ylim((0,25))
plt.plot(x,x**2);
To not have borders you can use set_xlim and set_ylim:
ax.set_xlim([0, 5])
ax.set_ylim([0, 25])
Full code:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use('ggplot')
fig = plt.figure()
x = np.linspace(0,5,11)
ax = fig.add_axes([0.1,0.1,1,1])
ax.plot(x,x**2)
ax.set_xlim([0, 5])
ax.set_ylim([0, 25])
plt.show()

How to remove or include errorbar plot from matplotlib axes?

This code shows an Attribute error:
I am plotting errorbar plot for let's say 10 different datasets (huge datasets) from a file containing multiple datasets (let's say for different days), and I am showing the user an option (Checkbox) to remove or include a plot of the particular dataset (through GUI).
So for this, I just want to erase the current axes and at a later time want to redraw it again.
How can I do this?
Below is a simplified example to show what I need.
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(1)
x, y, yerr = np.random.rand(3,10)
l = ax.errorbar(x, y, yerr, marker='s', mfc='red', mec='green', ms=20, mew=4)
canvas = fig.canvas
canvas.draw()
bkg = canvas.copy_from_bbox(ax.bbox)
plt.show()
plt.pause(1)
ax.clear()
canvas.restore_region(bkg)
ax.draw_artist(l)
# here it throws an AttributeError: 'ErrorbarContainer'
#object has no attribute 'draw'

Matplotlib get all axes artist objects for ArtistAnimation?

I am trying to make an animation using ArtistAnimation like this:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
ims = []
for i in range(60):
x = np.linspace(0,i,1000)
y = np.sin(x)
im = ax.plot(x,y, color='black')
ims.append(im)
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True,
repeat_delay=1000)
plt.show()
This animates a sine wave growing across the figure. Currently I'm just adding the Lines2D object returned by ax.plot() to ims. However, I would like to potentially draw multiple overlapping plots on the Axes and adjust the title, legend and x-axis range for each frame. How do I get an object that I can add to ims after plotting and making all the changes I want for each frame?
The list you supply to ArtistAnimation should be a list of lists of artists, one list per frame.
artist_list = [[line1a, line1b, title1], [line2a, line2b, title2], ...]
where the first list is shown in the first frame, the second list in the second frame etc.
The reason your code works is that ax.plot returns a list of lines (in your case only a list of a single line).
In any case, the following might be a more understandable version of your code where an additional text is animated.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
artist_list = []
for i in range(60):
x = np.linspace(0,i,1000)
y = np.sin(x)
line, = ax.plot(x,y, color='black')
text = ax.text(i,0,i)
artist_list.append([line, text])
ani = animation.ArtistAnimation(fig, artist_list, interval=50, blit=True,
repeat_delay=1000)
plt.show()
In general, it will be hard to animate changing axes limits with ArtistAnimation, so if that is an ultimate goal consider using a FuncAnimation instead.

MatPlotLib + GeoPandas: Plot Multiple Layers, Control Figsize

Given the shape file available here: I know can produce the basic map that I need with county labels and even some points on the map (see below). The issue I'm having is that I cannot seem to control the size of the figure with figsize.
Here's what I have:
import geopandas as gpd
import matplotlib.pyplot as plt
%matplotlib inline
figsize=5,5
fig = plt.figure(figsize=(figsize),dpi=300)
shpfileshpfile=r'Y:\HQ\TH\Groups\NR\PSPD\Input\US_Counties\cb_2015_us_county_20m.shp'
c=gpd.read_file(shpfile)
c=c.loc[c['GEOID'].isin(['26161','26093','26049','26091','26075','26125','26163','26099','26115','26065'])]
c['coords'] = c['geometry'].apply(lambda x: x.representative_point().coords[:])
c['coords'] = [coords[0] for coords in c['coords']]
ax=c.plot()
#Control some attributes regarding the axis (for the plot above)
ax.spines['top'].set_visible(False);ax.spines['bottom'].set_visible(False);ax.spines['left'].set_visible(False);ax.spines['right'].set_visible(False)
ax.tick_params(axis='y',which='both',left='off',right='off',color='none',labelcolor='none')
ax.tick_params(axis='x',which='both',top='off',bottom='off',color='none',labelcolor='none')
for idx, row in c.iterrows():
ax.annotate(s=row['NAME'], xy=row['coords'],
horizontalalignment='center')
lat2=[42.5,42.3]
lon2=[-84,-83.5]
#Add another plot...
ax.plot(lon2,lat2,alpha=1,marker='o',linestyle='none',markeredgecolor='none',markersize=15,color='white')
plt.show()
As you can see, I opted to call the plots by the axis name because I need to control attributes of the axis, such as tick_params. I'm not sure if there is a better approach. This seems like a "no-brainer" but I can't seem to figure out why I can't control the figure size.
Thanks in advance!
I just had to do the following:
Use fig, ax = plt.subplots(1, 1, figsize = (figsize))
2.use the ax=ax argument in c.plot()
import geopandas as gpd
import matplotlib.pyplot as plt
%matplotlib inline
figsize=5,5
#fig = plt.figure(figsize=(figsize),dpi=300)
#ax = fig.add_subplot(111)
fig, ax = plt.subplots(1, 1, figsize = (figsize))
shpfileshpfile=r'Y:\HQ\TH\Groups\NR\PSPD\Input\US_Counties\cb_2015_us_county_20m.shp'
c=gpd.read_file(shpfile)
c=c.loc[c['GEOID'].isin(['26161','26093','26049','26091','26075','26125','26163','26099','26115','26065'])]
c['coords'] = c['geometry'].apply(lambda x: x.representative_point().coords[:])
c['coords'] = [coords[0] for coords in c['coords']]
c.plot(ax=ax)
ax.spines['top'].set_visible(False);ax.spines['bottom'].set_visible(False);ax.spines['left'].set_visible(False);ax.spines['right'].set_visible(False)
ax.tick_params(axis='y',which='both',left='off',right='off',color='none',labelcolor='none')
ax.tick_params(axis='x',which='both',top='off',bottom='off',color='none',labelcolor='none')
for idx, row in c.iterrows():
ax.annotate(s=row['NAME'], xy=row['coords'],
horizontalalignment='center')
lat2=[42.5,42.3]
lon2=[-84,-83.5]
ax.plot(lon2,lat2,alpha=1,marker='o',linestyle='none',markeredgecolor='none',markersize=15,color='white')

Resources