Reputation: 1711
Does anyone know a way to bring a scatter plot to the foreground in matplotlib? I have to display the scatter plotting on top of the contour, but by default it is plotted underneath...
Upvotes: 74
Views: 93050
Reputation: 23011
Everything on a matplotlib figure as zorder
and you can find their default values in the docs. But the truth is, you don't even need to remember the default values because you can set the zorder with any number you want where the Artist with the higher zorder
value will be drawn on top; only the ordinal rankings of the values (not the cardinal values) you assign matter when it comes to showing which plot on top of which.
For example, in the following figure, we assign zorder
value to both scatter
and contourf
calls. As you can see, in both subplots, the plot with the higher zorder is shown on top.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from scipy import stats
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = stats.multivariate_normal([0, 0], [[1, 0], [0, 1]]).pdf(np.dstack((X, Y)))
Z2 = stats.multivariate_normal([1, 1], [[1.5, 0], [0, 0.5]]).pdf(np.dstack((X, Y)))
Z = 10.0 * (Z2 - Z1)
vmax = np.abs(Z).max()
norm = mpl.colors.Normalize(vmin=-vmax, vmax=vmax)
cmap = mpl.colormaps["PRGn"]
levels = np.arange(-2.0, 1.601, 0.4)
x = np.random.uniform(-3,3,10)
y = np.random.uniform(-2,2,10)
fig, axes = plt.subplots(1, 2, sharey=True)
for ax, zord in zip(axes, [-0.5, 0.5]):
ax.scatter(x, y, zorder=zord)
# ^^^^^^^^^^^ <--- zorder here
ax.contourf(X, Y, Z, levels,
norm=norm, cmap=cmap, zorder=zord*2)
# ^^^^^^^^^^^^^ <--- zorder here
ax.autoscale(False)
ax.set_title(f'Scatter with zorder={zord}\ncontour with zorder={zord*2}')
If two Artists have the same zorder
, then the one drawn last will be shown on top. In fact, contour and scatter are both a Collection, so by default, they have the same zorder
. The reason scatter plots were not shown in the OP was scatter plot was plotted before the contour plot. So one solution to OP is to draw the scatter plot after the contour plot (so in sodd's answer, because scatter plot is drawn last, you don't even need to set zorder). You can confirm that with the following (using the same data as above):
plt.contourf(X, Y, Z, levels, norm=norm, cmap=cmap)
plt.scatter(x, y)
set_zorder()
As mentioned above, the Artists of both scatter plot and contour plot are stored in .collections
of the Axes object they are drawn on (and they are stored in it in the order they are drawn). You can confirm that using the following code. Then you can change the zorder
of these objects by calling set_zorder()
on them as well. This is especially useful if the plots are created using some third party library that uses matplotlib in the backend.
The code below produces a figure the same as above.
fig, ax = plt.subplots()
ax.scatter(x, y)
ax.contourf(X, Y, Z, levels, norm=norm, cmap=cmap)
ax.collections # <Axes.ArtistList of 2 collections>
scatter = ax.collections[0] # <matplotlib.collections.PathCollection at 0x223b2519610>
contour = ax.collections[1] # <matplotlib.contour.QuadContourSet at 0x223a69dc390>
# set zorders here
scatter.set_zorder(1)
contour.set_zorder(0.9)
Upvotes: 0
Reputation: 12923
You can manually choose in which order the different plots are to be displayed with the zorder
parameter of e.g. the scatter
method.
To demonstrate, see the code below, where the scatter plot in the left subplot has zorder=1
and in the right subplot it has zorder=-1
. The object with the highest zorder
is placed on top. This means that the scatter will be placed on top of the contour in the first subplot, while it is placed underneath in the second subplot.
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = 10.0 * (Z2 - Z1)
norm = cm.colors.Normalize(vmax=abs(Z).max(), vmin=-abs(Z).max())
cmap = cm.PRGn
levels = np.arange(-2.0, 1.601, 0.4)
fig, axes = plt.subplots(1,2, sharey=True)
for ax, zord in zip(axes, [1, -1]):
ax.contourf(X, Y, Z, levels,
cmap=cm.get_cmap(cmap, len(levels)-1),
norm=norm)
ax.autoscale(False) # To avoid that the scatter changes limits
ax.scatter(np.random.uniform(-3,3,10),
np.random.uniform(-2,2,10),
zorder=zord)
ax.set_title('Scatter with zorder={0}'.format(zord))
Upvotes: 102