def fit_time_func(model, date_list, ts_dis, unit_fac=100, G_fit=None, conf_level=0.95, seconds=0): """Fit a suite of fime functions to the time series. Equations: Gm = d Parameters: model - dict of time functions, check utils.time_func.estimate_time_func() for details. date_list - list of dates in YYYYMMDD format ts_dis - 1D np.ndarray, displacement time series unit_fac - float, scaling factor due to different data and display units G_fit - 2D np.ndarray, design matrix for the dense time series prediction plot conf_level - float in [0,1], confidence level of the plotted confidence intervals Returns: m_strs - dict, dictionary in {ds_name: ds_value} ts_fit - 1D np.ndarray, dense time series fit for plotting ts_fit_lim - list of 1D np.ndarray, the lower and upper boundaries of dense time series fit for plotting """ # init output m_strs = [] ts_fit = None ts_fit_lim = None if np.all(np.isnan(ts_dis)): return m_strs, ts_fit, ts_fit_lim # 1.1 estimate time func parameter via least squares (OLS) G, m, e2 = time_func.estimate_time_func( model=model, date_list=date_list, dis_ts=ts_dis, seconds=seconds) # 1.2 calc the precision of time func parameters # using the OLS estimation residues e2 = sum((d - Gm) ** 2) # assuming obs errors following normal distribution in time num_obs = len(date_list) num_param = G.shape[1] G_inv = linalg.inv(np.dot(G.T, G)) m_var_sum = e2.flatten() / (num_obs - num_param) m_std = np.sqrt(np.dot(np.diag(G_inv).reshape(-1, 1), m_var_sum)) # 1.3 translate estimation result into HDF5 ready datasets # AND compose list of strings for printout m_dict = ts2vel.model2hdf5_dataset(model, m, m_std)[0] m_strs = get_model_param_str(model, m_dict, unit_fac=unit_fac) # 2. reconstruct the fine resolution function if G_fit is not None: ts_fit = np.matmul(G_fit, m) ts_fit_std = np.sqrt(np.diag(G_fit.dot(np.diag(m_std**2)).dot(G_fit.T))) # calc confidence interval # references: # 1. Exercise 6.4 OMT: Interpretation from Hanssen et al. (2017) EdX online course. # Hanssen, R., Verhagen, S. and Samiei-Esfahany, S., (2017) Observation Theory: Estimating the Unknown, # Available at: https://www.edx.org/course/observation-theory-estimating-the-unknown # 2. https://stackoverflow.com/questions/20626994 alpha = 1 - conf_level # level of significance conf_int_scale = stats.norm.ppf(1 - alpha / 2) # scaling factor for confidence interval ts_fit_lim = [ts_fit - conf_int_scale * ts_fit_std, ts_fit + conf_int_scale * ts_fit_std] return m_strs, ts_fit, ts_fit_lim
def get_model_param_str(model, ds_dict, unit_fac=100): """Summary model parameters in a str paragraph. Parameters: model - dict, model dictionary ds_dict - dict, est. time func. param unit_fac - float, unit scaling factor, to determine the unit Returns: ds_strs - list of strings, each to summary the result of one time func """ # dataset unit dict ds_unit_dict = ts2vel.model2hdf5_dataset(model)[2] # update unit based on unit_fac for ds_name, ds_unit in ds_unit_dict.items(): units = ds_unit.split('/') if units[0] == 'm': if unit_fac == 1000 : units[0] = 'mm' elif unit_fac == 100 : units[0] = 'cm' elif unit_fac == 10 : units[0] = 'dm' elif unit_fac == 1 : units[0] = 'm' elif unit_fac == 0.001: units[0] = 'km' ds_unit_dict[ds_name] = '/'.join(units) # list of dataset names ds_names = [x for x in ds_dict.keys() if not x.endswith('Std')] w_key = max([len(x) for x in ds_names]) w_val = max([len('{:.2f}'.format(x[0])) for x in ds_dict.values()]) ds_strs = [] for ds_name in ds_names: # get param info ds_value = ds_dict[ds_name] ds_unit = ds_unit_dict[ds_name] ds_std_value = ds_dict.get(ds_name+'Std', None) # compose string ds_str = f'{ds_name:<{w_key}}: {ds_value[0]:>{w_val}.2f}' ds_str += f' +/- {ds_std_value[0]:>{w_val}.2f}' if ds_std_value is not None else '' ds_str += f' {ds_unit}' ds_strs.append(ds_str) return ds_strs
def get_model_param_str(model, ds_dict, disp_unit='cm'): """Summary model parameters in a str paragraph. Parameters: model - dict, model dictionary ds_dict - dict, est. time func. param disp_unit - float, display unit for length, which can be scaled Returns: ds_strs - list of strings, each to summary the result of one time func """ # dataset unit dict ds_unit_dict = ts2vel.model2hdf5_dataset(model)[2] ds_names = list(ds_unit_dict.keys()) # update ds_unit_dict based on disp_unit for ds_name in ds_names: units = ds_unit_dict[ds_name].split('/') if units[0] == 'm' and disp_unit != 'm': units[0] = disp_unit ds_unit_dict[ds_name] = '/'.join(units) # list of dataset names ds_names = [x for x in ds_dict.keys() if not x.endswith('Std')] w_key = max([len(x) for x in ds_names]) w_val = max([len('{:.2f}'.format(x[0])) for x in ds_dict.values()]) ds_strs = [] for ds_name in ds_names: # get param info ds_value = ds_dict[ds_name] ds_unit = ds_unit_dict[ds_name] ds_std_value = ds_dict.get(ds_name + 'Std', None) # compose string ds_str = f'{ds_name:<{w_key}}: {ds_value[0]:>{w_val}.2f}' ds_str += f' +/- {ds_std_value[0]:>{w_val}.2f}' if ds_std_value is not None else '' ds_str += f' {ds_unit}' ds_strs.append(ds_str) return ds_strs