Attempting a seasonal comparison at different depths between model and ONC data.
import matplotlib.pyplot as plt
import seaborn as sns
import datetime
import glob
import netCDF4 as nc
import pandas as pd
import numpy as np
import os
from salishsea_tools import viz_tools
import ONC_patrols as onc
%matplotlib inline
/home/nsoontie/anaconda3/lib/python3.4/site-packages/matplotlib/__init__.py:872: UserWarning: axes.color_cycle is deprecated and replaced with axes.prop_cycle; please use the latter. warnings.warn(self.msg_depr % (key, alt_key))
sns.set_color_codes()
badQC = [0,3,4,9]
files = glob.glob('/ocean/nsoontie/MEOPAR/ONC/Patrols/*.csv')
cols = sns.color_palette("hls", len(files))
grid_B = nc.Dataset('/data/nsoontie/MEOPAR/NEMO-forcing/grid/bathy_meter_SalishSea2.nc')
mesh_mask = nc.Dataset('/data/nsoontie/MEOPAR/NEMO-forcing/grid/mesh_mask_SalishSea2.nc')
def seasonal_cycle(data, depth_range, field):
"""Group data into months and apply mean/min/max to field in depth range given by drange. """
data_depth = data[(data['Depth Corrected (m)'] >= depth_range[0]) &
(data['Depth Corrected (m)'] <= depth_range[1])]
grouped = data_depth.groupby([data['day'].dt.year, data['day'].dt.month])
months=[]
means=[]
mins=[]
maxes=[]
for month, group in grouped:
months.append(datetime.datetime(month[0],month[1],15))
means.append(group[field].mean())
mins.append(group[field].min())
maxes.append(group[field].max())
return months, np.array(means), np.array(maxes), np.array(mins)
fig,axm = plt.subplots(1,1)
fig,axs = plt.subplots(2,2,figsize=(15,6))
c=0
fields = ['Practical Salinity Corrected (psu)',
'Temperature Corrected (C)']
dranges = [ (0,1), (119,120)]
for file in files:
data = onc.load_patrol_csv(file)
data = onc.exclude_bad(data,['Practical Salinity Corrected QC Flag '], badQC)
for field, ax_c in zip(fields,axs):
for drange, ax in zip(dranges, ax_c):
months, means,maxes,mins = seasonal_cycle(data,drange,field)
yerr = [means-mins, maxes-means]
ax.plot(months, means,'o-',color=cols[c])
ax.errorbar(months, means, yerr=yerr, color=cols[c], fmt='o')
ax.set_ylabel(field)
ax.set_title('{}: Depth range {}-{} m'.format(field,drange[0],drange[1]))
ax.set_xlim([datetime.datetime(2015,1,1),datetime.datetime(2016,3,31)])
data.plot(x='Longitude Corrected (deg)',y='Latitude Corrected (deg)',kind='scatter',ax=axm,c=cols[c])
c=c+1
viz_tools.plot_coastline(axm,grid_B,coords='map')
axm.set_ylim([48,51])
(48, 51)
Idea: for each cast, look up model cast but average over area, depth range and month.
def model_obs_data_frame(data, model_field, obs_field):
"""Build model data frame of daily mean, max, min of equivalent casts in observed data frame"""
model_obs_data = pd.DataFrame({'day':[], 'Model Mean':[],
'Model Max':[],'Model Min':[],
'Depth Corrected (m)':[],
'obs': []})
data_days, days = onc.list_days(data)
for d in days:
daily = data_days.get_group(d).dropna()
daily_casts = daily.groupby('Cast')
for c in daily_casts.groups:
cast = daily_casts.get_group(c)
lat, lon, date = onc.cast_position_and_time(cast)
obs_depth = np.array(cast['Depth Corrected (m)'][:])
try:
model_d_interp, model_max, model_min = onc.retrieve_nowcast_data(
lon, lat, date, obs_depth, model_field, grid_B, mesh_mask)
day_list = [date for i in range(model_d_interp.shape[0])]
sub_data = pd.DataFrame({'day': day_list,
'Model Mean': model_d_interp,
'Model Max': model_max,
'Model Min': model_min,
'Depth Corrected (m)': obs_depth,
'obs': np.array(cast[obs_field]),
'Longitude Corrected (deg)': np.array(cast['Longitude Corrected (deg)']),
'Latitude Corrected (deg)': np.array(cast['Latitude Corrected (deg)'])})
model_obs_data = model_obs_data.append(sub_data,ignore_index=True)
except IndexError:
print(
'No Model Point for {} {}'.format(
cast['Longitude Corrected (deg)'].mean(),
cast['Latitude Corrected (deg)'].mean()))
model_obs_data = model_obs_data.dropna()
return model_obs_data
def process_all_model_obs_dict(files, model_field, obs_field):
"""Create model-obs dictionary of data for all files listed in files."""
storage_dict = {}
for file in files:
data = onc.load_patrol_csv(file)
data = onc.exclude_bad(data,['Practical Salinity Corrected QC Flag '], badQC)
data_cast = onc.divide_into_casts(data)
key = os.path.basename(file).split('_')[0]
model_obs_data = model_obs_data_frame(data_cast, model_field, obs_field)
storage_dict[key] = model_obs_data
return storage_dict
def plot_map(files,data_dict, ax, cols, grid_B):
"""Plot casts color coded on a map"""
c=0
for file in files:
key = os.path.basename(file).split('_')[0]
data_region = data_dict[key]
data_region.plot(x='Longitude Corrected (deg)',y='Latitude Corrected (deg)',kind='scatter',ax=ax,c=cols[c])
c=c+1
viz_tools.plot_coastline(ax,grid_B,coords='map')
ax.set_ylim([48,51])
def fake_legend(ax):
"""Add a legend for model --s and obs -o"""
lo,=ax.plot([0,0],[0,0],'o-',color='gray')
lm,=ax.plot([0,0],[0,0],'s--',color='gray')
ax.legend([lo,lm],['obs','model'])
data_dict = process_all_model_obs_dict(files, 'vosaline', 'Practical Salinity Corrected (psu)')
data_dict_temp = process_all_model_obs_dict(files, 'votemper', 'Temperature Corrected (C)')
No Model Point for -124.86734926829268 49.60367543902441 No Model Point for -124.66473445454545 49.692539545454544 No Model Point for -123.30147711249997 48.334551287500005 No Model Point for -123.30579209999999 48.33254605 No Model Point for -123.18295900000003 48.461194 No Model Point for -123.44497209090906 48.3592954090909 No Model Point for -124.88309081132078 50.133188216981125 No Model Point for -124.88435187999998 50.02145567333332 No Model Point for -124.88373059333328 50.021488919999996 No Model Point for -124.8813384326241 50.13317171631203 No Model Point for -124.8818940066667 50.133191646666674 No Model Point for -124.92035517333338 49.92162987333333 No Model Point for -124.85466473333334 49.73138674999999 No Model Point for -124.92043986092716 49.92159999999987 No Model Point for -124.72611825675678 49.80339318243243 No Model Point for -124.92015144000004 49.921603720000014 No Model Point for -124.92028950335572 49.92159999999987 No Model Point for -124.15602561589404 49.57445858940396 No Model Point for -124.35768553691273 49.38875197986576 No Model Point for -125.27399967741934 455.9621534086022 No Model Point for -125.28148981249998 4.0 No Model Point for -125.27365982222221 50.0844527 No Model Point for -125.26677869230767 50.092094711538465 No Model Point for -125.26690597530859 50.08236830864198 No Model Point for -125.22824037500004 50.03548717857145 No Model Point for -123.47190000000023 48.70719999999996 No Model Point for -123.55089596363636 48.73519899090909 No Model Point for -124.86734926829268 49.60367543902441 No Model Point for -124.66473445454545 49.692539545454544 No Model Point for -123.30147711249997 48.334551287500005 No Model Point for -123.30579209999999 48.33254605 No Model Point for -123.18295900000003 48.461194 No Model Point for -123.44497209090906 48.3592954090909 No Model Point for -124.88309081132078 50.133188216981125 No Model Point for -124.88435187999998 50.02145567333332 No Model Point for -124.88373059333328 50.021488919999996 No Model Point for -124.8813384326241 50.13317171631203 No Model Point for -124.8818940066667 50.133191646666674 No Model Point for -124.92035517333338 49.92162987333333 No Model Point for -124.85466473333334 49.73138674999999 No Model Point for -124.92043986092716 49.92159999999987 No Model Point for -124.72611825675678 49.80339318243243 No Model Point for -124.92015144000004 49.921603720000014 No Model Point for -124.92028950335572 49.92159999999987 No Model Point for -124.15602561589404 49.57445858940396 No Model Point for -124.35768553691273 49.38875197986576 No Model Point for -125.27399967741934 455.9621534086022 No Model Point for -125.28148981249998 4.0 No Model Point for -125.27365982222221 50.0844527 No Model Point for -125.26677869230767 50.092094711538465 No Model Point for -125.26690597530859 50.08236830864198 No Model Point for -125.22824037500004 50.03548717857145 No Model Point for -123.47190000000023 48.70719999999996 No Model Point for -123.55089596363636 48.73519899090909
def plot_overview(obs_field, data_dict, dranges, lims, cols, files):
"""Plot an overview of seasonal trends in data_dict at depth ranges listed in dranges.
Compare model and observations."""
fig,axs = plt.subplots(1,len(dranges),figsize=(20,3))
c=0
for file in files:
key = os.path.basename(file).split('_')[0]
data_region = data_dict[key]
for drange, ax,ylim in zip(dranges, axs,lims):
obs_months, obs_means, _,_ = seasonal_cycle(data_region,drange,'obs')
months, means,_,_ = seasonal_cycle(data_region,drange,'Model Mean')
months, mins,_,_ = seasonal_cycle(data_region,drange,'Model Min')
months, maxes,_,_ = seasonal_cycle(data_region,drange,'Model Max')
yerr= [means-mins, maxes-means]
ax.plot(obs_months, obs_means,'o-',color=cols[c])
ax.plot(months, means,'s--',color=cols[c])
ax.errorbar(months, means, yerr=yerr, color=cols[c], fmt='o--')
ax.set_ylabel(obs_field)
ax.set_title('{}: Depth range {}-{} m'.format(obs_field,drange[0],drange[1]))
ax.set_xlim([datetime.datetime(2015,1,1),datetime.datetime(2016,3,31)])
plt.setp( ax.xaxis.get_majorticklabels(), rotation=70 )
fake_legend(ax)
ax.set_ylim(ylim)
c=c+1
fig,ax=plt.subplots(1,1,figsize=(5,3))
plot_map(files, data_dict, ax, cols, grid_B)
#Salinity
obs_field = 'Practical Salinity Corrected (psu)'
dranges = [ (1,2), (25,50), (80,100)]
lims=[(10,32), (27,32.5), (29,34)]
plot_overview(obs_field, data_dict, dranges, lims, cols,files)
#Temperature
obs_field = 'Temperature Corrected (C)'
lims=[(5,20), (5,15), (7.5,12)]
plot_overview(obs_field, data_dict_temp, dranges, lims, cols,files)
def calculate_bias(data, drange):
"""Calculat difference between model means and observed means in a depth range binned by month."""
obs_months, obs_means, _,_ = seasonal_cycle(data,drange,'obs')
months, mod_means,_,_ = seasonal_cycle(data,drange,'Model Mean')
months, mod_mins,_,_ = seasonal_cycle(data,drange,'Model Min')
months, mod_maxes,_,_ = seasonal_cycle(data,drange,'Model Max')
bias = mod_means - obs_means
upper_lim = mod_maxes - obs_means
lower_lim = mod_mins - obs_means
return bias, upper_lim, lower_lim, months
def plot_regional_bais(obs_field, data_dict, dranges, lims, cols,files):
"""Compare model and observed seasonal trends in depth ranges region by region."""
c=0
fig,axs = plt.subplots(1,len(dranges),figsize=(20,3))
for file in files:
key = os.path.basename(file).split('_')[0]
data_region = data_dict[key]
for drange, ax, ylim in zip(dranges, axs,lims):
model_bias, upper_lim, lower_lim, months = calculate_bias(data_region, drange)
ax.plot(months, model_bias,'o-',color=cols[c])
yerr=[model_bias - lower_lim, upper_lim-model_bias ]
ax.errorbar(months,model_bias, yerr=yerr, color=cols[c], fmt='o--')
ax.set_ylabel(obs_field)
ax.set_title('Model bias: Depth range {}-{} m'.format(drange[0],drange[1]))
ax.set_xlim([datetime.datetime(2015,1,1),datetime.datetime(2016,3,31)])
ax.set_ylim(ylim)
plt.setp( ax.xaxis.get_majorticklabels(), rotation=70 )
c=c+1
fig,ax=plt.subplots(1,1,figsize=(5,3))
plot_map(files, data_dict, ax, cols, grid_B)
#Salinity
obs_field = 'Practical Salinty Corrected (psu)'
dranges = [ (1,2), (25,50), (80,100)]
lims=[(-8,8), (-2,2), (-1.5,1.5)]
plot_regional_bais(obs_field, data_dict, dranges, lims, cols,files)
#Temperature
obs_field = 'Temperature Corrected (C)'
lims=[(-3,3), (-3,3), (-1.5,1.5)]
plot_regional_bais(obs_field, data_dict_temp, dranges, lims, cols,files)