EStt - 10 months ago 108

Python Question

I have a matrix of data ranging from 0 to 102, and when using imshow i'd like to have 0 displayed as burnt red, 1 displayed as yellow-green and every other value from 2 to 102 in a linear gradient from standard green to dark green.

So far I've tried using the colormap 'Greens' and replacing the first two values but I has inconsistent results and I don't know how to use only half of the colormap.

I've also tried to generate my own colormap using a colordict and then overriding the first two entries but it also has inconsitent results depending on the data submitted (for example 1 displaying as burnt red)

Answer

You could use `make_colormap`

to make a custom colormap:

```
mygreen = make_colormap([c('burnt red'),
c('yellow green'), idx[1], c('yellow green'),
(0,1,0), idx[2], (0,1,0), # RGB (0,1,0) is standard green?
c('dark green')])
```

The argument to

`make_colormap`

is a sequence of RGB values and floats.Every float is sandwiched between two RGB values.

The floats indicate locations (on a scale from 0.0 to 1.0) where the color map transitions from one RGB color to the next.

The first and last values in the sequence are the first and last colors in the color map.

```
import numpy as np
import matplotlib.colors as mcolors
import matplotlib.pyplot as plt
np.random.seed(2016)
def make_colormap(seq):
"""Return a LinearSegmentedColormap
seq: a sequence of floats and RGB-tuples.
- Every float is sandwiched between two RGB values.
- The floats indicate locations (on a scale from 0.0 to 1.0) where the
color map transitions from one RGB color to the next.
- The floats should be in increasing order
- The first and last values in the sequence are the first and last
colors in the color map.
http://stackoverflow.com/q/16834861/190597 (unutbu)
"""
seq = [(None,) * 3, 0.0] + list(seq) + [1.0, (None,) * 3]
cdict = {'red': [], 'green': [], 'blue': []}
for i, item in enumerate(seq):
if isinstance(item, float):
r1, g1, b1 = seq[i - 1]
r2, g2, b2 = seq[i + 1]
cdict['red'].append([item, r1, r2])
cdict['green'].append([item, g1, g2])
cdict['blue'].append([item, b1, b2])
return mcolors.LinearSegmentedColormap('CustomMap', cdict)
# There are 103 integers from 0 to 102 (inclusive)
idx = np.linspace(0, 1, 103)
c = mcolors.ColorConverter().to_rgb
mygreen = make_colormap([c('burnt red'),
c('yellow green'), idx[1], c('yellow green'),
(0,1,0), idx[2], (0,1,0), # RGB (0,1,0) is standard green?
c('dark green')])
arr = np.random.randint(0, 103, size=(11, 11))
print(np.where(arr==0))
print(np.where(arr==1))
plt.imshow(arr, interpolation='nearest', cmap=mygreen, vmin=0, vmax=102)
plt.colorbar(ticks=list(range(0, 100, 10))+[102])
plt.show()
```

Another (probably better) option is to modify `arr`

to take advantage of matplotlib's
`cmap.set_under`

and `cmap.set_over`

methods. These colormap methods allow you to set a color for
all values that go under or over specified limits:

```
cmap = make_colormap([(0,1,0), c('dark green')])
cmap.set_under('burnt red')
cmap.set_over('yellow green')
```

Thus, in the special case where you have just two special colors, you could
modify `arr`

so 0 gets mapped to, say, a negative number, and 1 gets mapped to a
number larger than 100, and shift the rest of the numbers to range from 0 to
100:

```
arr -= 2
arr = np.where(arr==-1, 102, arr)
```

For example,

```
import numpy as np
import matplotlib.colors as mcolors
import matplotlib.pyplot as plt
np.random.seed(2016)
def make_colormap(seq):
"""Return a LinearSegmentedColormap
seq: a sequence of floats and RGB-tuples.
- Every float is sandwiched between two RGB values.
- The floats indicate locations (on a scale from 0.0 to 1.0) where the
color map transitions from one RGB color to the next.
- The floats should be in increasing order
- The first and last values in the sequence are the first and last
colors in color map.
http://stackoverflow.com/q/16834861/190597 (unutbu)
"""
seq = [(None,) * 3, 0.0] + list(seq) + [1.0, (None,) * 3]
cdict = {'red': [], 'green': [], 'blue': []}
for i, item in enumerate(seq):
if isinstance(item, float):
r1, g1, b1 = seq[i - 1]
r2, g2, b2 = seq[i + 1]
cdict['red'].append([item, r1, r2])
cdict['green'].append([item, g1, g2])
cdict['blue'].append([item, b1, b2])
return mcolors.LinearSegmentedColormap('CustomMap', cdict)
c = mcolors.ColorConverter().to_rgb
cmap = make_colormap([(0,1,0), c('dark green')])
cmap.set_under('burnt red')
cmap.set_over('yellow green')
arr = np.random.randint(0, 102, size=(11, 11))
# modify arr so that 0 is mapped to a negative number (-2) and 1 is mapped to a
# positive number greater than 100, (say, 102), and all other values are
# decreased by 2
arr -= 2
arr = np.where(arr==-1, 102, arr)
plt.imshow(arr, interpolation='nearest', cmap=cmap, vmin=0, vmax=100)
plt.colorbar(extend='both')
plt.show()
```