Remove a section of a colormap - python

I really like the "RdBu_r" colormap, but I want to cutout the white part between the blues and reds. Is there an easy way to do this?

Yes, but in your case, it's probably easier to make a colormap that interpolates between blue and red instead.
For example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
cmap = LinearSegmentedColormap.from_list('name', ['red', 'blue'])
fig, ax = plt.subplots()
im = ax.imshow(np.random.random((10, 10)), cmap=cmap)
fig.colorbar(im)
plt.show()
Note that you could substitute the exact RGB values if you wanted a shade of red that isn't an HTML color name.
However, if you did want to "cut out the middle" of another colormap, you'd evaluate it on a range that didn't include the middle and create a new colormap:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
# Remove the middle 40% of the RdBu_r colormap
interval = np.hstack([np.linspace(0, 0.3), np.linspace(0.7, 1)])
colors = plt.cm.RdBu_r(interval)
cmap = LinearSegmentedColormap.from_list('name', colors)
# Plot a comparison of the two colormaps
fig, axes = plt.subplots(ncols=2)
data = np.random.random((10, 10))
im = axes[0].imshow(data, cmap=plt.cm.RdBu_r, vmin=0, vmax=1)
fig.colorbar(im, ax=axes[0], orientation='horizontal', ticks=[0, 0.5, 1])
axes[0].set(title='Original Colormap')
im = axes[1].imshow(data, cmap=cmap, vmin=0, vmax=1)
fig.colorbar(im, ax=axes[1], orientation='horizontal', ticks=[0, 0.5, 1])
axes[1].set(title='New Colormap')
plt.show()

Related

How can I normalize a colormap in Python?

I've made a colormap from a matrix (matrix300.txt). I would like to normalize my colormap, but I don't know how to do it. The axes should be from 0 to 3.
import numpy as np
import matplotlib.pyplot as plt
import math
from matplotlib import colors
import matplotlib.colors as colors
p = np.loadtxt('matrix300.txt')
cmap = colors.ListedColormap(['yellow', 'white'], name='Gamma=1')
bounds = [-1, 0, 1]
norm = colors.BoundaryNorm(bounds, cmap.N)
img = plt.imshow(p, interpolation='nearest', origin='lower', cmap=cmap, norm=norm)
plt.colorbar(img, cmap=cmap, norm=norm, boundaries=bounds)
plt.xlabel("h_0")
plt.ylabel("h")
plt.show()
Your usage of ListedColormap is not right here. It is for creating a user-defined colormap. So it may be a bit too much of elaborating if you need a colormap just for plotting. I post an example using ListedColormap anyway.
import numpy as np
import matplotlib.pyplot as plt
import math
from matplotlib import colors
import matplotlib.colors as colors
# dummy data
yy, xx = np.mgrid[0:1:10j, 0:1:10j]
p = np.exp(-10*(xx-0.5)**2-10*(yy-0.5)**2) # gaussian function
# create your own colormap
pmax, pmin = np.max(p), np.min(p) # get data range
bounds = np.linspace(pmin, pmax, 100) # set data range for colormap
num = 100 # how many colors do you need?
norm = colors.BoundaryNorm(bounds, ncolors=num) # an object for cmap normalization
carray = np.array([np.linspace(0,1,num), np.linspace(0.0,0.25,num), [0.5]*num, [1]*num]).T # list of RGBA values
cmap = colors.ListedColormap(carray, N=100, name='mycolormap') # create your own color map from carray
# plot data
img = plt.imshow(p, interpolation='nearest', origin='lower', cmap=cmap, norm=norm)
plt.colorbar(img, cmap=cmap, norm=norm, boundaries=bounds)
plt.show()
Mush easier way is to use pcolormesh.
fig, ax = plt.subplots(1,1)
ax.set_aspect("equal")
im = ax.pcolormesh(xx, yy, p, vmin=pmin, vmax=pmax)
cbar = fig.colorbar(mappable=im)
plt.show()

Matplotlib colorbar does not draw edges at extremities

When creating a matplotlib colorbar, it is possible to set drawedges to True which separates the colors of the colorbar with black lines. However, when the colorbar is extended using extend='both', the black lines at the extremities do not show up. Is that a bug? Is there a possibility to draw those edges otherwise?
Here is the code:
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import from_levels_and_colors
my_cmap = mpl.cm.viridis
bounds = np.arange(10)
nb_colors = len(bounds) + 1
colors = my_cmap(np.linspace(100, 255, nb_colors).astype(int))
my_cmap, my_norm = from_levels_and_colors(bounds, colors, extend='both')
plt.figure(figsize=(5, 1))
ax = plt.subplot(111)
cbar = mpl.colorbar.ColorbarBase(ax, cmap=my_cmap, norm=my_norm, orientation='horizontal', drawedges=True)
plt.subplots_adjust(left=0.05, bottom=0.4, right=0.95, top=0.9)
plt.show()
and the figure it gives:
I looked into it from your question and found a way to change the color of the border and vertical lines of the color bar. I used that to change them to red. The result I got was that the extended outline was red, so my guess is that I just pulled the short sides of the normal color bar rectangle to the left and right.
I found this response helpful.
cbar.outline.set_edgecolor('red')
cbar.dividers.set_color('red')
So I think the only way to do this is to add vertical lines.
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import from_levels_and_colors
my_cmap = mpl.cm.viridis
bounds = np.arange(10)
nb_colors = len(bounds) + 1
colors = my_cmap(np.linspace(100, 255, nb_colors).astype(int))
my_cmap, my_norm = from_levels_and_colors(bounds, colors, extend='both')
plt.figure(figsize=(6, 2))
ax = plt.subplot(111)
cbar = mpl.colorbar.ColorbarBase(ax, cmap=my_cmap, norm=my_norm, orientation='horizontal', drawedges=True)
# update
cbar.outline.set_edgecolor('red')
cbar.dividers.set_color('red')
plt.axvline(max(bounds), color='red', alpha=0.3, linewidth=3.5)
plt.axvline(min(bounds), color='red', alpha=0.3, linewidth=3.5)
plt.subplots_adjust(left=0.05, bottom=0.4, right=0.95, top=0.9)
plt.show()

Displaying lowest values as white

I have the following 3x3 matrix which I would like to plot:
import matplotlib.cm
import matplotlib.pyplot as plt
import numpy as np
import copy
cmap = copy.copy(cm.get_cmap("Blues"))
cmap.set_bad('white')
fig = plt.figure(figsize=(15, 10))
img = np.array([[-0.9, -0.5599234, 0.21042876],[-0.42735877, 0.61514954, -0.74305015],[0.61958201, -0.04358633, 0.78672511]])
im = plt.imshow(img, origin='upper', cmap=cmap)
The result looks as follows:
As visible the top left entry is smallest and should be displayed as white. How can I change it in such a way so that the smallest entry is displayed in white?
Second, is there a way to adapt the colormap such that it starts with darker values?
One way to have a colormap start with white, is to create a ListedColormap, e.g. going from white to darkblue. To start with the darkest color, just reverse the list of colors for the ListedColormap.
A standard colormap can be reversed, just by appending _r at the end of its name.
One way to create a colormap going from a mid-range to a dark blue, is creating a ListedColormap where the rgb-values are given as hexadecimal.
Here are some examples:
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import numpy as np
img = np.array([[-0.9, -0.5599234, 0.21042876], [-0.42735877, 0.61514954, -0.74305015], [0.61958201, -0.04358633, 0.78672511]])
fig, axs = plt.subplots(ncols=3, figsize=(12, 5))
cmap0 = LinearSegmentedColormap.from_list('', ['white', 'darkblue'])
cmap1 = 'Blues_r'
cmap2 = LinearSegmentedColormap.from_list('', ['#aaddee', '#000077'])
for ax, cmap in zip(axs, [cmap0, cmap1, cmap2]):
im = ax.imshow(img, origin='upper', cmap=cmap)
plt.colorbar(im, ax=ax, orientation='horizontal', pad=0.05)
ax.set_xticks([0, 1, 2])
ax.set_yticks([0, 1, 2])
ax.tick_params(labelbottom=False, labelleft=False, length=0) # hide ticks, but use position for a grid
ax.grid(True, color='white')
axs[0].set_title("Colormap from white to darkblue")
axs[1].set_title("Reversed blues colormap")
axs[2].set_title("Custom darker blues colormap")
plt.show()
Also of interest might be Seaborn's palette functions, which provide additional ways to create colormaps (the parameter as_cmap=True is needed for these functions to return a colormap).

matplotlib colorbar and histogram with shared axis

I would like to display a 2D np.array with imshow and the respective colorbar which should share its axis with a histogram of the np.array. Here is an attempt, however, without shared axes.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.axes_grid1 import make_axes_locatable
fig, ax = plt.subplots(figsize=(7,10))
data = np.random.normal(0, 0.2, size=(100,100))
cax = ax.imshow(data, interpolation='nearest', cmap=cm.jet)
divider = make_axes_locatable(plt.gca())
axBar = divider.append_axes("bottom", '5%', pad='7%')
axHist = divider.append_axes("bottom", '30%', pad='7%')
cbar = plt.colorbar(cax, cax=axBar, orientation='horizontal')
axHist.hist(np.ndarray.flatten(data), bins=50)
plt.show()
I tried to use the sharex argument in axHist with axHist = divider.append_axes("bottom", '30%', pad='7%', sharex=axBar) but this somehow shifts the histogram data:
Besides the shared axis x, how could one modify the histogram to take the same colors as the colormap, similar to here?
You may color every patch of histogram by bin value without sharex:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.colors import Normalize
fig, ax = plt.subplots(figsize=(7,10))
data = np.random.normal(0, 0.2, size=(100,100))
cax = ax.imshow(data, interpolation='nearest', cmap=cm.jet)
divider = make_axes_locatable(plt.gca())
axBar = divider.append_axes("bottom", '5%', pad='7%')
axHist = divider.append_axes("bottom", '30%', pad='7%')
cbar = plt.colorbar(cax, cax=axBar, orientation='horizontal')
# get hist data
N, bins, patches = axHist.hist(np.ndarray.flatten(data), bins=50)
norm = Normalize(bins.min(), bins.max())
# set a color for every bar (patch) according
# to bin value from normalized min-max interval
for bin, patch in zip(bins, patches):
color = cm.jet(norm(bin))
patch.set_facecolor(color)
plt.show()
For more information look for manual page: https://matplotlib.org/xkcd/examples/pylab_examples/hist_colormapped.html

How to adjust size of two subplots, one with colorbar and another without, in pyplot ?

Consider this example
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
plt.subplot(121)
img = plt.imshow([np.arange(0,1,.1)],aspect="auto")
ax = plt.gca()
divider = make_axes_locatable(ax)
cax = divider.append_axes("bottom", size="3%", pad=0.5)
plt.colorbar(img, cax=cax, orientation='horizontal')
plt.subplot(122)
plt.plot(range(2))
plt.show()
I want to make these two figures (plot region without colorbar) of the same size.
The size is automatically adjusted if the colorbar is plotted vertically or if two rows are used (211, 212) instead of two columns.
One can basically do the same for the second subplot as for the first, i.e. create a divider and append an axes with identical parameters, just that in this case, we don't want a colorbar in the axes, but instead simply turn the axis off.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
ax = plt.subplot(121)
img = ax.imshow([np.arange(0,1,.1)],aspect="auto")
divider = make_axes_locatable(ax)
cax = divider.append_axes("bottom", size="3%", pad=0.5)
plt.colorbar(img, cax=cax, orientation='horizontal')
ax2 = plt.subplot(122)
ax2.plot(range(2))
divider2 = make_axes_locatable(ax2)
cax2 = divider2.append_axes("bottom", size="3%", pad=0.5)
cax2.axis('off')
plt.show()
You can now do this without recourse to an extra toolkit by using constrained_layout:
import numpy as np
import matplotlib.pyplot as plt
fig, axs = plt.subplots(1, 2, constrained_layout=True)
ax = axs[0]
img = ax.imshow([np.arange(0,1,.1)],aspect="auto")
fig.colorbar(img, ax=ax, orientation='horizontal')
axs[1].plot(range(2))
plt.show()

Categories