how to extract a subset of a colormap as a new colormap in matplotlib?
The staticmethod colors.LinearSegmentedColormap.from_list can be used to create new LinearSegmentedColormaps. Below, I sample the original colormap at 100 points between 0.2 and 0.8:
cmap(np.linspace(0.2, 0.8, 100))
and use these colors to generate a new colormap:
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import numpy as np
def truncate_colormap(cmap, minval=0.0, maxval=1.0, n=100):
new_cmap = colors.LinearSegmentedColormap.from_list(
'trunc({n},{a:.2f},{b:.2f})'.format(n=cmap.name, a=minval, b=maxval),
cmap(np.linspace(minval, maxval, n)))
return new_cmap
arr = np.linspace(0, 50, 100).reshape((10, 10))
fig, ax = plt.subplots(ncols=2)
cmap = plt.get_cmap('jet')
new_cmap = truncate_colormap(cmap, 0.2, 0.8)
ax[0].imshow(arr, interpolation='nearest', cmap=cmap)
ax[1].imshow(arr, interpolation='nearest', cmap=new_cmap)
plt.show()
The plot on the left shows the image using the original colormap (in this example, jet
). The plot on the right shows the same image using new_cmap
.
I was just recently struggling with this on my own. Here are some possible solutions:
Try using vmin
, vmax
keyword arguments in your plotting function. For example, say you had data between 0 and 1 but didn't like the colors used at the extremes of the colormap for 0 and 1.
import matplotlib.pyplot as plt
import matplotlib.cm as cm
my_cmap = cm.spectral_r
my_cmap.set_over('c')
my_cmap.set_under('m')
plt.pcolor(data, vmin=0.01, vmax=0.99, cmap=my_cmap)
This will force the entire colormap to be used for values between 0.01 and 0.99 and values above and below will be cyan and magenta respectively. This may not solve your problem exactly, but it could be useful if you like a particular colormap and wish it had additional colors at both ends.
If you really want to change the colormap, look at the documentation here and for LinearSegmentedColormap here.
First,
import matplotlib.cm as cm
cdict = cm.get_cmap('spectral_r')._segmentdata
This returns a dictionary of all the colors that make up the colormap. However, it's pretty tricky figuring out exactly how to alter this dictionary. This dict has three keys, red, green, blue
. cdict[key]
returns a list of values of the form (x, y0, y1)
. Let's take a look at two consecutive elements of cdict['red']
:
((0.0, 0.0, 0.0)
(0.5, 1.0, 1.0),...
What this means is that data with z
(assuming we're doing a pcolor
or imshow
) between 0.0 and 0.5 will have the red component of the rgb color associated with that data will increase from 0.0 (no red) to 1.0 (maximum red). This means that to change the color of the colormap, you have to examine how each of the three components of rgb are interpolated in the region of the colormap that you are interested in. Just make sure that for each color, the first and the last entry start with x=0
and x=1
respectively; you must cover the whole spectrum of [0, 1].
If you want to change the beginning and end colors, try
import matplotlib.cm as cm
from matplotlib.colors import LinearSegmentedColormap
cdict = cm.get_cmap('spectral_r')._segmentdata
cdict['red'][0] = (0, 0.5, 0.5) # x=0 for bottom color in colormap
cdict['blue'][0] = (0, 0.5, 0.5) # y=0.5 gray
cdict['green'][0] = (0, 0.5, 0.5) # y1=y for simple interpolation
cdict['red'][-1] = (1, 0.5, 0.5) # x=1 for top color in colormap
cdict['blue'][-1] = (1, 0.5, 0.5)
cdict['green'][-1] = (1, 0.5, 0.5)
my_cmap = LinearSegmentedColormap('name', cdict)
Then use this cmap in your plotting function.
What I wanted to do was change the gray at the end of the spectral_r
colormap to pure white. This was achieved using
# Using imports from above
cdict = matplotlib.cm.get_cmap('spectral_r')._segmentdata
cdict['red'][0] = (0, 1, 1)
cdict['green'][0] = (0, 1, 1)
cdict['blue'][0] = (0, 1, 1)
my_cmap = LinearSegmentedColormap('my_cmap', cdict)