Python: Subplot overlapping with matplotlib - python

I have the following code for a 2x2 subplot:
figure(figsize=(10, 6), dpi=100)
plt.style.use('fivethirtyeight')
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
fig.tight_layout(pad=2)
fig.suptitle('Driving Relationships', fontsize=20)
# subplot 221
ax1.scatter(dataset2["duration"]/60, dataset2["distance"]/1000, c='red', alpha=0.5)
ax1.set_title("Duration vs Distance", fontsize=12)
ax1.set_xlabel("Duration (min)",fontsize=8)
ax1.set_ylabel("Distance (km)",fontsize=8)
# subplot 222
ax2.scatter(dataset2["duration"]/60, dataset2["speed_mean"], c='red', alpha=0.5)
ax2.set_title("Duration vs Speed", fontsize=12)
ax2.set_xlabel("Duration (min)",fontsize=8)
ax2.set_ylabel("Mean Speed (m/s)",fontsize=8)
# subplot 223
ax3.scatter(dataset2["ascent_total"], dataset2["acceleration_mean"], c='red', alpha=0.5)
ax3.set_title("Ascent vs Acceleration", fontsize=12)
ax3.set_xlabel("Ascent (m)",fontsize=8)
ax3.set_ylabel("Mean Acceleration (m/s^2)",fontsize=8)
# subplot 224
ax4.scatter(dataset2["descent_total"], dataset2["acceleration_mean"], c='red', alpha=0.5)
ax4.set_title("Descent vs Acceleration", fontsize=12)
ax4.set_xlabel("Descent (m)",fontsize=8)
ax4.set_ylabel("Mean Acceleration (m/s^2)",fontsize=8)
plt.show()
Despite my attempts to improve it, there are many overlappings as shown below:
I've tried changing the figure size (nothing happened). I also used fig.tight_layour() not a major improvement even when setting padding values. How can I fix my code to have a more presentable figure?

Try to write it after your plots
plt.tight_layout()

Apparently, for subplots changing the figure size is different. The following code did the job:
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(8, 8))

Related

Rotate xticks in subplots (xticklabels rotation)

I have a figure with four subplots in it. I want to rotate the xticks of all suplots by 45 degrees.
As per this question, I believe this can be done with plt.setp().
# Create subplots
fig, ax = plt.subplots(2, 2, figsize=(10,5), sharex=True, sharey=True)
# Try to rotate the xticks of all axes
plt.setp(plt.xticks()[1], rotation=45) # Close attempt
# Show
plt.show()
You can cycle through each of your subplots, set it to the current axes, and call plt.xticks() on each one.
fig, axes = plt.subplots(2, 2, figsize=(10,5), sharex=True, sharey=True)
for ax in axes.flatten():
plt.sca(ax)
plt.xticks(rotation = 45)
Result:

Is there a way to plot two cartopy maps as subplots in the same figure?

I have attempted to achieve this following the code provided at the end of this page.
What I have tried is the following:
fig, (ax1, ax2) = plt.subplots(1,2, figsize=(24, 10))
fig.suptitle('Comparison of 1970-2000 and 2070-2100 Soil Moisture Content')
ax1 = plt.axes(projection=ccrs.PlateCarree())
ax1.set_title('Under SSP245 Scenario')
cf1 = ax1.contourf(lon_hist, lat_hist, mrsos_change45, transform=ccrs.PlateCarree(), cmap='BrBG')
fig.colorbar(cf1, ax=ax1)
ax1.set_aspect('equal', adjustable=None)
ax1.add_feature(ctp.feature.BORDERS, linestyle='-', alpha=1)
ax1.coastlines(resolution='10m')
ax1.add_feature(ctp.feature.OCEAN, zorder=100, edgecolor='k')
ax1.coastlines()
ax1.gridlines(draw_labels=True)
ax2 = plt.axes(projection=ccrs.PlateCarree())
ax2.set_title('Under SSP585 Scenario')
cf2 = ax2.contourf(lon_hist, lat_hist, mrsos_change85, cf1.levels, transform=ccrs.PlateCarree(), cmap='BrBG')
fig.colorbar(cf2, ax=ax2)
ax2.set_aspect('equal')
ax2.add_feature(ctp.feature.BORDERS, linestyle='-', alpha=1)
ax2.coastlines(resolution='10m')
ax2.add_feature(ctp.feature.OCEAN, zorder=100, edgecolor='k')
ax2.coastlines()
ax2.gridlines(draw_labels=True)
plt.show()
But this only produces one the second of the two cartopy maps that I am trying to produce.
Unfortunately, I am unable to provide the data I am using as it is very large data files. But can anyone see mistakes in the code that stops it from producing the first one?
You create ax1 and ax2 then destroy/redefine both ax1 and ax2 in your code. The solution is creating them correctly in the first place, and do not redefine them.
# modified code (relevant parts only)
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import cartopy
# Use subplot_kw to declare the projection
fig, (ax1, ax2) = plt.subplots(1,2, figsize=(24, 7), subplot_kw={"projection": ccrs.PlateCarree()})
fig.suptitle('Comparison of 1970-2000 and 2070-2100 Soil Moisture Content')
#ax1 = plt.axes(projection=ccrs.PlateCarree()) #this destroys/redefines ax1
ax1.set_title('Under SSP245 Scenario')
#cf1 = ax1.contourf(lon_hist, lat_hist, mrsos_change45, transform=ccrs.PlateCarree(), cmap='BrBG')
#fig.colorbar(cf1, ax=ax1)
ax1.set_aspect('equal', adjustable=None)
ax1.add_feature(cartopy.feature.BORDERS, linestyle='-', alpha=1)
#ax1.coastlines(resolution='10m')
ax1.add_feature(cartopy.feature.OCEAN, zorder=100, edgecolor='k')
ax1.coastlines()
ax1.gridlines(draw_labels=True)
#ax2 = plt.axes(projection=ccrs.PlateCarree()) #this destroys/redefines ax2
ax2.set_title('Under SSP585 Scenario', fontsize=14, va='bottom', backgroundcolor="white")
#cf2 = ax2.contourf(lon_hist, lat_hist, mrsos_change85, cf1.levels, transform=ccrs.PlateCarree(), cmap='BrBG')
#fig.colorbar(cf2, ax=ax2)
#ax2.set_aspect('equal')
ax2.add_feature(cartopy.feature.BORDERS, linestyle='-', alpha=1)
#ax2.coastlines(resolution='10m')
ax2.add_feature(cartopy.feature.OCEAN, zorder=100, edgecolor='k')
ax2.coastlines()
ax2.gridlines(draw_labels=True)
plt.show()

Matplotlib legend does not match seaborn bubble plot sizes

I have created a bubble plot using seaborn, and used matplotlib to draw the legend to the right of my seaborn plots. I specified the sizing of the bubbles in my seaborn code using sizes=(1,900) but the scaling on my matplotlib legend does not reflect what the plots show. The legend reads from 0 to 45 but the actual data in my plots range from 0 to 900
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(11,4))
sns.scatterplot(y="Min", x="Max",
size="Count", sizes=(1,900), alpha=0.5,
color='r', data=code1, ax=ax1, legend=False)
sns.scatterplot(y="Min", x="Max", alpha=0.5,
color='b', size="Count", sizes=(1,900),
data=code2, ax=ax2, legend=False)
sns.scatterplot(y="Min", x="Max", alpha=0.5,
color='g', size="Count", sizes=(1,900),
data=code3, ax=ax3)
ax3.legend(loc='upper right', bbox_to_anchor=(1.7,1), labelspacing=2,
fontsize=14, frameon=False, markerscale=1)
Here is my plot
I was unable to figure out how seaborn structures the legend output for ingestion by matplotlib. I did learn that my data (code1, code2, and code3) had different min and max values which should have been specified under seaborn's sizes argument. For code1, sizes=(1,900); for code2, sizes=(1,300); for code3, sizes=(1,45). Because I was using matplotlib to draw the legend to the right of code3's plot, the scaling was specific to the rightmost plot rather than for all 3 plots. In the end, I ended up using matplotlib's legend_elements as follows:
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(12,4))
scatter = ax1.scatter(y=code1["Min"], x=code1["Max"],
s=code1["Count"],
color='r', alpha=0.5)
ax2.scatter(y=code2["Min"], x=code2["Max"],
color='b', s=code2["Count"], alpha=0.5)
ax3.scatter(y=code3["Min"], x=code3["Max"],
color='g', s=code3["Count"], alpha=0.5)
kw = dict(prop="sizes", num=[10,100,500,900])
legend = ax3.legend(*scatter.legend_elements(**kw), title="Count", fontsize=12,
loc='upper right', bbox_to_anchor=(1.5,1), labelspacing=2,
frameon=False)

How to Return a MatPlotLib Figure with its corresponding Legend

I'm trying to define a function which returns a pre-styled figure with certain grid, style, width and other properties. However, when I return the fig and its axes, the legend is missing. Here's a simplified example:
def getfig():
plt.style.use('default')
fig, axs = plt.subplots(1, 1, figsize=(1,1), sharey=False)
if issubclass(type(axs),mpl.axes.SubplotBase):
axs=[axs]
for ax in axs:
ax.grid(color='grey', axis='both', linestyle='-.', linewidth=0.4)
ax.legend(loc=9, bbox_to_anchor=(0.5, -0.3), ncol=2)
return fig,axs
fig,axs=getfig()
axs[0].plot(range(10), label="label")
What am I missing?
Thanks!
UPDATE:
This is what I'm using so far but I think there really should be a way to force all future legends associated to a figure to have a certain style.
def fig_new(rows=1,columns=1,figsize=(1,1)):
plt.style.use('default')
fig, axs = plt.subplots(rows,columns, figsize=figsize, sharey=False)
if issubclass(type(axs),mpl.axes.SubplotBase):
axs=[axs]
for ax in axs:
ax.grid(color='grey', axis='both', linestyle='-.', linewidth=0.4)
return fig,axs
def fig_leg(fig):
for ax in fig.get_axes():
ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.3), ncol=5)
fig,axs=fig_new()
axs[0].plot(range(10), label="label")
fig_leg(fig)
You need to call the legend after an artist with a label is plotted to the axes.
An option is to let the function return the arguments to use for the legend afterwards.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
def getfig():
plt.style.use('default')
fig, axs = plt.subplots(1, 1, figsize=(1,1), sharey=False)
if issubclass(type(axs),mpl.axes.SubplotBase):
axs=np.array([axs])
legendkw = []
for ax in axs:
ax.grid(color='grey', axis='both', linestyle='-.', linewidth=0.4)
legendkw.append(dict(loc=9, bbox_to_anchor=(0.5, -0.3), ncol=2))
return fig,axs,legendkw
fig,axs,kw=getfig()
axs[0].plot(range(10), label="label")
for i,ax in enumerate(axs.flat):
ax.legend(**kw[i])
plt.show()

Python: Suplots with secondary-axis

I wrote the following code below to do the following graph:
fig, ax = plt.subplots(figsize=(8, 6))
ax.patch.set_facecolor('white')
ax.plot(df.index, df.X1.values, 'b',
label='NMA', linewidth=1.5)
ax.set_ylabel('Index')
ax2 = ax.twinx()
ax2.plot(df.index, df.Y.values, 'r--',
label='Rate', linewidth=1.5)
ax2.set_ylabel('Rate')
lines = ax.get_lines() + ax2.get_lines()
lgd = ax.legend(lines, [line.get_label() for line in lines],
loc='lower center', ncol=2, bbox_to_anchor=(0.5, -0.15),
frameon=False)
ax.set_title('Economic Rate and Index',
weight='bold')
for i in range(5):
plt.axvspan(Dates['Peak'][i], Dates['Trough'][i],
facecolor='grey', alpha=0.5)
plt.grid(False)
plt.savefig('C:\\test.pdf',
bbox_extra_artists=(lgd,), bbox_inches='tight')
I am having a hard time to reproduce this figure in a subplot (2X2). The only thing I would change in each of the subplots is the blue line (X1 in df... for X2, X3...). How can I have a 2X2 subplot of the above graph? Of Course I would only keep one legend at the bottom of the subplots. Thanks for the help.
The data is here and the "Dates" to reproduce the gray bars here.
This is how you could create a 2x2 raster with twinx each:
import matplotlib.pyplot as plt
fig, ((ax1a, ax2a), (ax3a, ax4a)) = plt.subplots(2, 2)
ax1b = ax1a.twinx()
ax2b = ax2a.twinx()
ax3b = ax3a.twinx()
ax4b = ax4a.twinx()
ax1a.set_ylabel('ax1a')
ax2a.set_ylabel('ax2a')
ax3a.set_ylabel('ax3a')
ax4a.set_ylabel('ax4a')
ax1b.set_ylabel('ax1b')
ax2b.set_ylabel('ax2b')
ax3b.set_ylabel('ax3b')
ax4b.set_ylabel('ax4b')
plt.tight_layout()
plt.show()
Result:

Categories