Background colour in pyplot - matplotlib

I want to have three distinct regions in the background of my graph. I would also like to shade them so that at the boundaries between these regions I have a white color. I have a workaround for this as follows:
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
fig = plt.figure()
sfig = fig.add_subplot(111)
nrec = 10
for i in range(300, 422):
sfig.axvspan(i, i+1, facecolor='plum', alpha=(1-abs(i-((422.0-300.0)/2.0 + 300.0))/(422.0-300.0)*2.0)*0.6, edgecolor='plum', zorder = 0, lw = 0)
for i in range(423, 577):
sfig.axvspan(i, i+1, facecolor='silver', alpha=(1-abs(i-((577-423)/2 + 423))/(577-423)*2)*0.6, edgecolor='silver', zorder = 0, lw = 0)
for i in range(578, 700):
sfig.axvspan(i, i+1, facecolor='cyan', alpha=(1-abs(i-((700-578)/2 + 578))/(700-578)*2)*0.6, edgecolor='cyan', zorder = 0, lw = 0)
plt.show()
However, when I save this figure in the .pdf format I see some lines, presumably from the boundaries of these vertical rectangles. Is there a better solution for this? If not, is there a way to not have these annoying boundaries between rectangles?

Related

adjust the location of color bar in subplots containing color and line plots

I am new to python programming. I was trying to make two subplots using matplotlib containing a line plot (panel-a) and 2-D color plot using imshow() (panel-b). I want the colorbar to be shown on the right side with same size as the color plot and it should not be within the subplot box limit.
`
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime as dt
from mpl_toolkits.axes_grid1 import make_axes_locatable
# Panel (a)
x1 = np.linspace(2, -2, 5)
y1 = np.linspace(-2, 2, 5)
# Panel (b)
N = 10
arr = np.random.random((N, N))
x_lims = list(map(dt.datetime.fromtimestamp, [982376726, 982377321]))
x_lims = mdates.date2num(x_lims)
y_lims = [0, 40]
fig, ax = plt.subplots(2, 1, figsize=(14, 10))
ax[0].plot(x1, y1)
ax[0].set_ylim(-2, 2)
ax[0].set_xlim(2, -2)
ax[0].set_xticks([2, 1, 0, -1, -2])
ax[0].set_yticks([-2, -1, 0, 1, 2])
im = ax[1].imshow(arr, extent=[x_lims[0], x_lims[1], y_lims[0],
y_lims[1]],
aspect='auto')
divider = make_axes_locatable(ax[1])
cax = divider.append_axes("right", size="5%", pad=0.05)
plt.colorbar(im, cax=cax, label="diff. en. flux")
ax[1].xaxis_date()
date_format = mdates.DateFormatter('%H:%M:%S')
ax[1].xaxis.set_major_formatter(date_format)

define size of individual subplots side by side

I am using subplots side by side
plt.subplot(1, 2, 1)
# plot 1
plt.xlabel('MEM SET')
plt.ylabel('Memory Used')
plt.bar(inst_memory['MEMORY_SET_TYPE'], inst_memory['USED_MB'], alpha = 0.5, color = 'r')
# pol 2
plt.subplot(1, 2, 2)
plt.xlabel('MEM POOL')
plt.ylabel('Memory Used')
plt.bar(set_memory['POOL_TYPE'], set_memory['MEMORY_POOL_USED'], alpha = 0.5, color = 'g')
they have identical size - but is it possible to define the width for each subplot, so the right one could be wider as it has more entries and text would not squeeze or would it be possible to replace the bottom x-text by a number and have a legend with 1:means xx 2:means yyy
I find GridSpec helpful for subplot arrangements, see this demo at matplotlib.
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import pandas as pd
N=24
inst_memory = pd.DataFrame({'MEMORY_SET_TYPE': np.random.randint(0,3,N),
'USED_MB': np.random.randint(0,1000,N)})
set_memory = pd.DataFrame({'MEMORY_POOL_USED': np.random.randint(0,1000,N),
'POOL_TYPE': np.random.randint(0,10,N)})
fig = plt.figure()
gs = GridSpec(1, 2, width_ratios=[1, 2],wspace=0.3)
ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1])
ax1.bar(inst_memory['MEMORY_SET_TYPE'], inst_memory['USED_MB'], alpha = 0.5, color = 'r')
ax2.bar(set_memory['POOL_TYPE'], set_memory['MEMORY_POOL_USED'], alpha = 0.5, color = 'g')
You may need to adjust width_ratios and wspace to get the desired layout.
Also, rotating the text in x-axis might help, some info here.

Change Pyplot axes text color to white

I have a colorbar that I created for a heatmap image of surface temperature on Earth. The problem that I'm having is that the pyplot figure saves with a white background and I have the Earth image on a black background. I set the figure image to be transparent and need to change the text and ticks on the axes to be white.
I've tried everything I've seen on here and searched for hours on the matplotlib site but nothing seems to work for something as simple as changing text color.
a = np.array([[319.785, 198.988]])
plt.figure(figsize=(7, 1))
img = plt.imshow(a)
plt.gca().set_visible(False)
cax = plt.axes([0, .3, 1, 0.5])
cb = plt.colorbar(orientation='horizontal', cax=cax)
plt.savefig("colorbar.png", bbox_inches='tight', transparent=True)
plt.show()
You can set the color of the ticks, the color of the labels and the color of the axes edges all to white using the rcParams as follows.
import matplotlib.pyplot as plt
import numpy as np
params = {"ytick.color" : "w",
"xtick.color" : "w",
"axes.labelcolor" : "w",
"axes.edgecolor" : "w"}
plt.rcParams.update(params)
a = np.array([[319.785, 198.988]])
fig =plt.figure(figsize=(7, 1))
# set facecolor black for testing:
fig.set_facecolor("k")
img = plt.imshow(a)
plt.gca().set_visible(False)
cax = plt.axes([0, .3, 1, 0.5])
cb = plt.colorbar(orientation='horizontal', cax=cax)
plt.show()

Cut parts of plotted artists

Assume I have drawn some background image like this:
fig, ax = plt.subplots()
imdata = np.random.randn(10, 10)
im = ax.imshow(imdata, extent=(0, 1, 0, 1), aspect='auto',
cmap='coolwarm', interpolation='nearest')
Now I'm adding a number of rectangles like:
rect = matplotlib.patches.Rectangle((0.3,0.3),0.4,0.4)
ax.add_artist(rect)
Now I want to cut several other rectangles out of the previously added rectangle, so the underlying image is shown again. By cut, I really mean that specifying such a "deletion rectangle" will cut out parts from the previously drawn rectangles. So if they overlap, only the overlapping parts will be cut away. Where the "deletion rectangles" do not intersect space occupied by the rectangles above, nothing shall happen to the visible area.
How can I achieve that?
You can use a path to construct the rectangles. To position the rectangles the vertices of the path can be translated and transformed. Then, using the fact that inverted vertices will be cut out of a path, one create holes in the outer rectangle.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch, Rectangle
fig, ax = plt.subplots()
imdata = np.random.randn(10, 10)
# create rectangle, coordinates are ignored
rec = Rectangle((0,0),1,1).get_path()
#the big rectangle
r0 = rec.vertices+0.5
# r1 and r2 are the rectangles to cut out of r0
r1 = 0.6+rec.vertices[::-1]*0.35
r2 = 1+rec.vertices[::-1]*0.35
path = Path(vertices=np.concatenate([r0, r1, r2]),
codes=np.concatenate([rec.codes]*3))
im = ax.imshow(imdata, extent=(0, 2, 0, 2), aspect='equal',
cmap='coolwarm', interpolation='nearest')
patch = PathPatch(path, facecolor='w')
ax.add_patch(patch)
plt.tight_layout()
plt.show()
Or, a solution which makes it easier to specify the coordinates of the rectangles:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch, Rectangle
fig, ax = plt.subplots()
imdata = np.random.randn(10, 10)
def create_rec(x0, y0, width, height):
rec_patch = Rectangle((x0, y0),width, height)
rec_path = rec_patch.get_path()
rec_path = rec_patch.get_patch_transform().transform_path(rec_path)
return rec_path.vertices, rec_path.codes
#the big rectangle
r0,c = create_rec(0.3, 0.6, 1, 1.2)
# r1 and r2 are the rectangles to cut out of r0
r1,c = create_rec(0.4, 0.7, 0.3, 0.4)
r2,c = create_rec(0.8, 1, 0.4, 0.5)
path = Path(vertices=np.concatenate([r0, r1[::-1], r2[::-1]]),
codes=np.concatenate([c]*3))
im = ax.imshow(imdata, extent=(0, 2, 0, 2), aspect='equal',
cmap='coolwarm', interpolation='nearest')
patch = PathPatch(path, facecolor='w')
ax.add_patch(patch)
plt.tight_layout()
plt.show()
To account for the case where the rectangle is partially outside the original rectangle, the following (based on the second solution) might help:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch, Rectangle
fig, ax = plt.subplots()
imdata = np.random.randn(10, 10)
def create_rec(x0, y0, width, height):
rec_patch = Rectangle((x0, y0),width, height)
rec_path = rec_patch.get_path()
rec_path = rec_patch.get_patch_transform().transform_path(rec_path)
return rec_path.vertices, rec_path.codes
#the big rectangle
r0,c = create_rec(0.3, 0.6, 1, 1.2)
# r1 and r2 are the rectangles to cut out of r0
r1,c = create_rec(0.2, 0.5, 0.3, 0.4)
r2,c = create_rec(0.8, 1, 0.4, 0.5)
path = Path(vertices=np.concatenate([r0, r1[::-1], r2[::-1]]),
codes=np.concatenate([c]*3))
im = ax.imshow(imdata, extent=(0, 2, 0, 2), aspect='equal',
cmap='coolwarm', interpolation='nearest')
patho = Path(vertices=r0,codes=c)
patcho = PathPatch(patho, facecolor='none', edgecolor="none")
ax.add_patch(patcho)
patch = PathPatch(path, facecolor='w', clip_path=patcho, edgecolor="none")
ax.add_patch(patch)
plt.show()

Matplotlib: add_lines to colorbar with defined properties (color: OK; dotted: not OK)

I want to place a line at one level (e.g., 0) in the colorbar of a contourf plot with matplotlib.
With the following code, I can do it but not all the properties of the contour lines are conserved (i.e., the color and width of the line are correct, but I can't have it dotted in the colorbar).
Any idea of how to have a dotted line corresponding to a desired level in the colorbar?
import matplotlib.pyplot as plt
import numpy
x=y=range(10)
z=numpy.random.normal(0,2,size=(10,10))
surfplot=plt.contourf(x,y,z, cmap=plt.cm.binary_r)
cont=plt.contour(surfplot, levels=[0], colors='r', linewidths=5, linestyles=':')
cbar=plt.colorbar(surfplot)
cbar.add_lines(cont)
plt.show()
You could plot a horizontal line on your color bar directly.
cax = cbar.ax
cax.hlines(0.5, 0, 1, colors = 'r', linewidth = 10, linestyles = ':')
You'll have to calculate the y-coordinate of the line based on the data and the coloramp.
Colorbar.add_lines() currently only retains the colors and line widths.
However, you can update the line style of the new LineCollection after adding it:
import matplotlib.pyplot as plt
import numpy
plt.style.use('classic') # to match the look in the question
x = y = range(10)
z = numpy.random.normal(0, 2, size=(10, 10))
surfplot = plt.contourf(x, y, z, cmap=plt.cm.binary_r)
cont = plt.contour(surfplot, levels=[0], colors='r', linewidths=5, linestyles=':')
cbar = plt.colorbar(surfplot)
cbar.add_lines(cont)
cbar.lines[-1].set_linestyles(cont.linestyles) # adopt the contour's line styles
plt.show()