White background when setting the plot background - matplotlib

The following creates a plot with a white background thereby ignoring set_facecolor.
import matplotlib.pyplot as plt
from descartes.patch import PolygonPatch
import cartopy.crs as ccrs
fig = plt.figure()
ax = fig.add_subplot(111, projection=ccrs.Mercator())
ax.set_facecolor((198/255, 236/255, 253/255))
plt.show()
If I remove where I set the projection, then the color is as expected. How can I set the background color?
I am plotting my own map using shapely polygons using ax.plot. I wish to set the color of the water by setting the background color since my polygons have holes for representing lakes.

Cartopy's projections create various new properties, including two extra patches, the background and outline patches.
It is likely that the background is the one you want to change, but without further example steps this is not certain. Here is how to set each one:
fig = plt.figure();
ax1 = fig.add_subplot(121, projection=ccrs.Mercator())
ax2 = fig.add_subplot(122, projection=ccrs.Mercator())
ax1.background_patch.set_facecolor((198/255, 236/255, 253/255))
ax2.outline_patch.set_facecolor((198/255., 236/255., 253/255.))
plt.show()
Also take care with your color commands -- the example you gave used integer divide, which results in (0,0,0) = black. On the 2nd suplot you see the color you presumably wanted.
For completeness, note that the regular axis patch is turned off, so changes to that patch will not be seen.

Related

Colorbar frame and color not aligned

I have a vexing issue with a colorbar and even after vigorous research I cannot find the question even being asked. I have a plot where I overlay a contour and a pcolormesh and I would like a colorbar to indicate values. That works fine except for one thing:
The colorbar frame and color are offset
The colorbar frame and the actual bar are offset such that below you have a white bit in the frame and on top the color is poking out. While the frame is aligned with the axis as desired, the colorbar is offset.
Here is a working example that emulates the situation I was in, i.e. multiple plots with insets.
import matplotlib.gridspec as gridspec
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
figheight = 4.2 - (2.1 - 49.519 / 25.4)
matplotlib.rcParams['figure.figsize'] = (5.25, figheight)
matplotlib.rcParams['axes.linewidth'] = 0.5
fig = plt.figure()
grid = gridspec.GridSpec(2, 1, height_ratios=[49.519 / 25.4 / figheight, 2.1 / figheight])
ax0 = plt.subplot(grid[0, 0])
ax1 = plt.subplot(grid[1, 0])
plt.tight_layout()
###############################################################################################
#
# Define position of inset
#
###############################################################################################
ax1.axis('off')
pos1 = ax1.get_position()
pos2 = matplotlib.transforms.Bbox([[pos1.x0, pos1.y0],
[.8*pos1.x1,
0.8*pos1.height + pos1.y0]])
left, bottom, width, height = [pos2.x0, pos2.y0, pos2.width, pos2.height]
ax2 = fig.add_axes([left, bottom, width, height])
###############################################################################################
#
# ax2 (inset) plot
#
###############################################################################################
pos2 = ax2.get_position()
ax2.axis('on')
x = np.linspace(0,5)
z = (np.outer(np.sin(x), np.cos(x))+1)*0.5
im = ax2.pcolormesh(z)
c = ax2.contour(z, linewidths=7)
ax2pos = ax2.get_position()
cbar_axis = fig.add_axes([ax2pos.x1+0.05,ax2pos.y0, .02, ax2pos.height])
colorbar = fig.colorbar(im, ax = ax2,
cax = cbar_axis, ticks = [0.1, .5, .9])
colorbar.outline.set_visible(True)
plot = 'Minimal.pdf'
fig.savefig(plot)
plt.close()
The problem persists in both the inline display and the saved .pdf if 'Inline' graphics backend is chosen. Using tight layout or not changes how badly the offset is depending on the size of the bar - same with using PyQT5 rather than inline graphics backend. I thought it was gone when I was changing between the various combinations, but I just realized it's still there.
I would appreciate any input.
As suggested by ImportanceOfBeingErnest I have tried using np.round on the figsize and that didn't change things. While you can fiddle around with sizes to make it look okay, it always stands over on one or the other side by some amount. When I change the graphics backend on Spyder 3 from 'Inline' to 'QT5' the problem becomes less severe with or without rounding. A summary of this is in this picture Colorbar overlap cases. Note that with not rounded and PyQT5 the problem still occurs, but is not as severe.
On inspection, it is clear that the colorbar is not only bleeding out over the top of its axes, but it's also positioned slightly to the left.
So, the problem here appears to be a conflict between the position of the colorbar axis and the colorbar itself when rasterization occurs. You can find more details on this issue in matplotlib's github repository, but I'll summarize what's going on here.
Colorbars are rasterized when the output is produced, so as to avoid artifacting issues during rendering. The position of the colorbar is snapped to the nearest integer pixels during the rasterization process, while the axis is kept where it is supposed to be. Then, when the output is produced, the colorbar falls within borders of fixed pixels of the image, despite the fact that the image is, itself, vectorized. Thus, there are two strategies that can be employed to avoid this mishap.
Use a finer DPI
The conversion from vectorized coordinates to rasterized coordinates takes place assuming a given DPI on the image. By default, this is set to be 72. However, by using more DPI, the overall shift induced by the rasterization process will be smaller, as the closest pixel the colorbar will snap to will be much nearer. Here, we change the output to have fig.savefig(plot,dpi=4000), and the problem goes away:
Note, however, that on my machine, the output size changed from 62 KB to 78 KB due to this change (although the DPI adjustment was also, admittedly, extreme). If you are worried about file sizes, you should pick a lower DPI that fixes the problem.
Use a different colormap
This rasterization happens when more than 50 colors are in the colorbar. Thus, we can do a quick test, setting our colormap to Pastel1 via
im = ax2.pcolormesh(z,cmap='Pastel1'). Here, the colorbar / axis mismatch is mitigated.
As a fallback, adopting a colorbar with fewer than 50 colors should mitigate this problem.
Rasterize the Axis
For completeness, there is also a third option. If you rasterize the colorbar axis, both the axis boundaries and the colormap will be rasterized, and you'll lose the offset. This will also rasterize your labels, and the axis will shift as one, breaking alignment with the nearby axis. For this, you just need to include cbar_axis.set_rasterized(True).
First, a way to overlay a contour and a pcolormesh and create a colorbar would be the following
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np
x = np.linspace(0,5)
z = (np.outer(np.sin(x), np.cos(x))+1)*0.5
fig = plt.figure(figsize=(4, 4))
ax = fig.add_subplot(111)
im = ax.pcolormesh(z)
c = ax.contour(z, linewidths=7)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", "5%", pad="3%")
colorbar = fig.colorbar(im, cax=cax, ticks = [0.1, .5, .9])
plt.show()
Now to the problem from the question. It is of course possible to create the axes to put the colorbar in manually. Replacing the colorbar creation with the code from the question still produces a nice image.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0,5)
z = (np.outer(np.sin(x), np.cos(x))+1)*0.5
fig = plt.figure(figsize=(4, 4))
ax = fig.add_subplot(111)
plt.subplots_adjust(right=0.8)
im = ax.pcolormesh(z)
c = ax.contour(z, linewidths=7)
ax2pos = ax.get_position()
cbar_axis = fig.add_axes([ax2pos.x1+0.05,ax2pos.y0, .05, ax2pos.height])
colorbar = fig.colorbar(im, ax = ax,
cax = cbar_axis, ticks = [0.1, .5, .9])
colorbar.outline.set_visible(True)
plt.show()
Conclusion so far: The issue is not reproducible, at least not without a Minimal, Complete, and Verifiable example.
I'm uncertain about the reasons for the behaviour in the example from the question. However, it seems that it can be overcome by rounding the figure size to 3 significant digits
matplotlib.rcParams['figure.figsize'] = (5.25, np.round(figheight,3))

Use different math font for different subplots in the same figure?

I am trying to use different math font sets for two axes in the same figure, with no success. I have searched this issue using google and I have read the matplotlib's official guide on how to use the math font. But I can not find ways to achieve this effect. My complete code is as follows:
import matplotlib.pyplot as plt
import matplotlib as mpl
fig, (ax1, ax2) = plt.subplots(ncols=2)
mpl.rcParams['mathtext.fontset'] = 'cm' # use font "cm" for first axes
ax1.text(0.3, 0.5, r"$xyz$", fontsize=50)
ax1.set_title('before')
ax1.axis('off')
ax1.set_aspect('equal')
mpl.rcParams['mathtext.fontset'] = 'stixsans' # use font "stixsans" for second axes
ax2.text(0.3, 0.5, r"$xyz$", fontsize=50)
ax2.set_title('after')
ax2.axis('off')
ax2.set_aspect('equal')
plt.show()
The resulting figure shows that both the axes use the "stixsans" font, see picture here.
It seems that mpl.rcParams['mathtext.fontset'] = 'stixsans' in the later part has overruled the previous setting mpl.rcParams['mathtext.fontset'] = 'cm'. Any idea how to prevent this from happening and use "cm" and "stixsans" font for the two axes respectively?

Coloring Intersection of Circles/Patches in Matplotlib

The following code:
# in ipython notebook, enable inline plotting with:
# %pylab inline --no-import-all
import matplotlib.pyplot as plt
# create some circles
circle1 = plt.Circle((-.5,0), 1, color='r', alpha=.2)
circle2 = plt.Circle(( .5,0), 1, color='b', alpha=.2)
# add them to the plot (bad form to use ;, but saving space)
# and control the display a bit
ax = plt.gca()
ax.add_artist(circle1); ax.add_artist(circle2)
ax.set_xlim(-2, 2); ax.set_ylim(-2, 2)
ax.set_aspect('equal')
# display it
plt.plot()
Produces the following plot:
I would like to specify the colors of the four regions (1) the background (currently white), (2 and 3) each individual event (the non-overlapping areas, currently blue and red), and (4) the intersection event (currently blended to purple). For example, I might color them red, green, blue, yellow -or- I might give them four different, precisely specified grayscale values (the later is more likely). [The colors will be generated based on characteristics of the underlying data.]
I specifically do not want to use alpha blending to "infer" a color in the intersection. I need to explicitly control the colors of all four regions.
I can think of a few strategies to solve this:
Ask mpl to extract the "primitive" patch objects that make up the three distinctly colored graphical regions (and do something similar to operate on the background) and then color them.
Given the circles, manually compute their intersections and color that intersection (somehow). Going point by point seems ugly.
Thanks!
I'm not 100% sure but I think matplotlib does not have the functionality to intersect polygons. But you could use shapely:
import shapely.geometry as sg
import matplotlib.pyplot as plt
import descartes
# create the circles with shapely
a = sg.Point(-.5,0).buffer(1.)
b = sg.Point(0.5,0).buffer(1.)
# compute the 3 parts
left = a.difference(b)
right = b.difference(a)
middle = a.intersection(b)
# use descartes to create the matplotlib patches
ax = plt.gca()
ax.add_patch(descartes.PolygonPatch(left, fc='b', ec='k', alpha=0.2))
ax.add_patch(descartes.PolygonPatch(right, fc='r', ec='k', alpha=0.2))
ax.add_patch(descartes.PolygonPatch(middle, fc='g', ec='k', alpha=0.2))
# control display
ax.set_xlim(-2, 2); ax.set_ylim(-2, 2)
ax.set_aspect('equal')
plt.show()

Fit graph to colorbar in matplotlib

I have an imshow graph that shows a colobar for numerical values. The colorbar is much bigger than the graph. Is there a way to scale them so they end up the same size, preferably without affecting the aspect ratio of the graph?
grid = np.ma.array(grid, mask=np.isnan(grid))
plot.imshow(grid, interpolation='nearest', aspect='equal', vmax = private.vmax, vmin = private.vmin)
plot.minorticks_off()
plot.set_xticks(range(len(placex)))
plot.set_yticks(range(len(placey)))
plot.set_xticklabels(placex)
plot.set_yticklabels(placey, rotation = 0)
plot.colorbar()
plot.show()
You can specify an axes-object by any of the built-in methods of matplotlib and then use it for your colorbar, e.g.:
import matplotlib.pyplot as plt
import numpy as np
ax2 = plt.subplot2grid((1,6), (0, 5), colspan=1)
ax1 = plt.subplot2grid((1,6), (0, 0), colspan=5)
plt.imshow(np.random.random((10,10)))
plt.colorbar(cax=ax2)
plt.show()
This will result in something like:
Though, this doesn't help if your imshow-axes becomes very flat (due to aspect="equal", this might happen).
If you want to handle such cases, you can either
Adjust the figure size to the aspect of your grid, e.g.
fig = figure(figsize=grid.shape[1]*1.5/dpi, grid.shape[0]/dpi)
Read the coordinates of ax1 AFTER plotting, create ax2 just after that with the appropriately transformed coordinates and then use ax2 for the colorbar. This doesn't behave nicely when you resize the window, but might work out if you just create plots as image files automatically.
If 2. is what you need, I can add an example for this, but I will only do this work if you are sure this is what you want.
Greetings,
Thorsten

How can I set the background color on specific areas of a pyplot figure?

I've managed to plot a series of points with the following code:
plt = pp.figure()
for i in range(spt.shape[1]):
spktrain = spt[0,i]
for trial in spktrain:
non_z = np.nonzero(trial)
non_z = non_z[0]
pp.plot(t[non_z], trial[non_z], 'bo')
I would like to place alternating bands of white and gray background on the figure in order to separate the data from each iteration of the outer for loop. In other words, I would like the data from each "spktrain" to have it's own background color (the data does not overlap).
How can I go about changing the background color of a figure in a specific region?
You can use axhspan and/or axvspan like this:
import matplotlib.pyplot as plt
plt.figure()
plt.xlim(0, 5)
plt.ylim(0, 5)
for i in range(0, 5):
plt.axhspan(i, i+.2, facecolor='0.2', alpha=0.5)
plt.axvspan(i, i+.5, facecolor='b', alpha=0.5)
plt.show()