Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to adjust Matplotlib colorbar range in xarray plot?

I have a plot that looks like this

Pseudocolor maps

I cannot understand how to manually change or set the range of data values for the colorbar. I would like to experiment with ranges based on the data values shown in the plots and change the colorbar to (-4,4). I see that plt.clim, vmin and vmax are functions to possibly use.

Here is my code:

import cdsapi
import xarray as xr
import matplotlib.pyplot as plt
import numpy as np
import cartopy.crs as ccrs
# Also requires cfgrib library.

c = cdsapi.Client()

url = c.retrieve(
    'reanalysis-era5-single-levels-monthly-means',
    {
        'product_type': 'monthly_averaged_reanalysis',
        'format': 'grib',
        'variable': ['100m_u_component_of_wind','100m_v_component_of_wind'],
        'year': ['2006','2007','2008','2009','2010','2011','2012','2013','2014','2015','2016','2017','2018','2019','2020','2021'],
        'month': ['01','02','03','04','05','06','07','08','09','10','11','12'],
        'time': '00:00',
        'grid': [0.25, 0.25],
        'area': [70.00, -180.00, -40.00, 180.00],
    },
    "C:\\Users\\U321103\\.spyder-py3\\ERA5_MAPPING\\100m_wind_U_V.grib")
path = "C:\\Users\\U321103\\.spyder-py3\\ERA5_MAPPING\\100m_wind_U_V.grib"
ds = xr.load_dataset(path, engine='cfgrib')

wind_abs = np.sqrt(ds.u100**2 + ds.v100**2)
monthly_means = wind_abs.mean(dim='time')
wind_abs_clim = wind_abs.sel(time=slice('2006-01','2020-12')).groupby('time.month').mean(dim='time') # select averaging period

wind_abs_anom = ((wind_abs.groupby('time.month') / wind_abs_clim))-1 #deviation from climo

fg = wind_abs_anom.sel(time=slice('2021-01',None)).groupby('time.month').mean(dim='time').plot(col='month',
                        col_wrap=3,transform=ccrs.PlateCarree(),
                        cbar_kwargs={'orientation':'horizontal','shrink':0.6, 'aspect':40,'label':'Percent Deviation'},robust=False,subplot_kws={'projection': ccrs.Mercator()})

fg.map(lambda: plt.gca().coastlines())                                                                                               
like image 218
user2100039 Avatar asked Oct 19 '25 09:10

user2100039


1 Answers

I was able to reproduce your figure and found that I could add vmin and vmax as shown below. For some reason that meant I also had to specify the colormap, otherwise I ended up with viridis. But the code below works for me (with a bit of refactoring as I got it working — the only material change here is in the plotting section at the bottom).

First, loading the data:

import cdsapi

c = cdsapi.Client()
params = {
    'product_type': 'monthly_averaged_reanalysis',
    'format': 'grib',
    'variable': ['100m_u_component_of_wind', '100m_v_component_of_wind'],
    'year': [f'{n}' for n in range(2006, 2022)],
    'month': [f'{n:02d}' for n in range(1, 13)],
    'time': '00:00',
    'grid': [0.25, 0.25],
    'area': [70.00, -180.00, -40.00, 180.00],
}
path = '100m_wind_U_V.grib'
url = c.retrieve('reanalysis-era5-single-levels-monthly-means',
                 params,
                 path,
                )

Then there's the data pipeline:

import xarray as xr
import numpy as np
# Also need cfgrib library.

ds = xr.load_dataset(path, engine='cfgrib')
wind_abs = np.sqrt(ds.u100**2 + ds.v100**2)
monthly_means = wind_abs.mean(dim='time')
wind_abs_clim = (wind_abs.sel(time=slice('2006-01','2020-12'))
                         .groupby('time.month')
                         .mean(dim='time'))
wind_abs_anom = ((wind_abs.groupby('time.month') / wind_abs_clim)) - 1

Finally the plotting:

import cartopy.crs as ccrs
import matplotlib.pyplot as plt

cbar_kwargs = {'orientation':'horizontal', 'shrink':0.6, 'aspect':40, 'label':'Percent Deviation'}
subplot_kws = {'projection': ccrs.Mercator()}
fg = (wind_abs_anom.sel(time=slice('2021-01', None))
                   .groupby('time.month')
                   .mean(dim='time')
                   .plot(col='month',
                         col_wrap=3,
                         transform=ccrs.PlateCarree(),
                         cmap='RdBu_r', vmin=-3, vmax=3,  # <-- New bit.
                         cbar_kwargs=cbar_kwargs,
                         robust=False,
                         subplot_kws=subplot_kws
                        ))
fg.map(lambda: plt.gca().coastlines())

Sometimes I'll use a percentile to control the values for vmin and vmax automatically, like max_ = np.percentile(data, 99), then vmin=-max_, vmax=max_. This deals nicely with outliers that stretch the colormap, but it requires you to be able to calculate those values before making the plot.

If you want to start having more control over the plot, it might be a good idea to stop using the xarray plotting interface and use matplotlib and cartopy directly. Here's what that might look like (replacing all of the plotting code above):

import cartopy.crs as ccrs
import matplotlib.pyplot as plt

sel = wind_abs_anom.sel(time=slice('2021-01', None))

left, *_, right = wind_abs_anom.longitude
top, *_, bottom = wind_abs_anom.latitude  # Min and max latitude.
extent = [left, right, bottom, top]

fig, axs = plt.subplots(nrows=2, ncols=3,
                        figsize=(15, 6),
                        subplot_kw={'projection': ccrs.PlateCarree()},
                       )

for ax, (month, group) in zip(axs.flat, sel.groupby('time.month')):
    mean = group.mean(dim='time')
    im = ax.imshow(mean,
                   transform=ccrs.PlateCarree(),
                   extent=extent,
                   cmap='RdBu_r', vmin=-3, vmax=3)
    ax.set_title(f'month = {month}')
    ax.coastlines()

cbar_ax = fig.add_axes([0.2, 0.0, 0.6, 0.05])  # Left, bottom, width, height.
cbar = fig.colorbar(im, cax=cbar_ax, extend='both', orientation='horizontal')
cbar.set_label('Percent deviation')
    
plt.show()

For some reason, when I try to use ccra.Mercator() for the map, the data gets distorted; maybe you can figure that bit out.

like image 145
Matt Hall Avatar answered Oct 21 '25 22:10

Matt Hall



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!