I need to combine imshow
and regular line plots to create a figure. Unfortunately, the imshow
plot area is smaller than the regular line plots, leading to something that looks like this:
fig = plt.figure(figsize=(25, 9))
map_subplots = [
plt.subplot(2, 5, 1, projection=ccrs.PlateCarree()),
plt.subplot(2, 5, 3, projection=ccrs.PlateCarree()),
plt.subplot(2, 5, 6, projection=ccrs.PlateCarree()),
plt.subplot(2, 5, 8, projection=ccrs.PlateCarree())
]
for i, ax in enumerate(map_subplots):
ax.set_title(titles[i])
ax.coastlines(linewidth=0.5)
ax.imshow(eofs[i], extent=[0, 360, -90, 90], transform=ccrs.PlateCarree(), cmap='RdBu_r', origin='lower', vmin=-0.01, vmax=0.01)
ts_subplots = [
plt.subplot(2, 5, 2),
plt.subplot(2, 5, 4),
plt.subplot(2, 5, 7),
plt.subplot(2, 5, 9)
]
for i, ax in enumerate(ts_subplots):
ax.plot(pcs.time, pcs.sel(pc=i))
ax.set_ylim(-100, 100)
if True:
# remove yticks
ax.set_yticks([])
ax.set_xticks(pd.date_range("2013-01-01", "2016-01-01", freq='6MS'))
ax.set_xticklabels(["2013", "Jul", "2014", "Jul", "2015", "Jul", "2016"])
I've got a hacky workaround with gridspec
, that spreads the imshow
plots over multiple subplots, so that it forces them to be bigger:
# Create a figure
fig = plt.figure(figsize=(15, 9))
# Define a gridspec with custom widths and heights for each subplot
hr = 0.6
wr = 0.7
gs = gridspec.GridSpec(6, 5, width_ratios=[1, wr, 1, wr, 1], height_ratios=[hr, 1, hr, hr, 1, hr])
# Map subplots
map_subplots = [
plt.subplot(gs[:3, 0], projection=ccrs.PlateCarree()),
plt.subplot(gs[:3, 2], projection=ccrs.PlateCarree()),
plt.subplot(gs[3:, 0], projection=ccrs.PlateCarree()),
plt.subplot(gs[3:, 2], projection=ccrs.PlateCarree())
]
# Plot the map subplots
for i, ax in enumerate(map_subplots):
ax.set_title(titles[i])
ax.coastlines(linewidth=0.5)
ax.imshow(eofs[i], extent=[0, 360, -90, 90], transform=ccrs.PlateCarree(), cmap='RdBu_r', origin='lower', vmin=-0.01, vmax=0.01)
# Time series subplots
ts_subplots = [
plt.subplot(gs[1, 1]),
plt.subplot(gs[1, 3]),
plt.subplot(gs[4, 1]),
plt.subplot(gs[4, 3])
]
# Plot the time series subplots
for i, ax in enumerate(ts_subplots):
ax.plot(pcs.time, pcs.sel(pc=i))
ax.set_ylim(-100, 100)
if True:
# remove yticks
ax.set_yticks([])
ax.set_xticks(pd.date_range("2013-01-01", "2016-01-01", freq='6MS'))
ax.set_xticklabels(["2013", "Jul", "2014", "Jul", "2015", "Jul", "2016"])
# fig.text(0.04, 0.5, 'PC value (arb. units)', va='center', rotation='vertical', fontsize=12)
plt.subplots_adjust(wspace=0.1, hspace=0)
plt.tight_layout()
plt.show()
This is very close to working, but there's a huge gap between the two rows, which I can't get rid of, no matter how negative a subplots_adjust
parameter I supply. Any ideas on how to fix this?
This is what compressed layout is meant to handle (https://matplotlib.org/stable/users/explain/axes/constrainedlayout_guide.html#grids-of-fixed-aspect-ratio-axes-compressed-layout)
Note that there is still blank space, but it is above and below the plots instead of between them. One fix for that is to either adjust the size of the figure manually, or to use bbox_inches='tight' when you save the figure.
import matplotlib.pyplot as plt
import numpy as np
fig, axs = plt.subplots(2, 2, layout='compressed')
axs[0, 0].imshow(np.random.rand(100, 400))
axs[0, 1].plot(np.random.randn(500))
axs[1, 0].imshow(np.random.rand(100, 400))
axs[1, 1].plot(np.random.randn(500))
plt.show()