Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Looping or wrapping a colormap over a given interval

I recently ran into a problem where I wanted a cyclic colormap to be 'wrapped', for want of a better word, on a given interval. In the example below, I plot values over the interval [-5, 5], and I want to repeat the colormap on each interval [-1, 1]. I started out by using matplotlib.colors.Normalize, but while I intended this to normalise numbers onto the interval [0, 1], numbers outside the given vmin and vmax arguments were mapped outside of this interval:

>>> norm = mpl.colors.Normalize(vmin=-1, vmax=1)
>>> norm(2)
1.5

When this value is passed to a colormap, the value returned is by default cmap(1), but this can be changed by using cmap.set_over()/cmap.set_under() . However, from what I can see, this can only be set to a static color.

The unsatisfying solution I came up with was to define a function that applies my norm function to an array, mod 1, and then pass this as my data to imshow(), without the need to pass norm as an argument. My attempts so far are below:

Code:

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt

# Plot values between -5 and 5
x = np.linspace(-5,5,1000).reshape(1, -1)

# Normalize values to between -1 and 1
norm = mpl.colors.Normalize(vmin=-1, vmax=1)

# Function to apply norm cyclicly
def f(x):
    return norm(x)%1

# Use a cyclic cmap
cmap=plt.cm.hsv

# Plot image
fig, axs = plt.subplots(3, 1)

# Attempt using just cmap, no norm
axs[0].imshow(x, cmap=cmap, extent=[-5,5,0,1])

# Attempt applying norm, no application of modulus function
axs[1].imshow(x, cmap=cmap, norm=norm, extent=[-5,5,0,1])

# Correct output, applying norm%1 to data before passing to cmap
axs[2].imshow(f(x), cmap=cmap, extent=[-5,5,0,1])

Output:

enter image description here

Question:

Is there a better/built in way to loop/wrap a colormap without having to define a custom function, as in the bottom subplot?

like image 691
CDJB Avatar asked Dec 15 '25 15:12

CDJB


1 Answers

An idea could be to adapt the custom norm from the tutorial to get a cyclic norm:

import matplotlib.pyplot as plt
from matplotlib import colors
import numpy as np

class CyclicNormalize(colors.Normalize):
    def __init__(self, cmin=0, cmax=1, vmin=0, vmax=1, clip=False):
        self.cmin = cmin
        self.cmax = cmax
        colors.Normalize.__init__(self, vmin, vmax, clip=clip)

    def __call__(self, value, clip=False):
        x, y = [self.cmin, self.cmax], [0, 1]
        return np.ma.masked_array(np.interp(value, x, y, period=self.cmax - self.cmin))

x = np.linspace(-5, 5, 1000).reshape(1, -1)
cmap = plt.cm.hsv

cyclicnorm = CyclicNormalize(cmin=-1, cmax=1, vmin=-5, vmax=5)

fig, ax = plt.subplots()
pcm = ax.imshow(x, cmap=cmap, norm=cyclicnorm, extent=[-5, 5, 0, 1])
plt.show()

plot

like image 104
JohanC Avatar answered Dec 17 '25 08:12

JohanC



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!