I'm trying to insert an axis inside a plot but without the frame. Somehow the options do not work at all
I have the following mwe
import matplotlib.pyplot as plt
import mpl_toolkits.axisartist as AA
f = plt.figure()
ax = f.add_subplot(111)
iax = AA.Axes(f, [0.125, 0.125, 0.775, 0.2], frameon=False)
f.add_axes(iax, frameon=False)
iax.set_yticks([]), iax.set_xticks([])
plt.show()
The purpose of using matplotlib.axisartist is not clear from the question. To obtain a frameless axes you may use figure.add_axes([..], frameon=False):
import matplotlib.pyplot as plt
f = plt.figure()
ax = f.add_subplot(111)
iax = f.add_axes([0.125, 0.125, 0.775, 0.2], frameon=False)
iax.set_yticks([]), iax.set_xticks([])
iax.plot(range(10),[i%4 for i in range(10)])
plt.show()
Related
I want to implement buttons to turn on/off the fliers in a set of seaborn boxplots. I tried to follow the method of changing through the artists mentioned in this link: https://stackoverflow.com/a/36893152/18193150 but was unsuccessful. Appreciate if someone can show me how to do it. Cheers.
This is the code I tried with:
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
from matplotlib.widgets import Button
x = np.arange(1, 13)
index = np.repeat(x, 40)
np.random.seed(123)
df = pd.DataFrame({'A': np.random.normal(30, 2, len(index)),
'B': np.random.normal(10, 2, len(index))},
index=index)
red_diamond = dict(markerfacecolor='r', marker='D')
blue_dot = dict(markerfacecolor='b', marker='o')
fig=plt.figure(figsize=[10, 5])
ax = sns.boxplot(data=df, x=df.index, y='A', width=0.5, color='red',
boxprops=dict(alpha=.5, label='A'), flierprops=red_diamond)
sns.boxplot(data=df, x=df.index, y='B', width=0.5, color='blue',
boxprops=dict(alpha=.5, label='B'), flierprops=blue_dot, ax=ax)
# button to off boxplot fliers
resetax_off = plt.axes([0.8, 0.02, 0.08, 0.035])
button_off = Button(resetax_off, 'Flier off', color='red',
hovercolor='lightslategrey')
# button to on boxplot fliers
resetax_on = plt.axes([0.6, 0.02, 0.08, 0.035])
button_on = Button(resetax_on, 'Flier on', color='gold',
hovercolor='lightslategrey')
def click_off(event):
for i,artist in enumerate(ax.artists):
line = ax1.line[i+4] #trying to get Line2D for the fliers, 4th in the list of 6
line.set(alpha=0)
fig.canvas.draw_idle()
button_off.on_clicked(click_off)
def click_on(event):
for i,artist in enumerate(ax.artists):
line = ax1.line[i+4] #trying to get Line2D for the fliers
line.set(alpha=1)
fig.canvas.draw_idle()
button_on.on_clicked(click_on)
plt.show()
I have 2 sets of boxplots, one set in blue color and another in red color. I want the legend to show the label for each set of boxplots, i.e.
Legend:
-blue box- A, -red box- B
Added labels='A' and labels='B' within sns.boxplot(), but didn't work with error message "No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument". How do I add the labels?
enter image description here
code for the inserted image:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
x = list(range(1,13))
n = 40
index = [item for item in x for i in range(n)]
np.random.seed(123)
df = pd.DataFrame({'A': np.random.normal(30, 2, len(index)),
'B': np.random.normal(10, 2, len(index))},
index=index)
red_diamond = dict(markerfacecolor='r', marker='D')
blue_dot = dict(markerfacecolor='b', marker='o')
plt.figure(figsize=[10,5])
ax = plt.gca()
ax1 = sns.boxplot( x=df.index, y=df['A'], width=0.5, color='red', \
boxprops=dict(alpha=.5), flierprops=red_diamond, labels='A')
ax2 = sns.boxplot( x=df.index, y=df['B'], width=0.5, color='blue', \
boxprops=dict(alpha=.5), flierprops=blue_dot, labels='B')
plt.ylabel('Something')
plt.legend(loc="center", fontsize=8, frameon=False)
plt.show()
Here are the software versions I am using: seaborn version 0.11.2. matplotlib version 3.5.1. python version 3.10.1
The following approach sets a label via the boxprops, and creates a legend using part of ax.artists. (Note that ax, ax1 and ax2 of the question's code are all pointing to the same subplot, so here only ax is used.)
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
x = np.arange(1, 13)
index = np.repeat(x, 40)
np.random.seed(123)
df = pd.DataFrame({'A': np.random.normal(30, 2, len(index)),
'B': np.random.normal(10, 2, len(index))},
index=index)
red_diamond = dict(markerfacecolor='r', marker='D')
blue_dot = dict(markerfacecolor='b', marker='o')
plt.figure(figsize=[10, 5])
ax = sns.boxplot(data=df, x=df.index, y='A', width=0.5, color='red',
boxprops=dict(alpha=.5, label='A'), flierprops=red_diamond)
sns.boxplot(data=df, x=df.index, y='B', width=0.5, color='blue',
boxprops=dict(alpha=.5, label='B'), flierprops=blue_dot, ax=ax)
ax.set_ylabel('Something')
handles, labels = ax.get_legend_handles_labels()
handles = [h for h, lbl, prev in zip(handles, labels, [None] + labels) if lbl != prev]
ax.legend(handles=handles, loc="center", fontsize=8, frameon=False)
plt.show()
Alternative approaches could be:
pd.melt the dataframe to long form, so hue could be used; a problem here is that then the legend wouldn't take the alpha from the boxprops into account; also setting different fliers wouldn't be supported
create a legend from custom handles
f = plt.figure(figsize=(12,10))
ax1 = f.add_subplot(411)
ax2 = f.add_subplot(422)
ax3 = f.add_subplot(423)
ax4 = f.add_subplot(424)
ax5 = f.add_subplot(425)
ax6 = f.add_subplot(426)
ax7 = f.add_subplot(427)
ax8 = f.add_subplot(428)
I want to increase space between two rows: ax1 and ax2-ax3. Other spaces should remain the same. Using "f.subplots_adjust(hspace = 0.2, wspace= 0.25)" adjusts the spacing for all subplots. What can I do to increase hspace for the top-most subplot only?
import matplotlib.pyplot as plt
fig, axs = plt.subplot_mosaic([['top', 'top'],['left1', 'right1'], ['left2', 'right2']],
constrained_layout=True)
axs['top'].set_xlabel('Xlabel\n\n')
plt.show()
This will make all the y-axes the same size. If that is not important to you, then #r-beginners answer is helpful. Note that you need-not use subplot mosaic, though it is a useful new feature.
If you are not worried about the axes sizes matching, then a slightly better way than proposed above is to use the new subfigure functionality:
import matplotlib.pyplot as plt
fig = plt.figure(constrained_layout=True)
subfigs = fig.subfigures(2, 1, height_ratios=[1, 2], hspace=0.15)
# top
axtop = subfigs[0].subplots()
# 2x2 grid
axs = subfigs[1].subplots(2, 2)
plt.show()
Based on the gridspec sample in the official reference, I customized it using this example answer.The point is to use gridspec for the separate graphs you want to configure.
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
def format_axes(fig):
for i, ax in enumerate(fig.axes):
ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center")
ax.tick_params(labelbottom=False, labelleft=False)
fig = plt.figure()
gs_top = GridSpec(3, 3, top=0.95)
gs_base = GridSpec(3, 3)
ax1 = fig.add_subplot(gs_top[0, :])
# identical to ax1 = plt.subplot(gs.new_subplotspec((0, 0), colspan=3))
ax2 = fig.add_subplot(gs_base[1, :-1])
ax3 = fig.add_subplot(gs_base[1:, -1])
ax4 = fig.add_subplot(gs_base[-1, 0])
ax5 = fig.add_subplot(gs_base[-1, -2])
# fig.suptitle("GridSpec")
format_axes(fig)
plt.show()
I am making a set of figures with subplots in Jupyter Notebook using matplotlib and geopandas. The top plots (A & B) have geospatial data and use various basemaps (aerial imagery, shaded relief, etc.).
How can I rotate the top two plots 90-degrees, so that they are elongated?
(I will need to redo gridspec layout of course, but that is easy; what I don't know how to do is: rotate the plots but keep the geographic information for basemap plotting.)
Repeatable code is below.
import pandas as pd
import geopandas as gpd
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import contextily as ctx
from shapely.geometry import Point
plt.style.use('seaborn-whitegrid')
### DUMMY DATA
long, lat = [(-118.155, -118.051, -118.08), (38.89, 39.512, 39.1)]
q, t = [(0, 70500, 21000), (0, 8000, -1200)]
df = pd.DataFrame(list(zip(q, t, lat, long)), columns =['q', 't', 'lat', 'long'])
gdf = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(df['long'], df['lat']))
gdf.crs = "EPSG:4326"
### PLOTTING
fig = plt.figure(figsize=(10,7.5), constrained_layout=True)
gs = fig.add_gridspec(3, 2)
ax1 = fig.add_subplot(gs[0:2, 0])
ax2 = fig.add_subplot(gs[0:2, 1], sharex = ax1, sharey = ax1)
ax3 = fig.add_subplot(gs[-1, :])
### PlotA
gdf.plot(ax = ax1)
ctx.add_basemap(ax1, crs='epsg:4326', source=ctx.providers.Esri.WorldShadedRelief)
ax1.set_aspect('equal')
ax1.set_title('Plot-A')
ax1.tick_params('x', labelrotation=90)
### PlotB
gdf.plot(ax = ax2)
ctx.add_basemap(ax2, crs='epsg:4326', source=ctx.providers.Esri.WorldImagery, alpha=0.5)
ax2.set_aspect('equal')
ax2.set_title('Plot-B')
ax2.tick_params('x', labelrotation=90)
### PlotC
ax3.scatter(df.q, df.t)
ax3.set_aspect('equal')
ax3.set_title('Plot-C')
ax3.set_xlabel('q')
ax3.set_ylabel('t')
I want 3 rows of subplots each of different widths, but which all share the same X-axis, such as in the rough mock-up below. How can I do this? Can I use sharex=True even in GridSpec-adjusted plots?
You can place the axes by hand, or another method is to use an inset_axes:
import matplotlib.pyplot as plt
import numpy as np
fig, axs = plt.subplots(3, 1, constrained_layout=True, sharex=True, sharey=True)
ylim=[-3, 3]
axs[2].plot(np.random.randn(500))
axs[2].set_ylim(ylim)
xlim = axs[2].get_xlim()
ax0 = axs[0].inset_axes([300, ylim[0], xlim[1]-300, ylim[1]-ylim[0]], transform=axs[0].transData)
ax0.set_ylim(ylim)
ax0.set_xlim([300, xlim[1]])
axs[0].axis('off')
ax0.plot(np.arange(300, 500), np.random.randn(200))
ax1 = axs[1].inset_axes([150, ylim[0], xlim[1] - 150, ylim[1]-ylim[0]], transform=axs[1].transData)
ax1.set_ylim(ylim)
ax1.set_xlim([150, xlim[1]])
axs[1].axis('off')
ax1.plot(np.arange(150, 500), np.random.randn(350))
plt.show()
You can pass which axes to use as reference for sharing axes when you create your subplot
fig = plt.figure()
gs = matplotlib.gridspec.GridSpec(3,3, figure=fig)
ax1 = fig.add_subplot(gs[0,2])
ax2 = fig.add_subplot(gs[1,1:], sharex=ax1)
ax3 = fig.add_subplot(gs[2,:], sharex=ax1)
ax1.plot([1,5,0])