bayouwxman
bayouwxman

Reputation: 89

Center matplotlib colormap on a specific value

I'm making plots using matplotlib colormap "seismic" and would like to have the white color centered on 0. When I run my script with no changes, white falls from 0 to -10. I tried then setting vmin=-50, vmax=50 but I completely lose the white in that case. Any suggestions on how to accomplish that?

from netCDF4 import Dataset as NetCDFFile
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap
nc = NetCDFFile('myfile.nc')
lat = nc.variables['lat'][:]
lon = nc.variables['lon'][:]
time = nc.variables['time'][:]
hgt = nc.variables['hgt'][:]
map = Basemap(llcrnrlon=180.,llcrnrlat=0.,urcrnrlon=320.,urcrnrlat=80.)
lons,lats = np.meshgrid(lon,lat)
x,y = map(lons,lats)
cs = map.contourf(x,y,hgt[0],cmap='seismic')
cbar = plt.colorbar(cs, orientation='horizontal', shrink=0.5, 
cmap='seismic')
cbar.set_label('500mb Geopotential Height Anomalies(m)')
map.drawcoastlines()
map.drawparallels(np.arange(20,80,20),labels=[1,1,0,0], linewidth=0.5)
map.drawmeridians(np.arange(200,320,20),labels=[0,0,0,1], linewidth=0.5)
plt.show()

Plot with defaults:
Plot with defaults

Plot with vmin, vmax set:
Plot with vmin, vmax set

Upvotes: 2

Views: 4338

Answers (1)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339062

You can set the levels you want to show manually. As long as you have the same spacing of intervals to the left and to the right of zero this works nicely.

levels = [-50,-40,-30,-20,-10,10,20,30,40,50]
ax.contourf(X,Y,Z, levels)

Example:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-6.3,6.3)
y = np.linspace(-3.1,3.1)
X,Y = np.meshgrid(x,y)
Z = -np.cos(X)*np.cos(Y)*45

levels = [-50,-40,-30,-20,-10,10,20,30,40,50]
fig, ax = plt.subplots(figsize=(4,2))
cont = ax.contourf(X,Y,Z,levels, cmap="seismic")
fig.colorbar(cont, orientation="horizontal")
plt.show()

enter image description here

Or, if you want the colorbar to be proportional to the data,

fig.colorbar(cont, orientation="horizontal", spacing="proportional")

enter image description here

If levels are unequal, you need to specify vmin and vmax.

levels = [-50,-40,-30,-20,-10,10,30,50,80,100]
cont = ax.contourf(X,Y,Z,levels, cmap="seismic", vmin=-50, vmax=50)

enter image description here

The disadvantage is that you loose resolution, hence you may use a BoundaryNorm to select equally spaced colors for unequally spaced labels.

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

x = np.linspace(-6.3,6.3)
y = np.linspace(-3.1,3.1)
X,Y = np.meshgrid(x,y)
Z = -np.cos(X)*np.cos(Y)*45

levels = [-50,-40,-30,-20,-10,10,30,50,80,100]
norm = matplotlib.colors.BoundaryNorm(levels, len(levels)-1)
fig, ax = plt.subplots(figsize=(4,2))
cont = ax.contourf(X,Y,Z,levels,cmap=plt.get_cmap("seismic",len(levels)-1), norm=norm)
fig.colorbar(cont, orientation="horizontal")
plt.show()

enter image description here

To change the ticklabels on the colorbar so something other than the levels or in case they are too dence you may use the ticks argument.

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-6.3,6.3)
y = np.linspace(-3.1,3.1)
X,Y = np.meshgrid(x,y)
Z = -np.cos(X)*np.cos(Y)*45

levels = np.arange(-45,50,5)
levels = levels[levels!=0]
ticks=np.arange(-40,50,10)

fig, ax = plt.subplots(figsize=(4,2))
cont = ax.contourf(X,Y,Z,levels,cmap="seismic", spacing="proportional")
fig.colorbar(cont, orientation="horizontal", ticks=ticks, spacing="proportional")
plt.show()

enter image description here

Upvotes: 2

Related Questions