When I try to plot figures with limited amounts of dead-space on the top, sides, and bottom I use either tight_layout or constrained_layout. However, it seems like ImageGrid doesn't support this. Take a look at the following example:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
# Data to populate plots.
im = np.arange(100).reshape((10, 10))
fig = plt.figure(figsize=(4, 6), constrained_layout=True)
grid = ImageGrid(fig, 111,
nrows_ncols=(5, 2),
axes_pad=0.2,
label_mode="L")
for ax in grid:
ax.imshow(im)
fig.suptitle("Testing a suptitle on with ImageGrid")
fig.savefig("imagegrid_suptitle.png")
I get the following warning:
UserWarning: There are no gridspecs with layoutgrids. Possibly did not call parent GridSpec with the "figure" keyword
fig.savefig("imagegrid_suptitle.png")
And this is the resulting plot: ImageGrid with constrained_layout=True
As can be seen, there's a lot of dead-space at the top, sides, and bottom, and the suptitle is located far above the top two grids. Is this a bug in constrained_layout, or is it simply not possible to support its use with ImageGrid?
Please note that I have tried to use tight_layout, but that produces a warning about the lack of support for ImageGrid:
UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.
fig.tight_layout()
It also results in the suptitle overlapping with the two top grids.
Related
In an old standalone plotting package (sm) there was a style available for scatter plots which I found more appealing to the general style. It appears as each point looking almost like a histogram which stretches to the next point.
An example of a scatter plot using this style:
Matplotlib does have this style for histograms, but I'm wondering if there's a way to cheat the system to allow the style to work for scatter plots.
I think some of the confusion comes from the fact that the desired plot is not a scatter plot. It's a line plot with lines in form of a step-like function.
You may plot step functions with pyplot.step(x,y) or plot(x,y, drawstyle="steps").
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(42)
x = np.linspace(0,1)
y = np.random.rand(len(x))
fig, ax = plt.subplots()
ax.step(x,y)
# or
# ax.plot(x,y, drawstyle="steps")
plt.show()
I'm using this answer to set up 3x3 grid on which to plot my data. I chose this method specifically because it works with tight_layout().
However, I'm reading the docs on the AxesGrid toolkit and I can't figure out how to put colorbars only on the rightmost plots.
So far this is what I have:
from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import ImageGrid
letters='abcdefghi'
f1=plt.figure(figsize=(9,9))
grid = ImageGrid(f1, 111,
nrows_ncols=(3,3),
axes_pad=0.05,
share_all=True,
cbar_location="right",
cbar_mode="each",
cbar_size="2%",
cbar_pad=0.15)
A=np.random.rand(10,10)
for i,axis in enumerate(grid):
im=axis.imshow(A)
axis.annotate(s=letters[i], xy=(0.1, .85), xycoords='axes fraction', bbox=dict(boxstyle="square", fc="w", alpha=0.9))
if i in (2,5,8):
axis.cax.colorbar(im)
f1.tight_layout()
f1.savefig('example.png')
Which produces this figure:
Which is obviously not right, since every subplot has its own colorbar, even though it's not colored. I'm looking to have only c, f and i with colorbars that should be different. Is that possible?
You have to modify your ImageGrid. You set cbar_mode to each therefore all images have own colorbar set it to edge and set the direction to row (one colorbar for one row of images):
grid = ImageGrid(f1, 111,
nrows_ncols=(3,3),
axes_pad=0.05,
share_all=True,
cbar_location="right",
cbar_mode='edge',
direction = 'row',
cbar_size="2%",
cbar_pad=0.15)
To show colorbar with all labels I a little bit expand your figure f1=plt.figure(figsize=(9.5,9))
There is an example for edge colorbars in matplotlib tutorial: https://matplotlib.org/examples/axes_grid/demo_edge_colorbar.html
I am producing a map of the world with pie charts in individual model grid boxes. I make the map and coastlines using cartopy. The pie charts I produce using inset_axes. Unfortunately the pie charts hide the coastlines and I'd like to see them clearly.
Minimum working example:
import cartopy.crs as ccrs
import numpy as np
import cartopy.feature as feature
import matplotlib.pyplot as plt
def plot_pie_inset(dataframe_pie,ilat_pie,ilon_pie,axis_main,width_local,alpha_local):
ax_sub= inset_axes(axis_main, width=width_local, height=width_local, loc=3, bbox_to_anchor=(ilat_pie, ilon_pie),bbox_transform=axis_main.figure.transFigure, borderpad=0.0)
wedges,texts= ax_sub.pie(dataframe_pie,colors=colors_dual)
for w in wedges:
w.set_linewidth(0.02)
w.set_alpha(alpha_local)
w.set_zorder(1)
plt.axis('equal')
colors_dual=['RosyBrown','LightBlue']
lat_list= np.arange(0.2,0.7,0.05)
fig= plt.figure()
ax_main= plt.subplot(1,1,1,projection=ccrs.PlateCarree())
ax_main.coastlines(zorder=3)
for ilat in np.arange(len(lat_list)):
plot_pie_inset([75,25],lat_list[ilat],0.72,ax_main,0.2,0.9)
plt.show()
I can see the coastlines by making the pie charts partially transparent by reducing the alpha value. However, this makes the colors somewhat muted. My aim is to have the coastlines as the topmost layer.
I have attempted to use 'zorder' to force the coastlines to the top layer. However, 'zorder' cannot be passed to inset_axes, nor to ax.pie so I've made the patches of color in pie charts translucent. This fails because the ax_main.coastlines does not have its own 'zorder'. The coastline zorder seems to be tied to that of ax_main. There is no benefit in increasing the zorder of ax_main.
Any suggestions greatly welcomed.
The problem is that each axes either lies on top or below another axes. So changing the zorder of artists within axes, does not help here. In principle, one could set the zorder of the axes themselves, putting the inset axes behind the main axes.
ax_sub.set_zorder(axis_main.get_zorder()-1)
Cartopy's GeoAxes uses its own background patch. This would then need to be set to invisble.
ax_main.background_patch.set_visible(False)
Complete example:
import cartopy.crs as ccrs
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
def plot_pie_inset(dataframe_pie,ilat_pie,ilon_pie,axis_main,width_local,alpha_local):
ax_sub= inset_axes(axis_main, width=width_local, height=width_local, loc=3,
bbox_to_anchor=(ilat_pie, ilon_pie),
bbox_transform=axis_main.transAxes,
borderpad=0.0)
wedges,texts= ax_sub.pie(dataframe_pie,colors=colors_dual)
for w in wedges:
w.set_linewidth(0.02)
w.set_alpha(alpha_local)
w.set_zorder(1)
plt.axis('equal')
# Put insets behind main axes
ax_sub.set_zorder(axis_main.get_zorder()-1)
colors_dual=['RosyBrown','LightBlue']
lat_list= np.arange(0.2,0.7,0.05)
fig= plt.figure()
ax_main= plt.subplot(1,1,1,projection=ccrs.PlateCarree())
ax_main.coastlines()
# set background patch invisible, such that axes becomes transparent
# since the GeoAxes from cartopy uses a different patch as background
# the following does not work
# ax_main.patch.set_visible(False)
# so we need to set the GeoAxes' background_patch invisible
ax_main.background_patch.set_visible(False)
for ilat in np.arange(len(lat_list)):
plot_pie_inset([75,25],lat_list[ilat],0.72,ax_main,0.2,0.9)
plt.show()
An alternative solution suggest by a colleague neglects to use the inset_axes but achieves a similar result. The main difference is that the coordinate system in this solution is in the original latitude/longitude coordinates rather than figure coordinates.
def plot_pie_direct(dataframe_pie,ilat_pie,ilon_pie,axis_main,width_local,alpha_local):
wedges,texts= ax_main.pie(dataframe_pie,colors=colors_aer_atm,radius=width_local)
for w in wedges:
w.set_linewidth(0.02) ## Reduce linewidth to near-zero
w.set_center((ilat_pie,ilon_pie))
w.set_zorder(0)
fig= plt.figure()
ax_main= plt.axes(projection=ccrs.PlateCarree())
ax_main.coastlines(zorder=3)
ax_main.set_global()
lim_x= ax_main.get_xlim()
lim_y= ax_main.get_ylim()
for ilat in np.arange(len(lat_list_trim)):
plot_pie_direct(frac_aer_atm_reshape_trim[:,ilat,ilon],x_val_pies[ilon],y_val_pies[ilat],ax_main,lat_list_diff_trim,0.9)
ax_main.coastlines(zorder=3)
ax_main.set_xlim(lim_x)
ax_main.set_ylim(lim_y)
plt.show()
So I'm trying to draw two subplots in the same figure that share the x axis. However, I cannot get it to draw the last minor xtick. I have no idea from where this behaviour comes, but I managed to reproduce it with random data.
The system used is python2.7 and matplotlib v1.2.1
So here goes my minimal error-reproducing example:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
from matplotlib.ticker import MaxNLocator
xdat = np.linspace(0,6.6,endpoint=True)
ydat1 = np.random.rand(50)*500
ydat2 = np.random.rand(50)*4
fig = plt.figure(figsize=(6,8), dpi=72)
gs = gridspec.GridSpec(2,1, height_ratios=[3,1])
fig.subplots_adjust(hspace=0.0)
ax1 = plt.subplot(gs[0])
ax1.plot(xdat, ydat1)
ax1.set_xlim(0,6.6)
ax1.set_xticks(range(0,8,1))
ax1.minorticks_on()
[label.set_visible(False) for label in ax1.get_xticklabels() ] # Make tick labels invisible
ax2 = plt.subplot(gs[1], sharex=ax1)
ax2.plot(xdat, ydat2, 'r-')
ax2.yaxis.set_major_locator(MaxNLocator(nbins=5, steps=[1,2,4,5,10], symmetric=False, prune='upper'))
plt.show()
I got the following image:
I have no idea whether I found a bug or if there is an easy way to alleviate this problem (i.e. update matplotlib).
Haven't been able to find where the bug comes from yet, but version 1.3.1 has the same behavior.
A work around would be to set the minor ticks manually, by adding a ax2.xaxis.set_ticks(np.hstack((ax2.xaxis.get_ticklocs(minor=True), 6.4)), minor=True), where 6.4 is the last minor tick.
Or you can force the xlim to be slightly larger than the default and the last tick will come out. ax2.set_xlim((0,6.6)). The default is (0.0, 6.5999999999999996).
I guess it can be considered as a bug.
In the following code snippet:
import numpy as np
import pandas as pd
import pandas.rpy.common as com
import matplotlib.pyplot as plt
mtcars = com.load_data("mtcars")
df = mtcars.groupby(["cyl"]).apply(lambda x: pd.Series([x["cyl"].count(), np.mean(x["wt"])], index=["n", "wt"])).reset_index()
plt.plot(df["n"], range(len(df["cyl"])), "o")
plt.yticks(range(len(df["cyl"])), df["cyl"])
plt.show()
This code outputs the dot plot graph, but the result looks quite awful, since both the xticks and yticks don't have enough space, that it's quite difficult to notice both 4 and 8 of the cyl variable output its values in the graph.
So how can I plot it with enough space in advance, much like you can do it without any hassles in R/ggplot2?
For your information, both of this code and this doesn't work in my case. Anyone knows the reason? And do I have to bother to creating such subplots in the first place? Is it impossible to automatically adjust the ticks with response to the input values?
I can't quite tell what you're asking...
Are you asking why the ticks aren't automatically positioned or are you asking how to add "padding" around the inside edges of the plot?
If it's the former, it's because you've manually set the tick locations with yticks. This overrides the automatic tick locator.
If it's the latter, use ax.margins(some_percentage) (where some_percentage is between 0 and 1, e.g. 0.05 is 5%) to add "padding" to the data limits before they're autoscaled.
As an example of the latter, by default, the data limits can be autoscaled such that a point can lie on the boundaries of the plot. E.g.:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(range(10), 'ro')
plt.show()
If you want to avoid this, use ax.margins (or equivalently, plt.margins) to specify a percentage of padding to be added to the data limits before autoscaling takes place.
E.g.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(range(10), 'ro')
ax.margins(0.04) # 4% padding, similar to R.
plt.show()