After this introduction, you will know how to:
GriddedData and UngriddedData)ColocatedData)pandas or xarray
import pyaerocom as pya
import matplotlib.pyplot as plt
pya.change_verbosity('critical', log=pya.const.print_log) # don't output warnings
pya.__version__
'0.8.1.dev1'
Please make sure to use version 0.8.1.dev1. If you have an earlier version, follow these instructions in order to update your installation.
import socket
if socket.gethostname() == 'pc4971':
    print('I am on Jonas PC')
    DATA_BASEDIR = '/home/jonasg/MyPyaerocom/pyaerocom-testdata'
else:
    print('I assume I am on the Abisko Jupyter hub')
    DATA_BASEDIR = '/home/notebook/shared-ns1000k/inputs/pyaerocom-testdata/'
I am on Jonas PC
pya.const.BASEDIR = DATA_BASEDIR
browse_database)¶pya.browse_database('*TM5*')
Pyaerocom ReadGridded --------------------- Data ID: TM5_AP3-CTRL2919 Data directory: /home/jonasg/MyPyaerocom/pyaerocom-testdata/modeldata/TM5_AP3-CTRL2919/renamed Available experiments: ['AP3-CTRL2019'] Available years: [2010] Available frequencies ['monthly'] Available variables: ['abs350aer', 'abs440aer', 'abs440dryaer', 'abs550aer', 'abs550dryaer', 'abs550drylt1aer', 'abs870aer', 'abs870dryaer', 'airmass', 'asyaer', 'asydryaer', 'depbc', 'depdms', 'depdust', 'dephno3', 'depmsa', 'depn', 'depnh3', 'depnh4', 'depnhx', 'depno2', 'depno3', 'depnoy', 'depo3', 'depoa', 'deps', 'depso2', 'depso4', 'depss', 'dh', 'drybc', 'drydms', 'drydust', 'dryhno3', 'drynh3', 'dryno2', 'dryno3', 'drynoy', 'dryo3', 'dryoa', 'dryso2', 'dryso4', 'dryss', 'ec440dryaer', 'ec550aer', 'ec550dryaer', 'ec550drylt1aer', 'ec870dryaer', 'emibc', 'emico', 'emidms', 'emidust', 'emiisop', 'emin', 'eminh3', 'eminox', 'emioa', 'emis', 'emiso2', 'emiso4', 'emiss', 'emiterp', 'emivoc', 'hus', 'loadbc', 'loaddust', 'loadno3', 'loadoa', 'loadso4', 'loadss', 'od350aer', 'od440aer', 'od550aer', 'od550aerh2o', 'od550bc', 'od550dust', 'od550lt1aer', 'od550lt1dust', 'od550lt1ss', 'od550no3', 'od550oa', 'od550so4', 'od550ss', 'od870aer', 'pr', 'sconcbc', 'sconcdust', 'sconcmsa', 'sconcnh4', 'sconcno3', 'sconcoa', 'sconcso4', 'sconcss', 'ta', 'temp', 'vmrch4', 'vmrco', 'vmrno', 'vmrno2', 'vmro3', 'vmroh', 'wetbc', 'wetdms', 'wetdust', 'wethno3', 'wetmsa', 'wetnh3', 'wetnh4', 'wetno3', 'wetnoy', 'wetoa', 'wetso2', 'wetso4', 'wetss']
You can use the Data ID or the Data directory to read this dataset (next step)
ReadGridded class)¶DATA_ID = 'TM5_AP3-CTRL2919'
reader = pya.io.ReadGridded(DATA_ID)
print(reader)
Pyaerocom ReadGridded --------------------- Data ID: TM5_AP3-CTRL2919 Data directory: /home/jonasg/MyPyaerocom/pyaerocom-testdata/modeldata/TM5_AP3-CTRL2919/renamed Available experiments: ['AP3-CTRL2019'] Available years: [2010] Available frequencies ['monthly'] Available variables: ['abs350aer', 'abs440aer', 'abs440dryaer', 'abs550aer', 'abs550dryaer', 'abs550drylt1aer', 'abs870aer', 'abs870dryaer', 'airmass', 'asyaer', 'asydryaer', 'depbc', 'depdms', 'depdust', 'dephno3', 'depmsa', 'depn', 'depnh3', 'depnh4', 'depnhx', 'depno2', 'depno3', 'depnoy', 'depo3', 'depoa', 'deps', 'depso2', 'depso4', 'depss', 'dh', 'drybc', 'drydms', 'drydust', 'dryhno3', 'drynh3', 'dryno2', 'dryno3', 'drynoy', 'dryo3', 'dryoa', 'dryso2', 'dryso4', 'dryss', 'ec440dryaer', 'ec550aer', 'ec550dryaer', 'ec550drylt1aer', 'ec870dryaer', 'emibc', 'emico', 'emidms', 'emidust', 'emiisop', 'emin', 'eminh3', 'eminox', 'emioa', 'emis', 'emiso2', 'emiso4', 'emiss', 'emiterp', 'emivoc', 'hus', 'loadbc', 'loaddust', 'loadno3', 'loadoa', 'loadso4', 'loadss', 'od350aer', 'od440aer', 'od550aer', 'od550aerh2o', 'od550bc', 'od550dust', 'od550lt1aer', 'od550lt1dust', 'od550lt1ss', 'od550no3', 'od550oa', 'od550so4', 'od550ss', 'od870aer', 'pr', 'sconcbc', 'sconcdust', 'sconcmsa', 'sconcnh4', 'sconcno3', 'sconcoa', 'sconcso4', 'sconcss', 'ta', 'temp', 'vmrch4', 'vmrco', 'vmrno', 'vmrno2', 'vmro3', 'vmroh', 'wetbc', 'wetdms', 'wetdust', 'wethno3', 'wetmsa', 'wetnh3', 'wetnh4', 'wetno3', 'wetnoy', 'wetoa', 'wetso2', 'wetso4', 'wetss']
pya.get_variable('od550aer')
od550aer standard_name: atmosphere_optical_thickness_due_to_ambient_aerosol_particles; Unit: 1
model_data = reader.read_var('od550aer', start=2010)
type(model_data)
pyaerocom.griddeddata.GriddedData
GriddedData object¶model_data.ts_type # temporal resolution
'monthly'
model_data.quickplot_map('June 2010');
GriddedData to iris.Cube or xarray.DataArray¶Actually, the GriddedData object is based on the iris.Cube object which can be accessed via the cube attr:
cube = model_data.cube
type(cube)
iris.cube.Cube
cube
| Atmosphere Optical Thickness Due To Ambient Aerosol (1) | time | latitude | longitude | 
|---|---|---|---|
| Shape | 12 | 90 | 120 | 
| Dimension coordinates | |||
| time | x | - | - | 
| latitude | - | x | - | 
| longitude | - | - | x | 
| Attributes | |||
| Conventions | CF-1.6 | ||
| computed | False | ||
| concatenated | False | ||
| contact | Twan van Noije (noije@knmi.nl) | ||
| data_id | TM5_AP3-CTRL2919 | ||
| experiment_id | AP3-CTRL2019 | ||
| from_files | ['/home/jonasg/MyPyaerocom/pyaerocom-testdata/modeldata/TM5_AP3-CTRL29... | ||
| institute_id | KNMI | ||
| institution | Royal Netherlands Meteorological Institute, De Bilt, The Netherlands | ||
| model_id | TM5 | ||
| outliers_removed | False | ||
| project_id | AeroCom Phase 3 | ||
| reader | None | ||
| references | Van Noije, T.P.C., et al. (Geosci. Model Dev., 7, 2435-2475, 2014); Bergman,... | ||
| region | None | ||
| regridded | False | ||
| source | TM5-mp, r1058: CTM ERA-Interim 3x2 34L | ||
| title | TM5 model output prepared for AeroCom Phase 3 | ||
| ts_type | monthly | ||
| var_name_read | n/d | ||
| Cell methods | |||
| point | longitude, latitude | ||
| mean | time | ||
try: # pyaerocom >= 0.8.1
    data_arr = model_data.to_xarray()
except: # pyaerocom < 0.8.1
    print('Coming soon in pyaerocom v0.8.1')
    import xarray as xarr
    data_arr = xarr.DataArray.from_iris(model_data.cube)
data_arr
<xarray.DataArray 'od550aer' (time: 12, lat: 90, lon: 120)>
dask.array<shape=(12, 90, 120), dtype=float32, chunksize=(12, 90, 61)>
Coordinates:
  * time     (time) object 2010-01-15 12:00:00 ... 2010-12-15 12:00:00
  * lat      (lat) float64 -89.0 -87.0 -85.0 -83.0 -81.0 ... 83.0 85.0 87.0 89.0
  * lon      (lon) float64 -181.5 -178.5 -175.5 -172.5 ... 169.5 172.5 175.5
Attributes:
    standard_name:     atmosphere_optical_thickness_due_to_ambient_aerosol
    long_name:         Ambient Aerosol Optical Thickness at 550 nm
    institution:       Royal Netherlands Meteorological Institute, De Bilt, T...
    institute_id:      KNMI
    source:            TM5-mp, r1058: CTM ERA-Interim 3x2 34L
    model_id:          TM5
    references:        Van Noije, T.P.C., et al. (Geosci. Model Dev., 7, 2435...
    experiment_id:     AP3-CTRL2019
    project_id:        AeroCom Phase 3
    title:             TM5 model output prepared for AeroCom Phase 3
    Conventions:       CF-1.6
    contact:           Twan van Noije (noije@knmi.nl)
    from_files:        ['/home/jonasg/MyPyaerocom/pyaerocom-testdata/modeldat...
    data_id:           TM5_AP3-CTRL2919
    var_name_read:     n/d
    ts_type:           monthly
    regridded:         False
    outliers_removed:  False
    computed:          False
    concatenated:      False
    cell_methods:      longitude: latitude: point time: mean
GriddedData object that may be useful¶LON_ABISKO = 18.8312 #° E
LAT_ABISKO = 68.3495 #° N 
subset_abisko = model_data.sel(longitude=LON_ABISKO, latitude=LAT_ABISKO)
try: # pyaerocom >= 0.8.1
    subset_abisko.to_xarray().plot();
except: # pyaerocom < 0.8.1
    print('Coming soon in pyaerocom v0.8.1')
    import xarray as xarr
    data_arr = xarr.DataArray.from_iris(subset_abisko.cube).plot()
ReadUngridded class)¶
pya.browse_database('Aeronet*V3*Lev2*')
Dataset name: AeronetSunV3Lev2.daily
Data directory: /home/jonasg/MyPyaerocom/pyaerocom-testdata/obsdata/AeronetSunV3Lev2.0.daily/renamed
Supported variables: ['od340aer', 'od440aer', 'od500aer', 'od870aer', 'ang4487aer', 'ang4487aer_calc', 'od550aer']
Last revision: 20190920
Reading failed for AeronetSunV3Lev2.AP. Error: OSError('Data directory /home/jonasg/MyPyaerocom/pyaerocom-testdata/obsdata/AeronetSunV3Lev2.0.AP/renamed of observation network AeronetSunV3Lev2.AP does not exists')
Reading failed for AeronetSDAV3Lev2.daily. Error: OSError('Data directory /home/jonasg/MyPyaerocom/pyaerocom-testdata/obsdata/Aeronet.SDA.V3L2.0.daily/renamed of observation network AeronetSDAV3Lev2.daily does not exists')
Reading failed for AeronetSDAV3Lev2.AP. Error: NetworkNotImplemented('No reading class available yet for dataset AeronetSDAV3Lev2.AP')
Reading failed for AeronetInvV3Lev2.daily. Error: OSError('Data directory /home/jonasg/MyPyaerocom/pyaerocom-testdata/obsdata/Aeronet.Inv.V3L2.0.daily/renamed of observation network AeronetInvV3Lev2.daily does not exists')
Reading failed for AeronetSunV3Lev2.0.daily. Error: AttributeError("'NoneType' object has no attribute 'experiment'")
OBS_ID = 'AeronetSunV3Lev2.daily'
obs_reader = pya.io.ReadUngridded(OBS_ID)
print(obs_reader)
Dataset name: AeronetSunV3Lev2.daily Data directory: /home/jonasg/MyPyaerocom/pyaerocom-testdata/obsdata/AeronetSunV3Lev2.0.daily/renamed Supported variables: ['od340aer', 'od440aer', 'od500aer', 'od870aer', 'ang4487aer', 'ang4487aer_calc', 'od550aer'] Last revision: 20190920
obs_data = obs_reader.read(vars_to_retrieve='od550aer')
obs_data
UngriddedData <networks: ['AeronetSunV3Lev2.daily']; vars: ['od550aer']; instruments: ['sun_photometer'];No. of stations: 1230
UngriddedData object¶ax = obs_data.plot_station_coordinates(markersize=16)
ax = obs_data.plot_station_coordinates(var_name='od550aer', 
                                       start=2008, stop=2012, 
                                       markersize=12, color='lime', ax=ax)
len(obs_data.metadata)
1230
e.g. metadata of first file:
obs_data.metadata[0]
OrderedDict([('var_info',
              OrderedDict([('od550aer', OrderedDict([('units', '1')]))])),
             ('latitude', 45.3139),
             ('longitude', 12.508299999999998),
             ('altitude', 10.0),
             ('station_name', 'AAOT'),
             ('PI', 'Brent_Holben'),
             ('ts_type', 'daily'),
             ('data_id', 'AeronetSunV3Lev2.daily'),
             ('variables', ['od550aer']),
             ('instrument_name', 'sun_photometer'),
             ('data_revision', '20190920')])
obs_arctic = obs_data.apply_filters(latitude=[70, 90])
obs_arctic.plot_station_coordinates(markersize=40, marker='x', color='r');
lille_data = obs_data.to_station_data('*Lille*')
type(lille_data)
pyaerocom.stationdata.StationData
StationData¶. and [] to access and assign attributes)pandas.Series lille_data.keys()
odict_keys(['dtime', 'var_info', 'station_coords', 'data_err', 'overlap', 'data_flagged', 'filename', 'station_id', 'station_name', 'instrument_name', 'PI', 'country', 'ts_type', 'latitude', 'longitude', 'altitude', 'data_id', 'dataset_name', 'data_product', 'data_version', 'data_level', 'revision_date', 'website', 'ts_type_src', 'stat_merge_pref_attr', 'data_revision', 'od550aer'])
lille_data.longitude
3.1416669999999987
lille_data.latitude
50.611667000000004
pandas.Series simply by using the variable name¶lille_data.od550aer # THIS is exactly equivalent to the command lille_data['od550aer']
1995-06-30 12:00:00    0.403992
1995-07-01 12:00:00    0.695444
1995-07-04 12:00:00    0.213412
1995-07-07 12:00:00    0.757272
1995-07-08 12:00:00    1.072732
                         ...   
2019-06-16 12:00:00    0.061789
2019-06-17 12:00:00    0.100830
2019-06-18 12:00:00    0.130222
2019-06-20 12:00:00    0.046714
2019-06-21 12:00:00    0.074854
Length: 3399, dtype: float64
ax = lille_data.plot_timeseries(var_name='od550aer', marker='x', linestyle='none')
ax = lille_data.plot_timeseries(var_name='od550aer', ts_type='monthly', linestyle='-', lw=3, ax=ax)
col_data = pya.colocation.colocate_gridded_ungridded(model_data, 
                                                     obs_data, 
                                                     ts_type='monthly',
                                                     start=2010)
col_data
<xarray.DataArray 'od550aer' (data_source: 2, time: 12, station_name: 290)>
array([[[     nan, 0.117588, ...,      nan, 0.222138],
        [     nan, 0.132128, ...,      nan, 0.429762],
        ...,
        [0.132236, 0.195057, ...,      nan, 0.261765],
        [     nan,      nan, ...,      nan, 0.37905 ]],
       [[0.149478, 0.146562, ..., 0.037668, 0.230157],
        [0.145884, 0.286354, ..., 0.06611 , 0.464022],
        ...,
        [0.164331, 0.199312, ..., 0.035466, 0.405731],
        [0.104762, 0.138498, ..., 0.040375, 0.455539]]])
Coordinates:
  * data_source   (data_source) <U22 'AeronetSunV3Lev2.daily' 'TM5_AP3-CTRL2919'
    var_name      (data_source) <U8 'od550aer' 'od550aer'
    var_units     (data_source) <U1 '1' '1'
    ts_type_src   (data_source) <U7 'daily' 'monthly'
  * time          (time) datetime64[ns] 2010-01-01 2010-02-01 ... 2010-12-01
  * station_name  (station_name) <U19 'ARM_Darwin' ... 'Zinder_Airport'
    latitude      (station_name) float64 -12.43 37.97 15.35 ... 62.45 13.78
    longitude     (station_name) float64 130.9 23.72 -1.479 ... -114.4 8.99
    altitude      (station_name) float64 29.9 130.0 305.0 ... 300.0 220.8 456.0
Attributes:
    data_source:        ['AeronetSunV3Lev2.daily', 'TM5_AP3-CTRL2919']
    var_name:           ['od550aer', 'od550aer']
    ts_type:            monthly
    filter_name:        WORLD-wMOUNTAINS
    ts_type_src:        ['daily', 'monthly']
    start_str:          20100101
    stop_str:           20101231
    var_units:          ['1', '1']
    vert_scheme:        None
    data_level:         3
    revision_ref:       20190920
    from_files:         ['aerocom3_TM5_AP3-CTRL2019_od550aer_Column_2010_mont...
    from_files_ref:     None
    stations_ignored:   None
    colocate_time:      False
    apply_constraints:  True
    min_num_obs:        {'yearly': {'monthly': 3}, 'monthly': {'daily': 7}, '...
    outliers_removed:   True
    region:             WORLD
    lon_range:          [-180, 180]
    lat_range:          [-90, 90]
    alt_range:          None
col_data.plot_scatter(marker='o', color='blue', alpha=0.1);
xarray.DataArray object can be simply accessed via¶col_data.data
<xarray.DataArray 'od550aer' (data_source: 2, time: 12, station_name: 290)>
array([[[     nan, 0.117588, ...,      nan, 0.222138],
        [     nan, 0.132128, ...,      nan, 0.429762],
        ...,
        [0.132236, 0.195057, ...,      nan, 0.261765],
        [     nan,      nan, ...,      nan, 0.37905 ]],
       [[0.149478, 0.146562, ..., 0.037668, 0.230157],
        [0.145884, 0.286354, ..., 0.06611 , 0.464022],
        ...,
        [0.164331, 0.199312, ..., 0.035466, 0.405731],
        [0.104762, 0.138498, ..., 0.040375, 0.455539]]])
Coordinates:
  * data_source   (data_source) <U22 'AeronetSunV3Lev2.daily' 'TM5_AP3-CTRL2919'
    var_name      (data_source) <U8 'od550aer' 'od550aer'
    var_units     (data_source) <U1 '1' '1'
    ts_type_src   (data_source) <U7 'daily' 'monthly'
  * time          (time) datetime64[ns] 2010-01-01 2010-02-01 ... 2010-12-01
  * station_name  (station_name) <U19 'ARM_Darwin' ... 'Zinder_Airport'
    latitude      (station_name) float64 -12.43 37.97 15.35 ... 62.45 13.78
    longitude     (station_name) float64 130.9 23.72 -1.479 ... -114.4 8.99
    altitude      (station_name) float64 29.9 130.0 305.0 ... 300.0 220.8 456.0
Attributes:
    data_source:        ['AeronetSunV3Lev2.daily', 'TM5_AP3-CTRL2919']
    var_name:           ['od550aer', 'od550aer']
    ts_type:            monthly
    filter_name:        WORLD-wMOUNTAINS
    ts_type_src:        ['daily', 'monthly']
    start_str:          20100101
    stop_str:           20101231
    var_units:          ['1', '1']
    vert_scheme:        None
    data_level:         3
    revision_ref:       20190920
    from_files:         ['aerocom3_TM5_AP3-CTRL2019_od550aer_Column_2010_mont...
    from_files_ref:     None
    stations_ignored:   None
    colocate_time:      False
    apply_constraints:  True
    min_num_obs:        {'yearly': {'monthly': 3}, 'monthly': {'daily': 7}, '...
    outliers_removed:   True
    region:             WORLD
    lon_range:          [-180, 180]
    lat_range:          [-90, 90]
    alt_range:          None
ColocatedData object to NetCDF¶col_data.savename_aerocom
'od550aer_REF-AeronetSunV3Lev2.daily_MOD-TM5_AP3-CTRL2919_20100101_20101231_monthly_WORLD-wMOUNTAINS'
col_data.to_netcdf('.')
The example above did essentially the following steps:
GriddedData and UngriddedData, respectivelyColocatedData objectAll these steps can be done with a one-liner in pyaerocom using the Colocator class
Colocate the same model and observation network but now use the Angstrom exponent (ang4487aer) instead of the AOD:
colocator = pya.Colocator(model_id=DATA_ID,
                          obs_id=OBS_ID,
                          obs_vars='ang4487aer', 
                          ts_type='monthly',
                          start=2010, 
                          basedir_coldata='.',
                          reanalyse_existing=True,
                          save_coldata=True)
colocator.run()
The Colocator objects stores all ColocatedData objects that were created in it's data attribute, which is a nested dictionary, organised via model_id and var_name:
colocator.data
{'TM5_AP3-CTRL2919': {'ang4487aer': <xarray.DataArray 'ang4487aer' (data_source: 2, time: 12, station_name: 264)>
  array([[[     nan, 1.128966, ...,      nan, 0.259995],
          [     nan, 0.890816, ...,      nan, 0.305093],
          ...,
          [1.346214, 0.934353, ...,      nan, 0.494529],
          [     nan,      nan, ...,      nan, 0.489275]],
  
         [[0.867116, 0.737737, ..., 1.355472, 0.622317],
          [0.9829  , 0.305702, ..., 1.235905, 0.443166],
          ...,
          [1.284731, 0.657773, ..., 1.386418, 0.62829 ],
          [1.142794, 0.750162, ..., 1.374116, 0.413073]]])
  Coordinates:
    * data_source   (data_source) <U22 'AeronetSunV3Lev2.daily' 'TM5_AP3-CTRL2919'
      var_name      (data_source) <U10 'ang4487aer' 'ang4487aer'
      var_units     (data_source) <U1 '1' '1'
      ts_type_src   (data_source) <U7 'daily' 'monthly'
    * time          (time) datetime64[ns] 2010-01-01 2010-02-01 ... 2010-12-01
    * station_name  (station_name) <U19 'ARM_Darwin' ... 'Zinder_Airport'
      latitude      (station_name) float64 -12.43 37.97 15.35 ... 62.45 13.78
      longitude     (station_name) float64 130.9 23.72 -1.479 ... -114.4 8.99
      altitude      (station_name) float64 29.9 130.0 305.0 ... 300.0 220.8 456.0
  Attributes:
      data_source:        ['AeronetSunV3Lev2.daily', 'TM5_AP3-CTRL2919']
      var_name:           ['ang4487aer', 'ang4487aer']
      ts_type:            monthly
      filter_name:        WORLD-noMOUNTAINS
      ts_type_src:        ['daily', 'monthly']
      start_str:          20100101
      stop_str:           20101231
      var_units:          ['1', '1']
      vert_scheme:        None
      data_level:         3
      revision_ref:       20190920
      from_files:         ['aerocom3_TM5_AP3-CTRL2019_od440aer_Column_2010_mont...
      from_files_ref:     None
      stations_ignored:   None
      colocate_time:      0
      apply_constraints:  1
      outliers_removed:   1
      region:             WORLD
      lon_range:          [-180, 180]
      lat_range:          [-90, 90]
      alt_range:          [-1000000.0, 1000.0]
      _min_num_obs:       yearly,monthly,3;monthly,daily,7;daily,hourly,6;hourl...}}
colocator.data[DATA_ID]['ang4487aer'].plot_scatter(marker='o', alpha=0.1, color='g');
pya.browse_database('*CAM*')
Pyaerocom ReadGridded --------------------- Data ID: CAM53-Oslo_7310_MG15CLM45_5feb2017IHK_53OSLO_PD_UNTUNED Data directory: /home/jonasg/MyPyaerocom/pyaerocom-testdata/modeldata/CAM53-Oslo_7310_MG15CLM45_5feb2017IHK_53OSLO_PD_UNTUNED/renamed Available experiments: ['UNTUNED'] Available years: [2008, 2009, 2010] Available frequencies ['monthly'] Available variables: ['abs550aer', 'abs550aercs', 'ang4487aer', 'ang4487csaer', 'od550aer', 'od550csaer', 'od550lt1aer']
colocator.run(model_id=ANOTHER_MODEL_ID)
colocator.data.keys()
dict_keys(['TM5_AP3-CTRL2919', 'CAM53-Oslo_7310_MG15CLM45_5feb2017IHK_53OSLO_PD_UNTUNED'])
colocator.data[ANOTHER_MODEL_ID]['ang4487aer'].plot_scatter(marker='o', alpha=0.1, color='g');
_arr = col_data.data
mean_bias = ((_arr[1] - _arr[0])/_arr[0]).mean('time') * 100 #%
mean_bias
<xarray.DataArray 'od550aer' (station_name: 290)>
array([ 32.791464,  44.197759, -23.840775, ...,  19.99503 ,  36.219445,
        14.187602])
Coordinates:
    var_name      <U8 'od550aer'
    var_units     <U1 '1'
  * station_name  (station_name) <U19 'ARM_Darwin' ... 'Zinder_Airport'
    latitude      (station_name) float64 -12.43 37.97 15.35 ... 62.45 13.78
    longitude     (station_name) float64 130.9 23.72 -1.479 ... -114.4 8.99
    altitude      (station_name) float64 29.9 130.0 305.0 ... 300.0 220.8 456.0
    data_source   <U22 'AeronetSunV3Lev2.daily'
    ts_type_src   <U7 'daily'
ax = pya.plot.mapping.init_map()
_sc = ax.scatter(mean_bias.longitude, mean_bias.latitude, marker='o', c=mean_bias.data, cmap='bwr', 
                 vmin=-100, vmax=100, s=80)
cb = plt.colorbar(_sc)
cb.ax.set_ylabel('Normalised mean bias [%]');
https://aerocom-evaluation.met.no/overall.php?project=aerocom&exp=PIII-optics2019