MPA
MPA

Reputation: 2038

Rotate matplotlib colourmap

The ProPlot Python package adds additional features to the Matplotlib library, including colourmap manipulations. One feature that is particularly attractive to me is the ability to rotate/shift colourmaps. To give you an example:

import proplot as pplot
import matplotlib.pyplot as plt
import numpy as np

state = np.random.RandomState(51423)
data = state.rand(30, 30).cumsum(axis=1)

fig, axes = plt.subplots(ncols=3, figsize=(9, 4))
fig.patch.set_facecolor("white")

axes[0].pcolormesh(data, cmap="Blues")
axes[0].set_title("Blues")
axes[1].pcolormesh(data, cmap="Blues_r")
axes[1].set_title("Reversed Blues")
axes[2].pcolormesh(data, cmap="Blues_s")
axes[2].set_title("Rotated Blues")

plt.tight_layout()
plt.show()

enter image description here

In the third column, you see the 180° rotated version of Blues. Currently ProPlot suffers from a bug that doesn't allow the user to revert the plotting style to Matplotlib's default style, so I was wondering if there was an easy way to rotate a colourmap in Matplotlib without resorting to ProPlot. I always found cmap manipulations in Matplotlib a bit arcane, so any help would be much appreciated.

Upvotes: 3

Views: 1615

Answers (2)

Mr. T
Mr. T

Reputation: 12410

To reverse a ListedColormap, there is a built-in reversed() but for the intended rotation, we have to create our own function.

#fake data generation 
import numpy as np
np.random.seed(123)
#numpy array containing x, y, and color
arr = np.random.random(30).reshape(3, 10)


from matplotlib import pyplot as plt
from matplotlib.colors import ListedColormap

def rotate_cm(co_map, deg=180): 
    #define a function where the colormap is rotated by a certain degree 
    #180° shifts by 50%, 360° no change
    n = co_map.N
    #if rotating in the opposite direction feels more intuitive, reverse the sign here
    deg = -deg%360
    if deg < 0:
        deg += 360
    cutpoint = n * deg // 360
    new_col_arr = [co_map(i) for i in range(cutpoint, n)] + [co_map(i) for i in range(cutpoint)]
    return ListedColormap(new_col_arr)
     

fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(21,7))
#any listed colormap
my_cm = plt.cm.get_cmap("inferno")

#normal color map
cb1 = ax1.scatter(*arr[:2,:], c=arr[2,:], cmap=my_cm, marker="o")
plt.colorbar(cb1, ax=ax1)
ax1.set_title("regular colormap")

#reversed colormap
cb2 = ax2.scatter(*arr[:2,:], c=arr[2,:], cmap=my_cm.reversed(), marker="o")
plt.colorbar(cb2, ax=ax2)
ax2.set_title("reversed colormap")

#rotated colormap
cb3 = ax3.scatter(*arr[:2,:], c=arr[2,:], cmap=rotate_cm(my_cm, 90), marker="o")
#you can also combine the rotation with reversed()
#cb3 = ax3.scatter(*arr[:2,:], c=arr[2,:], cmap=rotate_cm(my_cm, 90).reversed(), marker="o")
plt.colorbar(cb3, ax=ax3)
ax3.set_title("colormap rotated by 90°")


plt.show()

Sample output: ![enter image description here

Upvotes: 1

Diziet Asahi
Diziet Asahi

Reputation: 40737

If what you are trying to do is shift the colormaps, this can be done (relatively) easily:

def shift_cmap(cmap, frac):
    """Shifts a colormap by a certain fraction.

    Keyword arguments:
    cmap -- the colormap to be shifted. Can be a colormap name or a Colormap object
    frac -- the fraction of the colorbar by which to shift (must be between 0 and 1)
    """
    N=256
    if isinstance(cmap, str):
        cmap = plt.get_cmap(cmap)
    n = cmap.name
    x = np.linspace(0,1,N)
    out = np.roll(x, int(N*frac))
    new_cmap = matplotlib.colors.LinearSegmentedColormap.from_list(f'{n}_s', cmap(out))
    return new_cmap

demonstration:

x = np.linspace(0,1,100)
x = np.vstack([x,x])

cmap1 = plt.get_cmap('Blues')
cmap2 = shift_cmap(cmap1, 0.25)

fig, (ax1, ax2) = plt.subplots(2,1)
ax1.imshow(x, aspect='auto', cmap=cmap1)
ax2.imshow(x, aspect='auto', cmap=cmap2)

enter image description here

Upvotes: 5

Related Questions