Compare data with different calendars#

Claire Carouge, CLEX CMS

Recently, someone contacted us for help to compute an ensemble mean over CMIP5 outputs from different models. He was looking at future projections which means the dates are outside the range covered by datetime64[ns] format. Additionally, neither model used the real calendar, one is using a 360 days calendar, the other is using a No Leap Year calendar. In that case, Xarray will default to using cftime objects to represent the dates using the calendar provided in the file.

Because of the way CMIP5 experiments are designed, in addition to using different calendars, both models also covered a slightly different time period. All this means, the data has to be manually aligned in time before we can average across the model dimension. This cannot be done automatically by Xarray.

When dealing with different calendars, the way to align the dates depends on what you are interested in. In our case, we didn’t care about the exact length of the month in days, the value for February 2100 represents the same month whether this month is 28 days in the No Leap calendar or 30 days in the 360 days calendar. This means we have a relatively simple problem. We don’t need to worry about weighting of the data when changing calendar, or masking data out. Masking data or weighting data would be necessary for changing daily data from a No Leap calendar (365 values per year) to a 360 day calendar (only 360 values per year). Deciding which technique to use would be for the researcher to decide depending on what would make sense scientifically.

For our simpler problem, we decided we could “cut” the precision of the date to the month and move all the dates to the same calendar. We are going to describe in this blog how this was done on a very simple example.

import xarray as xr
import cftime
%matplotlib inline

Inputs#

#HADGEM and BCC
filename=[ \
          '/g/data1b/al33/replicas/CMIP5/combined/MOHC/HadGEM2-ES/rcp85/mon/atmos/Amon/r1i1p1/v20130430/pr/'+'pr_Amon_HadGEM2-ES_rcp85_r1i1p1_227412-229911.nc', \
          '/g/data1b/al33/replicas/CMIP5/combined/BCC/bcc-csm1-1/rcp85/mon/atmos/Amon/r1i1p1/v20120705/pr/'+'pr_Amon_bcc-csm1-1_rcp85_r1i1p1_210001-230012.nc'   \
         ]

We’ll read the files and keep all the data in a list called var_list. We don’t need anything fancier than a list because we are keeping all the metadata we need to identify our data with the DataArray itself. You could choose to keep the data in a dictionary instead and use the model_id as your dictionary keys. This would allow you to select your data using the model name. If you do so, I would still advise to add the model_id (or any other identifying data) with the array itself using coordinates, as it is done below. This keeps the data self-describing which is the point of the Xarray and netCDF philosophies.

var_list = []
for ffile in filename:
    ds = xr.open_dataset(ffile)
    # Let's grab the model name from the attributes and add as a coordinate. 
    # That will help with doing averages across the mode dimension. 
    # Additionally, the model_id information is kept when looking at the variable itself 
    # when using a coordinate while the global attribute is lost.
    ds = ds.assign_coords({'model_id':ds.model_id})
    var_list.append(ds['pr'])
print(var_list[0],'\n',var_list[1])
<xarray.DataArray 'pr' (time: 300, lat: 145, lon: 192)>
[8352000 values with dtype=float32]
Coordinates:
  * time      (time) object 2274-12-16 00:00:00 ... 2299-11-16 00:00:00
  * lat       (lat) float64 -90.0 -88.75 -87.5 -86.25 ... 86.25 87.5 88.75 90.0
  * lon       (lon) float64 0.0 1.875 3.75 5.625 7.5 ... 352.5 354.4 356.2 358.1
    model_id  <U10 'HadGEM2-ES'
Attributes:
    standard_name:     precipitation_flux
    long_name:         Precipitation
    comment:           at surface; includes both liquid and solid phases from...
    units:             kg m-2 s-1
    original_name:     mo: m01s05i216
    history:           2011-11-24T09:57:29 out-of-bounds adjustments: (-1e-07...
    cell_methods:      time: mean
    cell_measures:     area: areacella
    associated_files:  baseURL: http://cmip-pcmdi.llnl.gov/CMIP5/dataLocation... 
 <xarray.DataArray 'pr' (time: 2412, lat: 64, lon: 128)>
[19759104 values with dtype=float32]
Coordinates:
  * time      (time) object 2100-01-16 12:00:00 ... 2300-12-16 12:00:00
  * lat       (lat) float64 -87.86 -85.1 -82.31 -79.53 ... 82.31 85.1 87.86
  * lon       (lon) float64 0.0 2.812 5.625 8.438 ... 348.8 351.6 354.4 357.2
    model_id  <U10 'bcc-csm1-1'
Attributes:
    standard_name:     precipitation_flux
    long_name:         Precipitation
    comment:           at surface; includes both liquid and solid phases from...
    units:             kg m-2 s-1
    original_name:     PRECC+PRECL
    cell_methods:      time: mean (interval: 20 mintues)
    cell_measures:     area: areacella
    associated_files:  baseURL: http://cmip-pcmdi.llnl.gov/CMIP5/dataLocation...

Time coordinates#

We can see those models are quite different. Different grids, different time periods. But there is an additional difference which isn’t obvious with this printing and it is the calendars. Let’s have a closer look at the time coordinates:

var_list[0].time
<xarray.DataArray 'time' (time: 300)>
array([cftime.Datetime360Day(2274, 12, 16, 0, 0, 0, 0, 6, 346),
       cftime.Datetime360Day(2275, 1, 16, 0, 0, 0, 0, 1, 16),
       cftime.Datetime360Day(2275, 2, 16, 0, 0, 0, 0, 3, 46), ...,
       cftime.Datetime360Day(2299, 9, 16, 0, 0, 0, 0, 5, 256),
       cftime.Datetime360Day(2299, 10, 16, 0, 0, 0, 0, 0, 286),
       cftime.Datetime360Day(2299, 11, 16, 0, 0, 0, 0, 2, 316)], dtype=object)
Coordinates:
  * time      (time) object 2274-12-16 00:00:00 ... 2299-11-16 00:00:00
    model_id  <U10 'HadGEM2-ES'
Attributes:
    bounds:         time_bnds
    axis:           T
    long_name:      time
    standard_name:  time
var_list[1].time
<xarray.DataArray 'time' (time: 2412)>
array([cftime.DatetimeNoLeap(2100, 1, 16, 12, 0, 0, 0, 1, 16),
       cftime.DatetimeNoLeap(2100, 2, 15, 0, 0, 0, 0, 3, 46),
       cftime.DatetimeNoLeap(2100, 3, 16, 12, 0, 0, 0, 4, 75), ...,
       cftime.DatetimeNoLeap(2300, 10, 16, 12, 0, 0, 0, 5, 289),
       cftime.DatetimeNoLeap(2300, 11, 16, 0, 0, 0, 0, 1, 320),
       cftime.DatetimeNoLeap(2300, 12, 16, 12, 0, 0, 0, 3, 350)], dtype=object)
Coordinates:
  * time      (time) object 2100-01-16 12:00:00 ... 2300-12-16 12:00:00
    model_id  <U10 'bcc-csm1-1'
Attributes:
    bounds:         time_bnds
    axis:           T
    long_name:      time
    standard_name:  time

We can see the time coordinates aren’t datetime64[ns] objects as is usually the case for DataArray objects. This is because the models don’t use the Gregorian calendar. In that case, Xarray defaults to cftime objects. But then, the objects are different depending on the calendar used in the file. As you can see HadGEM2-ES is using a 360_day calendar while BCC_CSM1.1 is using a NoLeap calendar.

Having 2 calendars means months don’t have the same length in both models. In our case, it doesn’t matter as we don’t care for data at a finer scale than the month. We also don’t want to weight the data in any way by the length of the month. This also means we do not care about the day, hour etc. of the date. Whether the date is 2100/02/15 12:00:00 or 2100/02/27 00:00:00, both dates would represent the same data.

Spatial grid mismatch#

These 2 models use different spatial grids. You cannot compare them at the grid cell level while on different grids. One would have to interpolate the data to a common grid before doing that. Xarray provides the interp() method to deal with that for example.

Here we are going to keep things simple and simply look at a Northern Hemisphere average so that we remove the spatial dimension altogether.

for ii in range(len(var_list)):
    var_list[ii] = var_list[ii].sel(lat=[0., 90.],method='nearest').mean(dim=['lon','lat'])
print(var_list[0],'\n',var_list[1])
<xarray.DataArray 'pr' (time: 300)>
array([3.64325097e-05, 4.27831728e-05, 3.92413967e-05, 4.56348062e-05,
       6.77763601e-05, 7.60465846e-05, 5.75202830e-05, 3.32962081e-05,
       3.24944958e-05, 2.62415069e-05, 3.26726295e-05, 3.26325171e-05,
       3.14639801e-05, 3.01010750e-05, 1.83014763e-05, 2.17203506e-05,
       4.41498742e-05, 6.74383700e-05, 5.94772682e-05, 4.54635920e-05,
       4.01743928e-05, 2.49527602e-05, 1.79039071e-05, 3.73657967e-05,
       4.09420027e-05, 3.00161100e-05, 2.47476764e-05, 2.80293734e-05,
       5.12982551e-05, 6.39619902e-05, 7.37399969e-05, 5.08287230e-05,
       3.63605941e-05, 1.45873200e-05, 2.69833417e-05, 2.63794154e-05,
       3.28406932e-05, 5.15875618e-05, 4.20846372e-05, 4.35442489e-05,
       4.25339240e-05, 5.28369601e-05, 6.19484199e-05, 3.61547754e-05,
       2.46096697e-05, 2.87036473e-05, 3.83880979e-05, 2.82257261e-05,
       3.83581246e-05, 3.57428253e-05, 2.66543047e-05, 3.79485609e-05,
       4.66462188e-05, 4.64161421e-05, 6.37636913e-05, 4.56774142e-05,
       2.72985562e-05, 1.47741275e-05, 2.09127593e-05, 3.68037108e-05,
       4.38679162e-05, 4.66758975e-05, 4.11564579e-05, 3.46690977e-05,
       5.10619138e-05, 6.95752678e-05, 6.34654280e-05, 4.57632887e-05,
       4.36050141e-05, 2.77569925e-05, 2.74525282e-05, 2.63740203e-05,
       4.90272760e-05, 4.01253383e-05, 2.88075807e-05, 3.69191184e-05,
       4.60569572e-05, 6.93620023e-05, 6.78801371e-05, 5.61330344e-05,
       4.08653905e-05, 3.88063636e-05, 2.64765476e-05, 3.19188839e-05,
       3.65671476e-05, 4.53115172e-05, 2.84333491e-05, 2.98032955e-05,
       4.76434361e-05, 6.81091988e-05, 6.42125015e-05, 4.02445876e-05,
       2.55941104e-05, 3.22429587e-05, 3.00236643e-05, 3.21069856e-05,
       3.98891825e-05, 4.96542816e-05, 2.04113730e-05, 2.24496325e-05,
       4.50844273e-05, 7.12071924e-05, 6.22269145e-05, 3.54138210e-05,
       3.18072707e-05, 1.90198753e-05, 1.92276184e-05, 2.27522833e-05,
       3.45489207e-05, 3.91517206e-05, 2.50352768e-05, 3.71644055e-05,
       4.09444910e-05, 4.97525507e-05, 5.23429881e-05, 6.00500389e-05,
       2.92963523e-05, 2.34099662e-05, 3.11563163e-05, 2.99090407e-05,
       4.01829711e-05, 2.91730976e-05, 3.02629687e-05, 4.31793851e-05,
       5.69406257e-05, 6.66533961e-05, 5.95321289e-05, 4.21358964e-05,
       3.53066534e-05, 2.64745322e-05, 2.07611738e-05, 2.81327011e-05,
       4.93523294e-05, 4.24605787e-05, 3.27603011e-05, 4.41637821e-05,
       5.38483109e-05, 6.30520153e-05, 5.58377833e-05, 4.18616219e-05,
       4.21365985e-05, 2.02145875e-05, 2.42206061e-05, 3.22632513e-05,
       2.76324990e-05, 2.14536922e-05, 2.13802778e-05, 3.33160642e-05,
       4.01660036e-05, 5.31559963e-05, 5.74125479e-05, 6.72948881e-05,
       3.95660572e-05, 2.64328264e-05, 2.41173620e-05, 2.74826943e-05,
       3.28176939e-05, 4.18594973e-05, 3.08873423e-05, 2.77997697e-05,
       4.34308931e-05, 7.09996311e-05, 6.70235022e-05, 4.49882100e-05,
       4.14250935e-05, 2.34740146e-05, 3.16928708e-05, 2.77652416e-05,
       4.19034332e-05, 4.53636203e-05, 2.47627231e-05, 2.28933441e-05,
       5.63285612e-05, 6.65051120e-05, 6.35059769e-05, 3.94946001e-05,
       2.70298133e-05, 2.41716389e-05, 2.37215354e-05, 2.39575838e-05,
       3.68956898e-05, 4.48219798e-05, 4.52555796e-05, 4.82457508e-05,
       5.16491309e-05, 5.22416194e-05, 5.16474538e-05, 3.66467902e-05,
       3.00525862e-05, 2.17982797e-05, 2.68968561e-05, 3.08575509e-05,
       3.30942494e-05, 2.82415986e-05, 3.58913676e-05, 3.83007900e-05,
       5.71281998e-05, 5.06764627e-05, 6.97621508e-05, 3.97482363e-05,
       2.84589569e-05, 3.98614320e-05, 2.47247972e-05, 3.41566520e-05,
       3.84891573e-05, 4.18647978e-05, 3.10019932e-05, 2.82894835e-05,
       4.20928955e-05, 6.12278745e-05, 5.41136542e-05, 4.81716015e-05,
       4.32904053e-05, 2.68176373e-05, 3.05247850e-05, 3.09426578e-05,
       5.21169459e-05, 5.03541924e-05, 3.87022774e-05, 5.21392212e-05,
       5.18711786e-05, 6.28749985e-05, 5.41823283e-05, 5.19385976e-05,
       3.05940011e-05, 2.16088174e-05, 3.30646835e-05, 2.88995889e-05,
       4.31572807e-05, 3.58428879e-05, 2.46742329e-05, 3.11370859e-05,
       4.36105365e-05, 6.55327240e-05, 7.07324361e-05, 5.10462596e-05,
       3.02958233e-05, 1.94129261e-05, 2.38559060e-05, 2.31218874e-05,
       3.82592443e-05, 3.21088919e-05, 3.35581535e-05, 2.44721214e-05,
       3.53916366e-05, 6.52056988e-05, 5.36810730e-05, 5.30228135e-05,
       3.45361223e-05, 2.21900700e-05, 2.36556498e-05, 1.96207639e-05,
       3.45765511e-05, 4.55885456e-05, 2.95403515e-05, 2.70133369e-05,
       3.90020505e-05, 5.51467347e-05, 5.46019146e-05, 3.65035594e-05,
       3.21434491e-05, 2.43737977e-05, 2.86705417e-05, 2.66214865e-05,
       4.36339797e-05, 3.60268132e-05, 3.00881638e-05, 4.35306283e-05,
       5.61130400e-05, 7.42721750e-05, 6.80996891e-05, 3.75807103e-05,
       3.38666614e-05, 3.41925661e-05, 2.94617275e-05, 3.45140616e-05,
       3.81903556e-05, 4.68480248e-05, 2.72760735e-05, 3.36759804e-05,
       5.02384646e-05, 8.06452663e-05, 6.21316722e-05, 4.41266056e-05,
       3.04287478e-05, 2.62973190e-05, 2.64842383e-05, 3.26729569e-05,
       4.52012209e-05, 4.85079363e-05, 3.67452449e-05, 3.40536790e-05,
       3.67230241e-05, 7.03531623e-05, 6.69856599e-05, 4.26577899e-05,
       3.21605548e-05, 2.25631738e-05, 3.23108943e-05, 2.39344427e-05],
      dtype=float32)
Coordinates:
  * time      (time) object 2274-12-16 00:00:00 ... 2299-11-16 00:00:00
    model_id  <U10 'HadGEM2-ES' 
 <xarray.DataArray 'pr' (time: 2412)>
array([2.7216340e-05, 2.1928223e-05, 2.5535373e-05, ..., 4.2512460e-05,
       4.3559990e-05, 4.7103615e-05], dtype=float32)
Coordinates:
  * time      (time) object 2100-01-16 12:00:00 ... 2300-12-16 12:00:00
    model_id  <U10 'bcc-csm1-1'

Align the times#

Finally, we can deal with the time coordinates. What we’ll do is put all the dates on a 360 day calendar for the middle of the month. Since all months have 30 days in a 360 day calendar, the mid-month mark is on 16th 00:00:00. There is no automatic way to change from one calendar to another because the way you want to do it depends on why you are doing it. Here we are going to get the year and month for each date and put them as arguments of the cftime.Datetime360Day() function which sets the date for the 360 day calendar.

def to_360day_monthly(da):
    '''Takes a DataArray. Change the 
    calendar to 360_day and precision to monthly.'''
    val = da.copy()
    time1 = da.time.copy()
    for itime in range(val.sizes['time']):
        bb = val.time.values[itime].timetuple()
        time1.values[itime] = cftime.Datetime360Day(bb[0],bb[1],16)

    # We rename the time dimension and coordinate to time360 to make it clear it isn't 
    # the original time coordinate.
    val = val.rename({'time':'time360'})
    time1 = time1.rename({'time':'time360'})
    val = val.assign_coords({'time360':time1})
    return val
ll = [to_360day_monthly(da) for da in var_list]
print(ll[0].time360)
print(ll[1].time360)
<xarray.DataArray 'time360' (time360: 300)>
array([cftime.Datetime360Day(2274, 12, 16, 0, 0, 0, 0, 6, 346),
       cftime.Datetime360Day(2275, 1, 16, 0, 0, 0, 0, 1, 16),
       cftime.Datetime360Day(2275, 2, 16, 0, 0, 0, 0, 3, 46), ...,
       cftime.Datetime360Day(2299, 9, 16, 0, 0, 0, 0, 5, 256),
       cftime.Datetime360Day(2299, 10, 16, 0, 0, 0, 0, 0, 286),
       cftime.Datetime360Day(2299, 11, 16, 0, 0, 0, 0, 2, 316)], dtype=object)
Coordinates:
  * time360   (time360) object 2274-12-16 00:00:00 ... 2299-11-16 00:00:00
    model_id  <U10 'HadGEM2-ES'
Attributes:
    bounds:         time_bnds
    axis:           T
    long_name:      time
    standard_name:  time
<xarray.DataArray 'time360' (time360: 2412)>
array([cftime.Datetime360Day(2100, 1, 16, 0, 0, 0, 0, 1, 16),
       cftime.Datetime360Day(2100, 2, 16, 0, 0, 0, 0, 3, 46),
       cftime.Datetime360Day(2100, 3, 16, 0, 0, 0, 0, 5, 76), ...,
       cftime.Datetime360Day(2300, 10, 16, 0, 0, 0, 0, 3, 286),
       cftime.Datetime360Day(2300, 11, 16, 0, 0, 0, 0, 5, 316),
       cftime.Datetime360Day(2300, 12, 16, 0, 0, 0, 0, 0, 346)], dtype=object)
Coordinates:
  * time360   (time360) object 2100-01-16 00:00:00 ... 2300-12-16 00:00:00
    model_id  <U10 'bcc-csm1-1'
Attributes:
    bounds:         time_bnds
    axis:           T
    long_name:      time
    standard_name:  time

We now have the same calendar and all dates refer to the middle of the month. We can concatenate the arrays together with xarray.concat() and xarray will align the arrays correctly. Make sure the dates that are common to both models are the same, otherwise Xarray might keep both sets. This would result in additional times in both models filled with NaN. Additionally, the data from both models would never be considered as having the same time and as such you wouldn’t be able to do a mean.

First, we are going to concatenate the data using the union of the time periods covered by both models

full_da = xr.concat(ll, 'model_id', join='outer')
# When plotting the data, you can see the HadGEM2-ES data is 
# correctly placed at the end of the timeseries.
full_da.plot(hue='model_id')
[<matplotlib.lines.Line2D at 0x7fdeb4083f28>,
 <matplotlib.lines.Line2D at 0x7fdeb4091198>]
../_images/863aee88e02ab6512bd9fff4ed316e5afb32bdf3343b8994caaef0813fe94064.png
full_da
<xarray.DataArray 'pr' (model_id: 2, time360: 2412)>
array([[          nan,           nan,           nan, ...,           nan,
                  nan,           nan],
       [2.7216340e-05, 2.1928223e-05, 2.5535373e-05, ..., 4.2512460e-05,
        4.3559990e-05, 4.7103615e-05]], dtype=float32)
Coordinates:
  * time360   (time360) object 2100-01-16 00:00:00 ... 2300-12-16 00:00:00
  * model_id  (model_id) object 'HadGEM2-ES' 'bcc-csm1-1'
# Calculating the model mean is then trivial
full_da.mean(dim='model_id').plot()
[<matplotlib.lines.Line2D at 0x7fdea9dad8d0>]
../_images/d31c96dbb472a1a2037e7ac563670db0516719fd34f91bd516f966647f4b3c38.png

Obviously, you may want to keep only the dates when you have data from all models. For this you simply change the join argument of concat:

inter_da = xr.concat(ll,'model_id',join='inner')
inter_da.plot(hue='model_id')
[<matplotlib.lines.Line2D at 0x7fdea9d08e48>,
 <matplotlib.lines.Line2D at 0x7fdea9d08eb8>]
../_images/9ef6509c1ef1bc6f54ba2719f5ee317edc57ea17ef26df64dea681aa380be3f4.png
inter_da.mean(dim='model_id').plot()
[<matplotlib.lines.Line2D at 0x7fdea9cefb00>]
../_images/e0c07fcbd11c084bd23b8a7fefcc2055c512507bac2fe65a9bad3d25567f91b5.png

Keep the original dates#

A potential issue with the above method is we loose the original dates associated with the data. Here is a solution to keep both time coordinates.

def to_360day_monthly_both(da):
    '''Takes a DataArray. Change the 
    calendar to 360_day and precision to monthly.'''
    val = da.copy()
    time1 = da.time.copy()
    for itime in range(val.sizes['time']):
        bb = val.time.values[itime].timetuple()
        time1.values[itime] = cftime.Datetime360Day(bb[0],bb[1],16)

    # Instead of renaming the time dimension and coordinate. We first add the
    # new coordinate with the time360 name, then we swap the dimension. 
    # This tells Xarray to now use time360 as a dimension and coordinate for this
    # array.
    val = val.assign_coords({'time360':time1})
    val = val.swap_dims({'time':'time360'})
    return val
ll = [to_360day_monthly_both(da) for da in var_list]
print(ll[0].time360)
print(ll[1].time360)
<xarray.DataArray 'time360' (time360: 300)>
array([cftime.Datetime360Day(2274, 12, 16, 0, 0, 0, 0, 6, 346),
       cftime.Datetime360Day(2275, 1, 16, 0, 0, 0, 0, 1, 16),
       cftime.Datetime360Day(2275, 2, 16, 0, 0, 0, 0, 3, 46), ...,
       cftime.Datetime360Day(2299, 9, 16, 0, 0, 0, 0, 5, 256),
       cftime.Datetime360Day(2299, 10, 16, 0, 0, 0, 0, 0, 286),
       cftime.Datetime360Day(2299, 11, 16, 0, 0, 0, 0, 2, 316)], dtype=object)
Coordinates:
    time      (time360) object 2274-12-16 00:00:00 ... 2299-11-16 00:00:00
    model_id  <U10 'HadGEM2-ES'
  * time360   (time360) object 2274-12-16 00:00:00 ... 2299-11-16 00:00:00
Attributes:
    bounds:         time_bnds
    axis:           T
    long_name:      time
    standard_name:  time
<xarray.DataArray 'time360' (time360: 2412)>
array([cftime.Datetime360Day(2100, 1, 16, 0, 0, 0, 0, 1, 16),
       cftime.Datetime360Day(2100, 2, 16, 0, 0, 0, 0, 3, 46),
       cftime.Datetime360Day(2100, 3, 16, 0, 0, 0, 0, 5, 76), ...,
       cftime.Datetime360Day(2300, 10, 16, 0, 0, 0, 0, 3, 286),
       cftime.Datetime360Day(2300, 11, 16, 0, 0, 0, 0, 5, 316),
       cftime.Datetime360Day(2300, 12, 16, 0, 0, 0, 0, 0, 346)], dtype=object)
Coordinates:
    time      (time360) object 2100-01-16 12:00:00 ... 2300-12-16 12:00:00
    model_id  <U10 'bcc-csm1-1'
  * time360   (time360) object 2100-01-16 00:00:00 ... 2300-12-16 00:00:00
Attributes:
    bounds:         time_bnds
    axis:           T
    long_name:      time
    standard_name:  time
# Look at what happens to the time coordinate after the concatenation:
full_da = xr.concat(ll, 'model_id', join='outer')
full_da
<xarray.DataArray 'pr' (model_id: 2, time360: 2412)>
array([[          nan,           nan,           nan, ...,           nan,
                  nan,           nan],
       [2.7216340e-05, 2.1928223e-05, 2.5535373e-05, ..., 4.2512460e-05,
        4.3559990e-05, 4.7103615e-05]], dtype=float32)
Coordinates:
  * time360   (time360) object 2100-01-16 00:00:00 ... 2300-12-16 00:00:00
    time      (model_id, time360) object nan nan ... 2300-12-16 12:00:00
  * model_id  (model_id) object 'HadGEM2-ES' 'bcc-csm1-1'

The time coordinate only has values where the original data had a date.

full_da.time.sel(model_id='HadGEM2-ES',time360=slice('2273-01','2275-01'))
<xarray.DataArray 'time' (time360: 25)>
array([nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
       nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
       cftime.Datetime360Day(2274, 12, 16, 0, 0, 0, 0, 6, 346),
       cftime.Datetime360Day(2275, 1, 16, 0, 0, 0, 0, 1, 16)],
      dtype=object)
Coordinates:
  * time360   (time360) object 2273-01-16 00:00:00 ... 2275-01-16 00:00:00
    time      (time360) object nan nan ... 2275-01-16 00:00:00
    model_id  <U10 'HadGEM2-ES'
Attributes:
    bounds:         time_bnds
    axis:           T
    long_name:      time
    standard_name:  time
full_da.plot(hue='model_id')
/g/data3/hh5/public/apps/miniconda3/envs/analysis3-19.07/lib/python3.6/site-packages/xarray/plot/plot.py:86: FutureWarning: This DataArray contains multi-dimensional coordinates. In the future, these coordinates will be transposed as well unless you specify transpose_coords=False.
  yplt = darray.transpose(xname, huename)
[<matplotlib.lines.Line2D at 0x7fdea9c06710>,
 <matplotlib.lines.Line2D at 0x7fdea9c06780>]
../_images/863aee88e02ab6512bd9fff4ed316e5afb32bdf3343b8994caaef0813fe94064.png
full_da.mean(dim='model_id')
<xarray.DataArray 'pr' (time360: 2412)>
array([2.7216340e-05, 2.1928223e-05, 2.5535373e-05, ..., 4.2512460e-05,
       4.3559990e-05, 4.7103615e-05], dtype=float32)
Coordinates:
  * time360  (time360) object 2100-01-16 00:00:00 ... 2300-12-16 00:00:00
full_da.mean(dim='model_id').plot()
[<matplotlib.lines.Line2D at 0x7fdea9bf77f0>]
../_images/d31c96dbb472a1a2037e7ac563670db0516719fd34f91bd516f966647f4b3c38.png

Conclusion#

We presented here two simple ways to change calendar for monthly data. Feel free to use either of the functions presented here. Please, remember this solution works well for the problem presented here. Other problems may require different approaches.