'Python- What colormap scheme should I use for exponential-ish data?
The issue
I have a plot I'm trying to make for trends of precipitation rates around the world using gridded data. I can make the plot itself fine, but the color range is giving me issues. I can't figure out how to make the colormap better fit my data, which seems exponential. I tried a logarithmic range, but it doesn't quite fit the data right.
The code & data range
Here's what my 8,192 data values look like when plotted in order on a simple x-y line plot. Data points are on the x-axis & values are on the y-axis.

Here's what my data looks like plotted with a LogNormal color range. It's too much mint green & orange-red for me.
#Set labels
lonlabels = ['0','45E','90E','135E','180','135W','90W','45W','0']
latlabels = ['90S','60S','30S','Eq.','30N','60N','90N']
#Set cmap properties
norm = colors.LogNorm() #creates logarithmic scale
#Create basemap
fig,ax = plt.subplots(figsize=(15.,10.))
m = Basemap(projection='cyl',llcrnrlat=-90,urcrnrlat=90,llcrnrlon=0,urcrnrlon=360.,lon_0=180.,resolution='c')
m.drawcoastlines(linewidth=1)
m.drawcountries(linewidth=1)
m.drawparallels(np.arange(-90,90,30.),linewidth=0.3)
m.drawmeridians(np.arange(-180.,180.,45.),linewidth=0.3)
meshlon,meshlat = np.meshgrid(lon,lat)
x,y = m(meshlon,meshlat)
#Plot variables
trend = m.pcolormesh(x,y,lintrends[:,:,0],cmap='jet', norm=norm, shading='gouraud')
#Set plot properties
#Colorbar
cbar=m.colorbar(trend, size='8%',location='bottom',pad=0.8) #Set colorbar
cbar.set_label(label='Linear Trend (mm/day/decade)',size=25) #Set label
for t in cbar.ax.get_xticklabels():
t.set_fontsize(25) #Set tick label sizes
#Titles & labels
fig.suptitle('Linear Trends of Precipitation (CanESM2)',fontsize=40,x=0.51,y=0.965)
ax.set_title('a) 1979-2014 Minimum Trend',fontsize=35)
ax.set_xticks(np.arange(0,405,45))
ax.set_xticklabels(lonlabels,fontsize=20)
ax.set_ylabel('Latitude',fontsize=25)
ax.set_yticks(np.arange(-90,120,30))
ax.set_yticklabels(latlabels,fontsize=20)
And here's what it looks like with a default, unaltered color range. (Same code minus the norm=norm argument.)
The question
Is there a mathematical scheme I can use to create a colormap that better shows the range of my data? Or do I need to make a custom range?
Solution 1:[1]
A hack
You could try applying a maximum value, i.e. for any value above 2 simply replace it with 2.
Then you would have a single color (the maximum) representing 2+ and the rest of the colors would be spread across your data more evenly.
Solution 2:[2]
I had the same issue and resolved it myself, so I'll answer here. What you really want is a nonlinear color scale. Functions like imshow can take a norm, which can scales the value-color relationship any which way, including nonlinearly. If you want the colors to be logarithmically scaled, that's a problem if 0 is a part of the domain, because it will try to map 0 to log(0) = -inf, which won't work. So you will have to add a small value to your data so that it's never 0. In literature sometimes you see log(1+x) plotted instead of x, so that's why, and that works. So you could plot 1+x, with logarithmic color scaling. Another solution is to not plot logarithmically, and instead plot via a power law, then you won't get issues with 0. So you can map the colors x to x^a with a < 1. This is fundamentally different, but unless you have fine detail at literally every scale (this is when you truly need logarithmic scaling), it should look fine.
So how do you do all this? Some matplotlib functions take a norm. You want norm to be some color scale, like matplotlib.colors.LogNorm(1, 2) for plotting 1+x with x between 0 and 1, or matplotlib.colors.PowerNorm(.5) for plotting x with sqrt(x) color scaling.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Community |
| Solution 2 | olafwx |


