Python matplotlib manual colormap
Asked Answered
C

2

5

I have 2-d fields to plot in python using matplotlib basemap. The values of the fields varies from 0 to more than 1000. Is it possible to create manual colormap with fixed gradations and colours? It should looks like :

for values - set colour

  • 0 - 1 - white
  • 1 - 5 - dark green
  • 5 - 10 - light green
  • 10 - 25 - yellow
  • 25 - 50 - brown
  • 50 - 100 - orange
  • 100 - 500 - light red
  • 500 - 1000 - dark red
  • > 1000 - purple

I am a new in python. So, any suggestions are welcome.

Canaster answered 26/8, 2014 at 12:54 Comment(1)
I did not mention that the data were in grib files and had proper lon-lat coordinates. So, I need to plot them with manual colour bar (like I specified above) on lon-lat map.Canaster
G
7

This is exactly what you want.

The way you have to input it is a little confusing though, so this might be more helpful.

To get the greater than 1000 bit though, you'll need to mask the values above 1000, and have the rest of the scale go from 0-1000.

from matplotlib.colors import LinearSegmentedColormap
cMap = []
for value, colour in zip([0,1,5,10,25,50,100,500,1000],["White", "DarkGreen", "LightGreen", "Yellow", "Brown", "Orange", "IndianRed", "DarkRed", "Purple"]):
    cMap.append((value/1000.0, colour))

customColourMap = LinearSegmentedColormap.from_list("custom", cMap)

That is all you need to create your custom colormap. To use it just pass it into the plot function (whichever one you're using) as the named argument cmap

Here's what it looks like.enter image description here

Gluttonous answered 26/8, 2014 at 13:1 Comment(10)
Any particular reason for the down vote? This points to the exact part of the documentation that provides exactly what the question asks...Gluttonous
Someone has slipped their finger to the wrong arrow - I find this a good answer (+1) with a lot of information.Undershrub
I did not mention that the data were in grib files and had proper lon-lat coordinates. So, I need to plot them with manual colour bar (like I specified above) on lon-lat mapCanaster
What do you mean? Once you specify the colour bar, it will normalise the data, and have the colours go from 0-1. If you wanted it to be capped at 1000, and make everything above that purple, then you can either edit the data you plot (IMO a bad idea, as you're hiding information), or you can mask the data above 1000 and make masked data purple. You can mask it with numpy masked arrays. The format of the data makes no difference to this process, the cmap keyword argument just keeps going up the class heirarchy until it hits something that wants it (i think the artist).Gluttonous
@AlexanderKurganskiy It doesn't matter what kind of plot you have. Once you have a working 2D plot of the data you want to show, you need only pass a ColorMap to the cmap keyword argument of whichever plotting function you're using.Gluttonous
@will, What if I have a continuous function? How can I use it (Without sampling it into segments)?Miff
@Miff - I don't think there is a way to do it without sampling, at least on some level - you always need to have knots in order to link the two together.Gluttonous
@will, I could create a pseudo continuous function so for each value in [0, 1] it will yield a triplet. It seems there is no wrapper for such case. I'd guess you could subclass the colormap class? I just have no idea how to...Miff
@Miff I would create a function that samples your continuous function on some arbitrary resolution and creates the stops for the colouring function. what's the reason you want it to be continuous?Gluttonous
@will, What you suggested is what can be done. What I am after is having a callable function so instead of interpolating between 2 points the direct solution can be calculated.Miff
G
0

Something like this should work:

from matplotlib import pyplot
from matplotlib.colors import LinearSegmentedColormap

def create_cmap(data, colours, stops=None):
    min_x = min(data)
    max_x = max(data)
    
    if stops is not None:
        min_x = min(min_x, min(stops))
        max_x = max(max_x, max(stops))
    
    if stops is None:
        d_x = (max_x - min_x)/(len(colours)-1)
        stops = [min_x + i*d_x for i in range(len(colours))]
    
    if min_x < min(stops):
        stops = [min_x] + stops
        colours = [colours[0]] + colours
        
    if max_x > max(stops):
        stops = stops + [max_x]
        colours = colours + [colours[-1]]
    
    stops = [(s-min_x)/(max_x-min_x) for s in stops]
    
    cmap_data = list(zip(stops, colours))
    cmap = LinearSegmentedColormap.from_list('continuous_map', cmap_data)
    
    def cmap_wrapper(x):
        x = max(min_x, min(x, max_x))
        x_n = (x-min_x)/(max_x-min_x)
        return cmap(x_n)
    
    return cmap_wrapper



colours = ['xkcd:white', 'xkcd:darkgreen', 'xkcd:lightgreen', 'xkcd:yellow', 'xkcd:brown', 'xkcd:orange', 'xkcd:coral', 'xkcd:crimson', 'xkcd:purple']
stops = [0, 1, 5, 10, 25, 50, 100, 500, 1000]

cmap = create_cmap(stops, colours, stops=stops)

fig = pyplot.figure(figsize=(10,10))
ax = fig.add_subplot(1,1,1)

for y in range(1000):
    ax.plot([0,1],[y,y],c=cmap(y))
    
pyplot.show()

enter image description here

Gluttonous answered 16/10, 2023 at 23:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.