def extract_ndates_around_date(self, date, n): ################################################################### """ Extract n values before and n values after a given date If n values are not available, returns all available values .data is set to None if no value at all is available :param date: date in decimal year :param n: number of observations to be extracted :return: a new Gts """ # import import inspect import numpy as np # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) # copy new_Gts = self.copy(data_xyz=None) new_Gts.data = None try: sel_data_after = np.copy(self.data[np.where((self.data[:, 0] > date))]) except: print("-- time series ", self.code, " does not have data after ", date) return (new_Gts) try: sel_data_before = np.copy(self.data[np.where( (self.data[:, 0] < date))]) except: print("-- time series ", self.code, " does not have data before ", date) return (new_Gts) # extract data if sel_data_after.shape[0] > n: sel_data_after = sel_data_after[:n, :] if sel_data_before.shape[0] > n: sel_data_before = sel_data_before[-n:, :] sel_data = np.vstack((sel_data_before, sel_data_after)) new_Gts.data = sel_data return (new_Gts)
def detrend_annual(self, method='L2', in_place=False, periods=None, exclude_periods=None): """ estimates a trend + annual terms in a time series and removes them velocity and annual attribute are saved in Gts.velocity & Gts.annual :param periods : periods used for estimation :param exclude_periods : periods to be excluded from estimation :param in_place : if True then replace the current time series :return : the detrended time series :note : outliers from Gts.outliers are ommitted in the estimation and offsets given Gts.offsets_dates are estimated simultaneously """ ########################################################################### # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) ########################################################################### import copy outliers = copy.deepcopy(self.outliers) tmp_ts = self.remove_outliers() if periods: tmp_ts = tmp_ts.extract_periods(periods) if exclude_periods: tmp_ts = tmp_ts.exclude_periods(periods) detrended = tmp_ts.make_model(option='detrend_annual', method=method) vel = detrended.velocity offsets_values = detrended.offsets_values annual = detrended.annual new_gts = self.copy() new_gts.outliers = outliers new_gts.offsets_values = offsets_values new_gts.velocity = vel new_gts.annual = annual model = new_gts.mmodel() new_gts.data[:, 1:4] = new_gts.data[:, 1:4] - model.data[:, 1:4] if in_place: self.data = new_gts.data else: return (new_gts)
def extract_ndates_before_date(self, date, n, verbose=False): ################################################################### """ Extract n values before a given date If n values are not available, returns all available values before date .data is set to None if no value at all is available :param date: date in decimal year :param n: number of observations to be extracted :return: a new Gts """ # import import inspect import numpy as np # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) # copy new_Gts = self.copy(data_xyz=None) new_Gts.data = None # extract data sel_data = np.copy(self.data[np.where(self.data[:, 0] < date)]) if self.data_xyz is not None: sel_data_xyz = np.copy(self.data_xyz[np.where(self.data[:, 0] < date)]) # extract data if sel_data.shape[0] > n: sel_data = sel_data[-n:, :] if self.data_xyz is not None: sel_data_xyz = sel_data_xyz[-n:, :] elif sel_data.shape[0] > 0: sel_data = sel_data[:, :] if self.data_xyz is not None: sel_data_xyz = sel_data_xyz[:, :] else: sel_data = None if verbose: print("-- time series ", self.code, " does not have data before ", date) new_Gts.data = sel_data if self.data_xyz is not None: new_Gts.data_xyz = sel_data_xyz return (new_Gts)
def remove_pole(self, pole, pole_type='euler', in_place=False, verbose=True): """ remove velocity predicted by an Euler pole or a rotation rate vector from a time series pole is a 1D array with 3 values requires self.lon & self.lat attributes to have been filled before if in_place = True then replace the current time series """ import numpy as np from pyacs.gts.Gts import Gts import inspect # after this method .data and .data_xyz are not consistent so .data_xyz is set to None self.data_xyz = None ########################################################################### # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) ########################################################################### if (self.lon is None) or (self.lat is None): print( "!!! ERROR: lon & lat needs to be properly filled to use this method." ) return () from pyacs.lib.gmtpoint import GMT_Point M = GMT_Point(code=self.code, lon=self.lon, lat=self.lat, Ve=0., Vn=0., SVe=0., SVn=0.) # N=M.substract_pole(pole,pole_type) N = M.pole(W=pole, SW=None, type_euler='euler', option='predict') vel_neu = np.array([N.Vn, N.Ve, 0.]) * 1E-3 if verbose: print("-- Removing velocity (NEU)", vel_neu * 1.E3) new_Gts = self.remove_velocity(vel_neu) if in_place: self.data = new_Gts.data.copy() return (new_Gts)
def rotate(self,angle,in_place=False): ################################################################### """ rotates the axis by an angle :param angle: angle in decimal degrees clockwise if in_place = True then replace the current time series """ # import import inspect import numpy as np # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3],__name__,self) except GtsInputDataNone as error: # print PYACS WARNING print( error ) return( self ) # computes the rotates components angle_radian = np.radians( -angle ) new_data=self.data.copy() new_x = new_data[:,2] * np.cos( angle_radian ) - new_data[:,1] * np.sin( angle_radian ) new_y = new_data[:,2] * np.sin( angle_radian ) + new_data[:,1] * np.cos( angle_radian ) new_data[:,1] = new_y new_data[:,2] = new_x new_data[:,4] = new_data[:,5] = 1.E-3 new_data[:,7:] = 0.0 new_Gts=self.copy(data=new_data) # .data_xyz set to None new_Gts.data_xyz = None if in_place: self.data=new_Gts.data.copy() return( self ) else: return(new_Gts)
def remove_velocity(self,vel_neu,in_place=False): ################################################################### """ remove velocity from a time series vel_neu is a 1D array of any arbitrary length, but with the velocities (NEU) to be removed in the first 3 columns if in_place = True then replace the current time series """ # import import inspect import numpy as np # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3],__name__,self) except GtsInputDataNone as error: # print PYACS WARNING print( error ) return( self ) if self.t0 != None: ref_date=self.t0 else: ref_date=np.mean(self.data[0,0]) v_neu= np.array( vel_neu[0:3] ) new_data=self.data.copy() new_data[:,1:4]=new_data[:,1:4]-np.dot( (new_data[:,0]-ref_date).reshape(-1,1) , v_neu.reshape(1,3) ) new_Gts=self.copy(data=new_data) # .data_xyz set to None new_Gts.data_xyz = None vel = vel_neu new_Gts.velocity = vel if in_place: self.data=new_Gts.data.copy() return( self ) else: return(new_Gts)
def frame(self, frame=None, in_place=False, verbose=False): """ Rotates a time series according to an Euler pole Returns a new Gts instance """ import numpy as np from pyacs.gts.Gts import Gts import inspect # after this method .data and .data_xyz are not consistent so .data_xyz is set to None self.data_xyz = None ########################################################################### # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) ########################################################################### # Euler poles taken from pygvel_pole_info.py lEuler = {} lEuler['soam'] = [-132.21, -18.83, 0.121] lEuler['nas'] = [-97.52, 6.48, 0.359] lEuler['nazca'] = [-94.4, 61.0, 0.57] lEuler['nas_wrt_soam'] = [-83.40, 15.21, 0.287] lEuler['inca_wrt_soam'] = [-63.76, 22.47, 0.092] lEuler['eura'] = [ -98.83483039304333, 54.22539546556553, 0.25678223107826376 ] # from Altamimi et al., 2012, eura_wrt_itrf2008 euler_vector = np.array(lEuler[frame]) new_Gts = self.remove_pole(euler_vector, verbose=verbose) if in_place: self.data = new_Gts.data.copy() return (new_Gts)
def differentiate(self): ################################################################### """ differentiate the current time series :return: the differentiated time series as a new Gts object :note : differentiation is made on .data. .data_xyz is set to None. """ # import import inspect import numpy as np import pyacs.lib.astrotime # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) new_Gts = self.copy() data = self.data data_diff = np.diff(data, axis=0) # for dates data_mjd = pyacs.lib.astrotime.decyear2mjd(data[:, 0]) new_data_mjd = (data_mjd[0:-1] + data_mjd[1:]) / 2. # data_diff[:,0]=data[0:len(data[:,0])-1,0]+data_diff[:,0] data_diff[:, 0] = pyacs.lib.astrotime.mjd2decyear(new_data_mjd) new_Gts.data = data_diff.copy() # set .data_xyz to None new_Gts.data_xyz = None return (new_Gts)
def decimate(self,time_step=30.,dates=[],method='median',verbose=False): ################################################################### """ decimate a time series :param time_step: time step in days :param dates: list of dates where point are forced to be written regardless time_step :param method: method used to be used to calculated the position. choose among ['median','mean','exact'] :param verbose: verbose mode :return : new Gts """ # import import inspect import numpy as np import pyacs.lib.astrotime as at # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3],__name__,self) except GtsInputDataNone as error: # print PYACS WARNING print( error ) return( self ) # start and end date start_decyear = self.data[0,0] end_decyear = self.data[-1,0] start_mjd = at.decyear2mjd(start_decyear) end_mjd = at.decyear2mjd(end_decyear) # new_gts to be filled new_gts=self.copy() new_gts.data=np.zeros((1,self.data.shape[1])) # loop on dates by time_step for date in np.arange(start_mjd ,end_mjd + time_step / 2. , time_step ): sub_ts=self.\ extract_periods( [at.mjd2decyear(date-time_step/2.),at.mjd2decyear(date+time_step/2.)] ,\ verbose = verbose) if sub_ts.data is None: continue if method == 'median': new_obs = np.median(sub_ts.data,axis=0) if method == 'mean': new_obs = np.mean(sub_ts.data,axis=0) if method == 'exact': new_obs = sub_ts.data[int(sub_ts.data.shape[0])-1,:] new_gts.data = np.vstack( ( new_gts.data, new_obs.reshape(1,-1) ) ) # case dates are provided for date in dates: sub_ts=self.extract_periods( [date,end_decyear] , verbose = verbose) if sub_ts.data is not None: new_obs = sub_ts.data[0,:] if verbose: print("-- adding observation at user requested date: %lf = %s = doy %d" % ( date, '-'.join(map(str, at.decyear2cal(date))), at.decyear2dayno(date))) if new_gts.data is not None: new_gts.data = np.vstack( ( new_gts.data, new_obs.reshape(1,-1) ) ) else: new_gts.data = new_obs.new_obs.reshape(1,-1) # remove first obs if new_gts.data.shape[0] == 1: if verbose: print('!!! decimated Gts has no date') new_gts.data = None new_gts.data_xyz = None return(new_gts) else: new_gts.data = np.delete(new_gts.data,0,axis=0) new_gts.neu2xyz() # reorder new_gts.reorder(verbose=verbose) if verbose: print('-- decimated Gts has ',new_gts.data.shape[0],' entries') return(new_gts)
def spline(self, smoothing=1, degree=5, date=None): """ :param smoothing: Positive smoothing factor used to choose the number of knots. Number of knots will be increased until the smoothing condition is satisfied: sum((w[i] * (y[i]-spl(x[i])))**2, axis=0) <= s :param degree: Degree of the smoothing spline. Must be <= 5. Default is k=3, a cubic spline. :param date: 1D array of interpolation dates in decimal year, or 'day' for every day. defualt None will interpolate at data date only. :return: new gts instance """ import numpy as np from pyacs.gts.Gts import Gts import inspect ########################################################################### # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) ########################################################################### # import from scipy.interpolate import UnivariateSpline import numpy as np Spline_ts = self.copy() s_e = UnivariateSpline(self.data[:, 0], self.data[:, 1], s=smoothing * 1E-3, k=degree) s_n = UnivariateSpline(self.data[:, 0], self.data[:, 2], s=smoothing * 1E-3, k=degree) s_u = UnivariateSpline(self.data[:, 0], self.data[:, 3], s=smoothing * 1E-3, k=degree) if date is None: Spline_ts.data[:, 1] = s_e(self.data[:, 0]) Spline_ts.data[:, 2] = s_n(self.data[:, 0]) Spline_ts.data[:, 3] = s_u(self.data[:, 0]) if isinstance(date, np.ndarray): Spline_ts.data[:, 1] = s_e(date) Spline_ts.data[:, 2] = s_n(date) Spline_ts.data[:, 3] = s_u(date) if date == 'day': import pyacs.lib.astrotime as at np_date = at.mjd2decyear( np.arange(at.decyear2mjd(self.data[0, 0]), at.decyear2mjd(self.data[-1, 0]))) Spline_ts.data = np.zeros((np_date.shape[0], 10)) Spline_ts.data[:, 0] = np_date Spline_ts.data[:, 1] = s_e(np_date) Spline_ts.data[:, 2] = s_n(np_date) Spline_ts.data[:, 3] = s_u(np_date) return (Spline_ts)
def substract_ts_daily(self,ts,verbose=True): ################################################################### """ substract the ts provided as argument to the current time series :param ts: time series to be substracted as a Gts instance :param verbose: verbose mode :return : new Gts :note: this method assumes daily time series """ # import import inspect import numpy as np import pyacs.lib.astrotime as at # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3],__name__,self) except GtsInputDataNone as error: # print PYACS WARNING print( error ) return( self ) # check data is not None try: if ts.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3],__name__,ts) except GtsInputDataNone as error: # print PYACS WARNING print( error ) return( self ) # find common dates np_mjd_1 = at.decyear2mjd( self.data[:,0] ).astype( int ) np_mjd_2 = at.decyear2mjd( ts.data[:,0] ).astype( int ) np_mjd_common_dates = np.intersect1d( np_mjd_1 , np_mjd_2 ) if verbose: print('-- ', np_mjd_common_dates.shape[0] , ' common dates found.') # test whether there are common dates if np_mjd_common_dates.shape[0] == 0: print('! WARNING No common dates between ',self.code,' from ',self.ifile) print('!!! and ',ts.code, ' from ',ts.ifile) new_data = None else: # extract mask1 = np.isin( np_mjd_1, np_mjd_common_dates ) data1 = self.data[ mask1 ] mask2 = np.isin( np_mjd_2, np_mjd_common_dates ) data2 = ts.data[ mask2 ] if data1.shape != data2.shape: print("!!! ERROR. Extracted data have different shapes") new_data = None # ENU new_data = data1 - data2 # dates new_data[:,0] = data1[:,0] # uncertainties new_data[:,4:8] = np.sqrt( data1[:,4:8]**2 + data2[:,4:8]**2 ) new_data[:,8:] = 0. new_code=self.code+'_'+ts.code new_Gts=self.copy(data=new_data) new_Gts.code=new_code # .data_xyz set to None new_Gts.data_xyz = None return(new_Gts)
def substract_ts(self, ts, tol=0.05, verbose=True): ################################################################### """ substract the ts provided as argument to the current time series :param ts: time series to be substracted as a Gts instance :param tol: date tolerance to decide whether two dates are identical in both time series. default = 1/4 day :param verbose: verbose mode :return : new Gts """ # import import inspect import numpy as np import pyacs.gts.Gts # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) # check data is not None try: if ts.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, ts) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) tol = tol / 365.25 # tolerance in decimal year # find common dates #common_dates=np.intersect1d(np.round(self.data[:,0],decimals=7),np.round(ts.data[:,0],decimals=7)) lindex = pyacs.gts.Gts.get_index_from_dates(self.data[:, 0], ts.data, tol) if verbose: print('-- ', len(lindex), ' common dates found.') common_dates = self.data[lindex, 0] # test whether there are common dates if common_dates.shape[0] == 0: print('! WARNING No common dates between ', self.code, ' from ', self.ifile) print('!!! and ', ts.code, ' from ', ts.ifile) new_data = None else: # extract extracted_target = self.extract_dates(common_dates, tol=tol) extracted_ref = ts.extract_dates(common_dates, tol=tol) new_data = extracted_target.data - extracted_ref.data new_data[:, 0] = common_dates new_data[:, 4] = np.sqrt(extracted_target.data[:, 4]**2 + extracted_ref.data[:, 4]**2) new_data[:, 5] = np.sqrt(extracted_target.data[:, 5]**2 + extracted_ref.data[:, 5]**2) new_data[:, 6] = np.sqrt(extracted_target.data[:, 6]**2 + extracted_ref.data[:, 6]**2) new_code = self.code + '_' + ts.code new_Gts = self.copy(data=new_data) new_Gts.code = new_code # .data_xyz set to None new_Gts.data_xyz = None return (new_Gts)
def plot(self, title=None, loffset=True, loutliers=True, verbose=False, date=[], yaxis=None, min_yaxis=None, yupaxis=None, xticks_minor_locator=1, lcomponent=['N', 'E', 'U'], error_scale=1.0, lperiod=[[]], lvline=[], save_dir_plots='.', save=None, show=True, unit='mm', date_unit='cal', date_ref=0.0, center=True, superimposed=None, lcolor=['r', 'g', 'c', 'm', 'y', 'k', 'b'], label=None, legend=False, set_zero_at_date=None, grid=True, plot_size=None, info=[], xlabel_fmt=None, **kwargs): ############################################################################### """ Create a plot of a North-East-Up time series and related info (offsets, outliers) using Matplotlib Coordinates of the time series are assumed to be in meters default plots units will be mm; Use unit='m' to get meters instead :param title: string to be added to the site name as a plot title :param loffset: boolean print a dash vertical line at offset dates :param loutliers: boolean print outliers :param verbose: boolean verbose mode :param date: [sdate,edate] start and end date for plots sdate and edate in decimal years if date_unit is either 'decyear' or 'cal', or in days if date_unit is 'days' :param yaxis: [min_y,max_y] min and max value for the yaxis if not provided automatically adjusted :param yupaxis: same as yaxis but applies to the up component only :param xticks_minor_locator: where xticks_minor_locator will be placed. Float when date_unit is 'decyear' or 'days', a string '%Y','%m','%d' is date_unit is 'cal'. :param lcomponent: list of components to be plotted (default =['N','E','U']) :param error_scale: scaling factor for error bars (default = 1.0, 0 means no error bar) :param lperiod: list of periods to be drawn in background (color=light salmon) :param lvline: list of dates where vertical lines will be drawn in background (color=green) :param save_dir_plots: directory used for saving the plots :param save: name, save the plot into name, if simply True an automatic name is given :param show: boolean, is True show the plot :param unit: 'm','cm','mm', default='mm' :param date_unit: 'decyear' or 'cal' or 'days', default='decyear' :param date_ref: reference date, default=0.0 :param center: boolean, if True the y_axis is centered around the mean value for the plotted period :param superimposed: if a gts is provided, it is superimposed to the master, default=None :param lcolor: color list used for the superimposed time series, default=['r','g','c','m','y','k','b'] :param label: label for superimposed time series to be displayed in legend, default=None :param legend: boolean. Set true to display label for superimposed time series, default=False :param set_zero_at_date: date at which the master and superimposed gts will be equal (default=None). date can also be a list with [date,offset_north,offset_east,offset_up] :param plot_size: plot size as a tuple. Default, best guess. :param grid: boolean :param info: title to appear in time series subplots :param **kwargs: any argument to be passed to matplotlib.pyplot.errorbar :note: The list of kwargs are: { 'linewidth' : 0,\ 'marker' : marker_main_symbol ,\ 'markersize' : marker_main_size, \ 'markerfacecolor' : marker_main_color,\ 'markeredgecolor' : marker_main_color,\ 'markeredgewidth' : marker_main_colorlw,\ 'ecolor' : error_bar_color,\ 'elinewidth' : error_bar_linewidth,\ 'capsize' : error_bar_capsize } """ ########################################################################### # import ########################################################################### # system import inspect import numpy as np import os.path # matplotlib import matplotlib.pyplot as plt import matplotlib.ticker as ticker from matplotlib.ticker import MultipleLocator import matplotlib.dates as mdates # pyacs from pyacs.gts.lib.errors import GtsInputDataNone import pyacs.gts.lib.plot.init_plot_settings import pyacs.lib.astrotime from pyacs.gts.Gts import Gts import pyacs.lib.utils import pyacs.gts.lib.plot.make_stitle import pyacs.gts.lib.plot.gts_to_ts ########################################################################### # lcolor ########################################################################### if (superimposed is not None) and (isinstance( superimposed, list)) and (len(superimposed) > 7): lcolor = np.random.rand(len(superimposed), 3) ########################################################################### # check data is not None ########################################################################### try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) ########################################################################### # ensure superimposed is either None or a list of gts ########################################################################### if superimposed is not None: if not isinstance(superimposed, list): superimposed = [superimposed] for agts in superimposed: if not isinstance(agts, Gts): raise TypeError ########################################################################### # ensure lperiod is a list of list ########################################################################### lperiod = pyacs.lib.utils.__ensure_list_of_list(lperiod) ########################################################################### # init default plot settings ########################################################################### H_plot_settings, H_kwargs_errorbar, H_kwargs_errorbar_superimposed = pyacs.gts.lib.plot.init_plot_settings.__init_kwargs( ) ########################################################################### # init plot settings with user provided values ########################################################################### for k, v in kwargs.items(): H_kwargs_errorbar[k] = v # turn show to off by default plt.ioff() # some drawing default parameters params = {'legend.fontsize': 5} plt.rcParams.update(params) # For figure caption txt_component = {} txt_component['N'] = 'North' txt_component['E'] = 'East' txt_component['U'] = 'Up' # plot size if plot_size is None: if len(lcomponent) == 1: plot_size = H_plot_settings['plot_size_1_component'] if len(lcomponent) == 2: plot_size = H_plot_settings['plot_size_2_component'] if len(lcomponent) == 3: plot_size = H_plot_settings['plot_size_3_component'] lperiod_facecolor = 'r' lperiod_alpha = 0.4 ########################################################################### # format for printing duration in subplot titles ########################################################################### if date != []: duration = date[-1] - date[0] else: duration = self.data[-1, 0] - self.data[0, 0] ########################################################################### # x-tick label ########################################################################### # adjust tickers for the decyear case if date_unit == 'decyear' or date_unit == 'days': if xlabel_fmt != None: fmt = ticker.FormatStrFormatter(xlabel_fmt) else: if duration >= 1.0: fmt = ticker.FormatStrFormatter('%4.0lf') if duration < 1.0: fmt = ticker.FormatStrFormatter('%6.2lf') if duration < 0.2: fmt = ticker.FormatStrFormatter('%6.3lf') if duration < 0.1: fmt = ticker.FormatStrFormatter('%6.4lf') plt.rcParams['axes.formatter.limits'] = (-7, 17) if date_unit == 'cal': if xlabel_fmt is not None: fmt = mdates.DateFormatter(xlabel_fmt) ########################################################################### # title and subtitle ########################################################################### if title is not None: stitle = title else: stitle = self.code ########################################################################### # info ########################################################################### if info == []: info = len(lcomponent) * [''] ########################################################################### # to_unit ########################################################################### if unit == 'cm': to_unit = 100. if unit == 'mm': to_unit = 1000. ########################################################################### # START PLOTTING ########################################################################### plt.ioff() f, ax = plt.subplots(len(lcomponent), sharex=True, figsize=plot_size) ########################################################################### # rotate xticks labels ########################################################################### f.autofmt_xdate(bottom=0.2, rotation=30, ha='right') # handle case where only one component and ax is not subscriptable if not isinstance(ax, np.ndarray): ax = np.array([ax]) np_date , data = pyacs.gts.lib.plot.gts_to_ts.gts_to_ts( self , \ date=date, \ unit=unit , \ date_unit=date_unit , \ date_ref=date_ref, \ set_zero_at_date = set_zero_at_date, \ center = center ) # master time series ########################################################################### idx_ax = 0 for component in lcomponent: # idx_component if component == 'N': idx = 0 if component == 'E': idx = 1 if component == 'U': idx = 2 # subplot title str_title = pyacs.gts.lib.plot.make_stitle.make_stitle( component, info=info[idx_ax]) ax[idx_ax].set_title(str_title, x=0, horizontalalignment='left', fontsize='11') # plot the data # print( H_kwargs_errorbar ) ax[idx_ax].errorbar(np_date, data[:, idx], yerr=data[:, idx + 3] * error_scale, **H_kwargs_errorbar) # plot outliers if self.outliers != []: ax[idx_ax].plot(np_date[self.outliers], data[self.outliers, idx], 'ro', markersize=H_kwargs_errorbar['markersize']) # Y-label ax[idx_ax].set_ylabel(unit) # grid ax[idx_ax].grid(grid) # xticks if date_unit == 'decyear': ax[idx_ax].xaxis.set_major_formatter(fmt) if date_unit == 'cal' and xlabel_fmt is not None: ax[idx_ax].xaxis.set_major_formatter(fmt) idx_ax = idx_ax + 1 # superimposed time series ########################################################################### if superimposed is None: wsuperimposed = [] else: wsuperimposed = list(superimposed) # Get label if label is None: label = [] for my_ts in wsuperimposed: label.append(my_ts.code) # test np_date = None data = None for i in np.arange(len(wsuperimposed)): if verbose: print('-- plotting superimposed time series: ', label[i]) np_date , data = pyacs.gts.lib.plot.gts_to_ts.gts_to_ts( wsuperimposed[i] , \ date=date, \ unit=unit , \ date_unit=date_unit , \ date_ref=date_ref, \ set_zero_at_date = set_zero_at_date, \ center = center ) idx_ax = 0 for component in lcomponent: # idx_component if component == 'N': idx = 0 if component == 'E': idx = 1 if component == 'U': idx = 2 # plot the data error_scale = 0. ax[idx_ax].errorbar(np_date, data[:, idx], yerr=data[:, idx + 3] * error_scale, color=lcolor[i], label=label[i]) idx_ax = idx_ax + 1 # plot offset_date of the master time series ########################################################################### if loffset: if date_unit == 'days': if (date_ref is None) or (date_ref == 0): # reference data is year.000 date_ref = float(int(self.data[0, 0])) ldate_offsets_in_date_unit = pyacs.lib.astrotime.decyear2mjd( self.offsets_dates) - pyacs.lib.astrotime.decyear2mjd(date_ref) if date_unit == 'decyear': ldate_offsets_in_date_unit = np.array( self.offsets_dates) - date_ref if date_unit == 'cal': ldate_offsets_in_date_unit = pyacs.lib.astrotime.decyear2datetime( self.offsets_dates) idx_ax = 0 for component in lcomponent: for odate in ldate_offsets_in_date_unit: ax[idx_ax].axvline(x=odate, linewidth=2, color='k', linestyle='--') idx_ax = idx_ax + 1 # lperiod option: periods to be highlighted ########################################################################### if lperiod != [[]]: if date_unit == 'days': if (date_ref is None) or (date_ref == 0): # reference data is year.000 date_ref = float(int(self.data[0, 0])) lperiod = pyacs.lib.astrotime.day_since_decyear( np.array(lperiod).flatten(), date_ref).reshape(-1, 2) if date_unit == 'decyear': lperiod = (np.array(lperiod).flatten() - date_ref).reshape(-1, 2) if date_unit == 'cal': lperiod = np.array( pyacs.lib.astrotime.decyear2datetime( np.array(lperiod).flatten())).reshape(-1, 2) # plot color background for periods idx_ax = 0 for component in lcomponent: for period in lperiod: (sdate, edate) = period ax[idx_ax].axvspan(sdate, edate, facecolor=lperiod_facecolor, alpha=lperiod_alpha) idx_ax = idx_ax + 1 # vertical lines option ########################################################################### if lvline != []: if date_unit == 'decyear': lvline_in_date_unit = np.array(lvline) - date_ref if date_unit == 'days': lvline_in_date_unit = np.array(lvline) - date_ref if date_unit == 'cal': lvline_in_date_unit = pyacs.lib.astrotime.decyear2datetime(lvline) idx_ax = 0 for component in lcomponent: for odate in lvline_in_date_unit: ax[idx_ax].axvline(x=odate, linewidth=2, color='g', linestyle='--') idx_ax = idx_ax + 1 # title ########################################################################### plt.suptitle(stitle) # yaxis ########################################################################### # added JMN 13/01/2021 to set a minimum value for y-scale if (min_yaxis is not None) and (yaxis is None): idx_ax = 0 for component in lcomponent: if component in ['N', 'E']: cyaxis = ax[idx_ax].get_ylim() ax[idx_ax].set_ylim(np.min([cyaxis[0], -min_yaxis]), np.max([cyaxis[1], min_yaxis])) if component == 'U': cyupaxis = ax[idx_ax].get_ylim() ax[idx_ax].set_ylim(np.min([cyupaxis[0], -min_yaxis]), np.max([cyupaxis[1], min_yaxis])) idx_ax = idx_ax + 1 if yaxis is not None: idx_ax = 0 for component in lcomponent: if component in ['N', 'E']: ax[idx_ax].set_ylim(yaxis[0], yaxis[1]) if component == 'U' and yupaxis is not None: ax[idx_ax].set_ylim(yupaxis[0], yupaxis[1]) idx_ax = idx_ax + 1 # date ########################################################################### if date != []: # case 'days' if date_unit == 'days': if (date_ref is None) or (date_ref == 0): np_date_x = pyacs.lib.astrotime.decyear2mjd( np.array(date)) - pyacs.lib.astrotime.decyear2mjd(date[0]) else: np_date_x = pyacs.lib.astrotime.decyear2mjd( np.array(date)) - pyacs.lib.astrotime.decyear2mjd(date_ref) # case 'decyear' if date_unit == 'decyear': if (date_ref is None) or (date_ref == 0): np_date_x = np.array(date) else: np_date_x = np.array(date) - date_ref # case 'cal' if date_unit == 'cal': np_date_x = pyacs.lib.astrotime.decyear2datetime(np.array(date)) idx_ax = 0 for component in lcomponent: if component in ['N', 'E']: ax[idx_ax].set_xlim(np_date_x[0], np_date_x[1]) if component == 'U' and yupaxis is not None: ax[idx_ax].set_xlim(np_date_x[0], np_date_x[1]) idx_ax = idx_ax + 1 # X Label ########################################################################### if date_unit == 'decyear': if (date_ref is None) or (date_ref == 0.0): str_xlabel = 'year' else: str_xlabel = ("decimal year since %s" % pyacs.lib.astrotime.decyear2datetime(date_ref)) if date_unit == 'days': if (date_ref is None) or (date_ref == 0): # reference data is year.000 date_ref = float(int(self.data[0, 0])) str_xlabel = ("days since %s" % pyacs.lib.astrotime.decyear2datetime(date_ref)) if date_unit == 'cal': str_xlabel = ("calendar date") ax[idx_ax - 1].set_xlabel(str_xlabel) # xtcicks minor locator ########################################################################### if date_unit == 'decyear': idx_ax = 0 for component in lcomponent: ax[idx_ax].xaxis.set_minor_locator( MultipleLocator(float(xticks_minor_locator))) idx_ax = idx_ax + 1 if date_unit != 'days': idx_ax = 0 if duration > 100: xticks_minor_locator = 10 if duration > 1000: xticks_minor_locator = 100 if duration > 10000: xticks_minor_locator = 1000 for component in lcomponent: ax[idx_ax].xaxis.set_minor_locator( MultipleLocator(float(xticks_minor_locator))) idx_ax = idx_ax + 1 if date_unit == 'cal': # default if self.data[-1, 0] - self.data[0, 0] > 5.: locator = mdates.YearLocator() else: locator = mdates.MonthLocator() # user provided default if xticks_minor_locator == '%Y': locator = mdates.YearLocator() # every year if xticks_minor_locator == '%m': locator = mdates.MonthLocator() # every month if xticks_minor_locator == '%d': locator = mdates.DayLocator() # every day idx_ax = 0 for component in lcomponent: ax[idx_ax].xaxis.set_minor_locator(locator) idx_ax = idx_ax + 1 # legend ########################################################################### if legend: idx_ax = 0 for component in lcomponent: ax[idx_ax].legend() idx_ax = idx_ax + 1 # tight_layout ########################################################################### plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=H_plot_settings['hspace']) # end of subplots ########################################################################### # save file as png if save option is set ########################################################################### if save is not None: # case save is a string if isinstance(save, str): # check whether save includes a path or simply a file name if os.path.sep in save: # save includes a path information fname = save else: # else fname = os.path.normpath(save_dir_plots + '/' + save) elif save: # save is simply set to True for automatic output file naming fname = os.path.normpath(save_dir_plots + '/' + self.code + '.png') # saving output file if verbose: print("-- Saving file to ", fname) # creates the directory of it does not exist if os.path.sep in fname: os.makedirs(os.path.dirname(fname), exist_ok=True) # plt.savefig(fname, dpi=150, facecolor='w', edgecolor='w', orientation='portrait', papertype='a4', format='png',transparent=False, pad_inches=0.1) # change 05/04/2021 papertype appears to be decommissioned for matplotlib 3.3 plt.savefig(fname, dpi=150, facecolor='w', edgecolor='w', orientation='portrait', format='png', transparent=False, pad_inches=0.1) # show ########################################################################### if show: # do not block program execution when displaying figures plt.ion() plt.show() else: plt.close(f) # returns the Gts for pyacs convention compatibility new_Gts = self.copy() return (new_Gts)
def get_coseismic(self, eq_date, window_days=5, sample_after=1, method='median', in_place=False): ################################################################### """ Get coseismic displacement at a given date. Coseismic displacement is estimated as the position difference between the median of window_days before the earthquake date and the median of sample_after samples after the earthquake date. note: only median method implemented """ # import import inspect import numpy as np import pyacs.lib.astrotime from datetime import timedelta # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) # eq date as datetime eq_datetime = pyacs.lib.astrotime.decyear2datetime(eq_date) # get the eq day at 00:00:00 and then go backward by window_days wdatetime = eq_datetime.replace( hour=0, minute=0, second=0, microsecond=0) - timedelta(days=window_days) # convert back to decimal year wdecyear = pyacs.lib.astrotime.datetime2decyear(wdatetime) # make extraction between eq - window_days and eq_date lindex = np.where((self.data[:, 0] > wdecyear) & (self.data[:, 0] < eq_date))[0] data_before_eq = self.data[lindex, 1:4] # take sample_after values after eq_date lindex = np.where(self.data[:, 0] > eq_date)[0][0] data_after_eq = self.data[lindex:lindex + sample_after, 1:4] # get offset values disp = np.median(data_after_eq, axis=0) - np.median(data_before_eq, axis=0) # calculates std before eq std_sdate = np.std(data_before_eq, axis=0) # calculates std after eq std_edate = np.std(data_after_eq, axis=0) # if std cannot be calculated or is 0. if np.max(std_sdate) == 0.0: lindex = np.where((self.data[:, 0] > wdecyear) & (self.data[:, 0] < eq_date))[0] std_sdate = np.mean(self.data[lindex, 4:7], axis=0) if np.max(std_edate) == 0.0: lindex = np.where(self.data[:, 0] > eq_date)[0][0] std_sdate = np.mean(self.data[lindex, 4:7], axis=0) std_disp = np.sqrt(std_edate**2 + std_sdate**2) new_Gts = self.copy() # modify the output time series new_Gts.data_xyz = None new_Gts.data[:, 1:4] = new_Gts.data[:, 1:4] - np.median(data_before_eq, axis=0) if in_place: self.offsets_values = np.array([eq_date] + disp.tolist() + std_disp.tolist()).reshape(1, 7) return (self) del new_Gts else: new_Gts.offsets_values = np.array([eq_date] + disp.tolist() + std_disp.tolist()).reshape(1, 7) return (new_Gts)
def detrend_median(self, delta_day=None, in_place=False, periods=[], exclude_periods=[], verbose=False, auto=False): ############################################################################### """ Calculates a velocity using the median of pair of displacements exactly separated by one year, inspired from MIDAS If the time series has less than a year of data, then the time series is kept untouched. :param delta_day: if None, it is one year, if 0 then it is the relax mode for campaign data, any integer is the time delta (in days) used to compute velocity. :param in_place: boolean, if True, in_place, if False, returns a new Gts instance (default) :param periods: periods (list of lists) to be included for trend calculation :param exclude_periods: periods (list of lists) to be excluded for trend calculation :param verbose: verbose mode :param auto: if True, then start will delta_day=None, if it fails or found less than 100 pairs then use delta_day=0, if fails then use regular detrend :note: returns None if time series is shorter than 1 year """ import numpy as np from pyacs.gts.Gts import Gts import inspect # after this method .data and .data_xyz are not consistent so .data_xyz is set to None self.data_xyz = None ########################################################################### # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) ########################################################################### ########################################################################### # 1-yr threshold ########################################################################### if (self.data[-1, 0] - self.data[0, 0]) < 1.: if verbose: print("!!! WARNING: time series shorter than 1 year for side: %s" % self.code) return (None) ########################################################################### # run the appropriate estimator using autoreference if auto is True ########################################################################### if auto: tts = self.detrend_median(delta_day=None, in_place=in_place, \ periods=periods, exclude_periods=exclude_periods, \ verbose=verbose, auto=False) if tts is None: tts = self.detrend_median(delta_day=0, in_place=in_place, \ periods=periods, exclude_periods=exclude_periods, \ verbose=verbose, auto=False) if tts is None: tts = self.detrend(in_place=in_place, \ periods=periods, exclude_periods=exclude_periods, \ verbose=verbose) return (tts) ########################################################################### # start main detrend_median ########################################################################### import pyacs.lib.astrotime tmp_ts = self.remove_outliers() if periods != []: tmp_ts = tmp_ts.extract_periods(periods) if exclude_periods != []: tmp_ts = tmp_ts.exclude_periods(periods) # minimum number of pair velocity estimates to consider the result min_vel_estimate = 100 ########################################################################### # MIDAS-like method, using 1-year pair data for velocity estimate ########################################################################### if delta_day is None: duration_in_year = tmp_ts.data[-1, 0] - tmp_ts.data[0, 0] if duration_in_year >= 1.: H_year = {} # creates H_year arrays for year in np.arange(int(tmp_ts.data[0, 0]), int(tmp_ts.data[-1, 0]) + 1): H_year[year] = np.zeros((365, 3)) H_year[year][:, :] = np.nan # deal with dates np_mjd = pyacs.lib.astrotime.decyear2mjd(tmp_ts.data[:, 0]) (np_doy, np_year, _np_ut) = pyacs.lib.astrotime.mjd2dayno(np_mjd) np_doy = np_doy.astype(int) np_year = np_year.astype(int) # fill H_year arrays for i in np.arange(np_doy.shape[0]): if np_doy[i] != 366: H_year[np_year[i]][np_doy[i] - 1, :] = tmp_ts.data[i, 1:4] # stack the velocities np_vel = np.zeros((365 * (len(H_year) - 1), 3)) np_vel[:, :] = np.nan i = 0 for year in sorted(H_year.keys())[0:-1]: np_vel[i * 365:(i + 1) * 365] = H_year[year + 1] - H_year[year] i = i + 1 # test whether there are at least 3 non-Nan numbers if np.count_nonzero(~np.isnan(np_vel)) < min_vel_estimate: return (None) # calculates the median velocity med_vel = np.nanmedian(np_vel, axis=0) [med_vel_north, med_vel_east, med_vel_up] = med_vel # calculate uncertainty and refined value of vel using step 2 from Blewitt et al. (2016) p. 2057 # remove Nan np_vel_north = np_vel[:, 0][np.logical_not(np.isnan(np_vel[:, 0]))] np_vel_east = np_vel[:, 1][np.logical_not(np.isnan(np_vel[:, 1]))] np_vel_up = np_vel[:, 2][np.logical_not(np.isnan(np_vel[:, 2]))] # calculates sigma fabs_vn = np.fabs(np_vel_north - med_vel_north) fabs_ve = np.fabs(np_vel_east - med_vel_east) fabs_vu = np.fabs(np_vel_up - med_vel_up) sigma_vn = 1.4826 * np.median(fabs_vn) sigma_ve = 1.4826 * np.median(fabs_ve) sigma_vu = 1.4826 * np.median(fabs_vu) # removes all vel > 1.4826 * med_vel np_vel_north_cln = np_vel_north[np.where(fabs_vn <= 2 * sigma_vn)] np_vel_east_cln = np_vel_east[np.where(fabs_ve <= 2 * sigma_ve)] np_vel_up_cln = np_vel_up[np.where(fabs_vu <= 2 * sigma_vu)] # get the new vel estimates med_vn = np.median(np_vel_north_cln) med_ve = np.median(np_vel_east_cln) med_vu = np.median(np_vel_up_cln) # get the new sigma med_sigma_vn = 1.4826 * np.median( np.fabs(np_vel_north_cln - med_vn)) med_sigma_ve = 1.4826 * np.median( np.fabs(np_vel_east_cln - med_ve)) med_sigma_vu = 1.4826 * np.median(np.fabs(np_vel_up_cln - med_vu)) # empirical factor for correlated noise ef = 3. sigma_vn = 1.2533 * med_sigma_vn / np.sqrt(fabs_vn.shape[0]) * ef sigma_ve = 1.2533 * med_sigma_ve / np.sqrt(fabs_ve.shape[0]) * ef sigma_vu = 1.2533 * med_sigma_vu / np.sqrt(fabs_vu.shape[0]) * ef # final med_vel med_vel = np.array( [med_vn, med_ve, med_vu, sigma_vn, sigma_ve, sigma_vu]) else: # time series has less than a year of data # velocity is set to 0. med_vel = np.array([0., 0., 0.]) # end case 1-yr elif (isinstance(delta_day, int) and delta_day == 0): ########################################################################### # case campaign, close to relaxed method in MIDAS ########################################################################### H_year = {} lvel = [] # creates H_year arrays for year in np.arange(int(tmp_ts.data[0, 0]), int(tmp_ts.data[-1, 0]) + 1): lindex = np.where(tmp_ts.data.astype(int)[:, 0] == year) # tt = tmp_ts.extract_periods([float(year),float(year)+0.999999]) # if tt.data is not None: # H_year[year] = tt.data[:,:4] H_year[year] = tmp_ts.data[lindex[0], :4] # removes years with no available data # H_year = {key:val for key, val in H_year.items() if val is not None} # calculates velocity values for all data pairs separated by more than one year # while H_year != {}: for year_start in sorted(list(H_year.keys())): # np_start = H_year[year_start] for year_end in sorted(list(H_year.keys())): ok = False for i in np.arange(H_year[year_end].shape[0]): for j in np.arange(H_year[year_start].shape[0]): # check wether the pair date includes a given offset date include_offset_date = False for odate in self.offsets_dates: if (odate >= H_year[year_start][j, 0]) and ( odate <= H_year[year_end][i, 0]): include_offset_date = True if not include_offset_date: # displacement v = (H_year[year_end][i] - H_year[year_start][j])[1:] # delta_year delta_year = H_year[year_end][ i, 0] - H_year[year_start][j, 0] # append to lvel if delta_year > 0.90: lvel.append(v / delta_year) ok = True if verbose: print("-- adding %.5lf - %.5lf " % (H_year[year_start][j, 0], H_year[year_end][i, 0])) print(i, j, v / delta_year) print(H_year[year_end][i], H_year[year_start][j]) # if some pairs have already been found, stop here to mitigate the effect of offsets # if ok: # break del H_year[year_start] # calculates the median velocity np_vel = np.array(lvel) np_vel_north = np_vel[:, 0] np_vel_east = np_vel[:, 1] np_vel_up = np_vel[:, 2] med_vel = np.median(np_vel, axis=0) [med_vel_north, med_vel_east, med_vel_up] = med_vel # calculates sigma fabs_vn = np.fabs(np_vel_north - med_vel_north) fabs_ve = np.fabs(np_vel_east - med_vel_east) fabs_vu = np.fabs(np_vel_up - med_vel_up) sigma_vn = 1.4826 * np.median(fabs_vn) sigma_ve = 1.4826 * np.median(fabs_ve) sigma_vu = 1.4826 * np.median(fabs_vu) # removes all vel > 1.4826 * med_vel np_vel_north_cln = np_vel_north[np.where(fabs_vn <= 2 * sigma_vn)] np_vel_east_cln = np_vel_east[np.where(fabs_ve <= 2 * sigma_ve)] np_vel_up_cln = np_vel_up[np.where(fabs_vu <= 2 * sigma_vu)] # get the new vel estimates med_vn = np.median(np_vel_north_cln) med_ve = np.median(np_vel_east_cln) med_vu = np.median(np_vel_up_cln) # get the new sigma med_sigma_vn = 1.4826 * np.median(np.fabs(np_vel_north_cln - med_vn)) med_sigma_ve = 1.4826 * np.median(np.fabs(np_vel_east_cln - med_ve)) med_sigma_vu = 1.4826 * np.median(np.fabs(np_vel_up_cln - med_vu)) # empirical factor for correlated noise ef = 3. sigma_vn = 1.2533 * med_sigma_vn / np.sqrt(fabs_vn.shape[0]) * ef sigma_ve = 1.2533 * med_sigma_ve / np.sqrt(fabs_ve.shape[0]) * ef sigma_vu = 1.2533 * med_sigma_vu / np.sqrt(fabs_vu.shape[0]) * ef # final med_vel med_vel = np.array( [med_vn, med_ve, med_vu, sigma_vn, sigma_ve, sigma_vu]) else: ########################################################################### # case delta_day with integer value ########################################################################### def delta(n): """ Simple delta function for integer """ if not n == 0: return (1) else: return (0) # first form the day vector np_mjd_data = list( map(int, pyacs.lib.astrotime.decyear2mjd(tmp_ts.data[:, 0]))) # so the index are i_np_mjd_data = np_mjd_data - np.min(np_mjd_data) # the void array filled with np.nan void = np.zeros( (np.max(np_mjd_data) - np.min(np_mjd_data) + 1, 3)) * np.nan # I fill void with the time series at existing dates void[i_np_mjd_data] = tmp_ts.data[:, 1:4] # now reshape the vector for easy pair differentiation # if TS = void[ : , (0,1,2) ] easy diff would be achieved by working on a array W = TS.reshape(-1,delta_day) # however, this requires to complete the number of lines of TS with np.nan to be able to reshape it CTS = np.zeros((delta_day - np.mod(void.shape[0], delta_day)) * delta(np.mod(void.shape[0], delta_day))) * np.nan # init med_vel med_vel = np.array([0., 0., 0.]) to_year = 365.25 / float(delta_day) for i in [0, 1, 2]: med_vel[i] = np.nanmedian( np.diff(np.append(void[:, i], CTS).reshape(-1, delta_day).T, axis=1)) * to_year ########################################################################### # return detrended time series ########################################################################### new_gts = self.remove_velocity(med_vel) new_gts.outliers = self.outliers new_gts.offsets_values = self.offsets_values new_gts.velocity = med_vel # if in_place: # self.velocity = new_gts.velocity # return( self ) # else: # return( new_gts ) return (self.remove_velocity(med_vel, in_place=in_place))
def plot(self,\ title=None,\ loffset=True,\ loutliers=True,\ verbose=False,\ date=[],\ yaxis=None, \ yupaxis=None, \ lcomponent=['N','E','U'],\ error_scale=1.0, \ lperiod=[[]],\ lvline=[],\ save_dir_plots='.', \ save=None,\ show=True,\ unit='mm',\ date_unit='decyear', \ date_ref=0.0, center=True,\ superimposed=None, \ superimposed_equal_date=None, \ plot_size=None, \ info = [], \ **kwargs): ############################################################################### """ Create a plot of a North-East-Up time series and related info (offsets, outliers) using Matplotlib Coordinates of the time series are assumed to be in meters default plots units will be mm; Use unit='m' to get meters instead :param title: string to be added to the site name as a plot title :param loffset: boolean print a dash vertical line at offset dates :param loutliers: boolean print outliers :param verbose: boolean verbose mode :param date: [sdate,edate] start and end date for plots sdate and edate in decimal years if date_unit is either 'decyear' or 'cal', or in days if date_unit is 'days' :param yaxis: [min_y,max_y] min and max value for the yaxis if not provided automatically adjusted :param yupaxis: same as yaxis but applies to the up component only :param lcomponent: list of components to be plotted (default =['N','E','U']) :param error_scale: scaling factor for error bars (default = 1.0, 0 means no error bar) :param lperiod: list of periods to be drawn in background (color=light salmon) :param lvline: list of dates where vertical lines will be drawn in background (color=green) :param save_dir_plots: directory used for saving the plots :param save: name, save the plot into name, if simply True an automatic name is given :param show: boolean, is True show the plot :param unit: 'm','cm','mm', default='mm' :param date_unit: 'decyear' or 'cal' or 'days', default='decyear' :param date_ref: reference date, default=0.0 :param center: boolean, if True the y_axis is centered around the mean value for the plotted period :param superimposed: if a gts is provided, it is superimposed to the master, default=None :param superimposed_equal_date: date at which the master and superimposed gts will be equal (default=None). date can also be a list with [date,offset_north,offset_east,offset_up] :param plot_size: plot size as a tuple. Default, best guess. :param info: info to appear in time series subtitles. A list of keywords among: 'wrms','bias','vel','period' :param **kwargs: any argument to be passed to plot_date (unit_date='cal') or errorbar (unit_date='decyear' or 'days') :note: The list of kwargs are: H_kwargs_plot_date={\\ 'marker' : marker_main_symbol,\\ 'markersize' : marker_main_size, \\ 'markerfacecolor' : marker_main_color,\\ 'markeredgecolor' : marker_main_color,\\ 'markeredgewidth' : marker_main_colorlw \\ } H_kwargs_plot_date_superimposed={ \\ 'color' : line_2_color,\\ 'linewidth' : line_2_width,\\ 'linestyle' : line_2_style,\\ 'marker' : marker_2_symbol,\\ 'markersize' : marker_2_size, \\ 'markerfacecolor' : marker_2_color,\\ 'markeredgecolor' : marker_2_color,\\ 'markeredgewidth' : marker_2_colorlw \\ } H_kwargs_errorbar={'linewidth' : 0,\ 'marker' : marker_main_symbol ,\ 'markersize' : marker_main_size, \ 'markerfacecolor' : marker_main_color,\ 'markeredgecolor' : marker_main_color,\ 'markeredgewidth' : marker_main_colorlw,\ 'ecolor' : error_bar_color,\ 'elinewidth' : error_bar_linewidth,\ 'capsize' : error_bar_capsize } H_kwargs_errorbar_superimposed={ \ 'color' : line_2_color,\ 'linewidth' : line_2_width,\ 'linestyle' : line_2_style,\ 'marker' : marker_2_symbol,\ 'markersize' : marker_2_size, \ 'markerfacecolor' : marker_2_color,\ 'markeredgecolor' : marker_2_color,\ 'markeredgewidth' : marker_2_colorlw, \ 'ecolor' : error_bar_color,\ 'elinewidth' : error_bar_linewidth,\ 'capsize' : error_bar_capsize } """ # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3],__name__,self) except GtsInputDataNone as error: # print PYACS WARNING print( error ) return( self ) # cdata #if not self.cdata(data=True): # print '! Cannot make plot. Problem with .data attribute.' # return(self) # deal with **kwargs for plot settings __init_kwargs() if date_unit == 'cal': # **kwarg for matplotlib plot_date for key,value in list(kwargs.items()): H_kwargs_plot_date[key]=value if (date_unit == 'days') or (date_unit == 'decyear'): # **kwarg for matplotlib errorbar for key,value in list(kwargs.items()): H_kwargs_errorbar[key]=value # we usually want the plot in mm if unit=='mm':to_mm=1000.0 if unit=='cm':to_mm=100.0 if unit=='m':to_mm=1.0 # import new modules import matplotlib.pyplot as plt import matplotlib.ticker as ticker import pyacs.lib.astrotime # turn show to off by default plt.ioff() # some drawing default parameters params = {'legend.fontsize': 4} plt.rcParams.update(params) # For figure caption txt_component={} txt_component['N']='North' txt_component['E']='East' txt_component['U']='Up' # format for printing duration in subplot titles if date != []:duration=date[-1]-date[0] else:duration=self.data[-1,0]-self.data[0,0] # adjust tickers for the decila years case if date_unit=='decyear': fmt=ticker.FormatStrFormatter('%6.1lf') if duration < 1.0:fmt=ticker.FormatStrFormatter('%6.2lf') if duration < 0.2:fmt=ticker.FormatStrFormatter('%6.3lf') if duration < 0.1:fmt=ticker.FormatStrFormatter('%6.4lf') plt.rcParams['axes.formatter.limits']=(-7,17) # ensure superimposed is either None or a list of gts if superimposed is not None: if not isinstance(superimposed,list): superimposed=[superimposed] for agts in superimposed: if not isinstance(agts,Gts): raise TypeError #for subplot title information if self.code:stitle=self.code else:stitle='' # list of color for superimposed ts lcolor = ['r','g','c','m','y','k','b'] if superimposed is not None: i=0 for agts in superimposed: stitle+=' / ' stitle+=agts.code+("-%s" % lcolor[i]) i = i + 1 if title is not None: stitle = stitle + ' ' + title # ensure lperiod is a list of list lperiod = __ensure_list_of_list(lperiod) # plot size if plot_size is None: if len(lcomponent)==1:plot_size=plot_size_1_component if len(lcomponent)==2:plot_size=plot_size_2_component if len(lcomponent)==3:plot_size=plot_size_3_component plt.figure(num=None, figsize=plot_size) # a small routine to select the data to be plotted depending on the component def __data_4_plot(gts, component, unit='mm'): if component=='N':data=gts.data[:,(0,1,4)] if component=='E':data=gts.data[:,(0,2,5)] if component=='U':data=gts.data[:,(0,3,6)] data[:,1:]=data[:,1:]*to_mm return(data) # handle superimposed_equal_date option if superimposed_equal_date is not None: center = False wgts=self.set_zero_at_date(superimposed_equal_date,offset=0.0) for i in np.arange( len(superimposed)): superimposed[i] = superimposed[i].set_zero_at_date(superimposed_equal_date,offset=0.0) wsuperimposed=superimposed else: wgts=self wsuperimposed=superimposed # Create frames for component in lcomponent: if component == 'N' and len(lcomponent)==3: nsubplot=311 if component == 'E' and len(lcomponent)==3: nsubplot=312 if component == 'U' and len(lcomponent)==3: nsubplot=313 if component == 'N' and len(lcomponent)==2: nsubplot=211 if component == 'E' and len(lcomponent)==2: nsubplot=212 if len(lcomponent)==1: nsubplot=111 # setup_save current subplot plt.subplot(nsubplot) # if date_unit=='decyear': # plt.subplot(nsubplot).xaxis.set_major_formatter(fmt) if wgts.velocity is None: vel=None else: if component == 'N':vel=wgts.velocity[0]*to_mm if component == 'E':vel=wgts.velocity[1]*to_mm if component == 'U':vel=wgts.velocity[2]*to_mm str_title=__make_stitle(component,vel,__data_4_plot(wgts,component),unit, info=info) # plot the data if date_unit == 'cal': (xmin_subplot,xmax_subplot) = __plot_ts(plt,__data_4_plot(wgts,component),\ loutliers=wgts.outliers,\ date=date,\ date_unit=date_unit,\ ref_date=date_ref,\ yaxis=yaxis,\ center=center,\ superimposed=None,\ verbose=verbose,\ error_scale=error_scale, **H_kwargs_plot_date) if (date_unit == 'days') or (date_unit == 'decyear'): (xmin_subplot,xmax_subplot) = __plot_ts(plt,__data_4_plot(wgts,component),\ loutliers=wgts.outliers,\ date=date,\ date_unit=date_unit,\ ref_date=date_ref,\ yaxis=yaxis,\ center=center,\ superimposed=None,\ verbose=verbose,\ error_scale=error_scale, **H_kwargs_errorbar) # plot offset dates as vertical lines if loffset: if date_unit == 'days': if (date_ref is None) or (date_ref == 0): # reference data is year.000 date_ref=float(int(wgts.data[0,0])) ldate_offsets_in_date_unit=pyacs.lib.astrotime.day_since_decyear(wgts.offsets_dates,date_ref) if date_unit == 'decyear': ldate_offsets_in_date_unit=np.array(wgts.offsets_dates)-date_ref if date_unit == 'cal': ldate_offsets_in_date_unit = __decyear2num(wgts.offsets_dates) for odate in ldate_offsets_in_date_unit: if odate < xmax_subplot and odate > xmin_subplot: plt.axvline(x= odate ,linewidth=2, color='g', linestyle='--') # subplot title plt.title(str_title, x=0, horizontalalignment='left', fontsize= '11') # superimposed option if superimposed is not None: for i in np.arange(len(wsuperimposed)): agts = wsuperimposed[i] # color for agts H_kwargs_plot_date_superimposed['color'] = lcolor[i] H_kwargs_errorbar_superimposed['color'] = lcolor[i] # calendar date if date_unit == 'cal': (xmin_subplot,xmax_subplot) = __plot_ts(plt,__data_4_plot( agts , component),\ loutliers = agts.outliers,\ date=date,\ date_unit=date_unit,\ ref_date=date_ref,\ yaxis=yaxis,\ center=center,\ superimposed=None,\ verbose=verbose,\ error_scale=error_scale, **H_kwargs_plot_date_superimposed) # date_init is 'days' or 'decyear' if (date_unit == 'days') or (date_unit == 'decyear'): (xmin_subplot,xmax_subplot) = __plot_ts(plt,__data_4_plot( agts ,component),\ loutliers = agts.outliers,\ date=date,\ date_unit=date_unit,\ ref_date=date_ref,\ yaxis=yaxis,\ center=center,\ superimposed=None,\ verbose=verbose,\ error_scale=error_scale, **H_kwargs_errorbar_superimposed) # lperiod option: periods to be highlighted if lperiod != [[]]: if date_unit == 'days': if (date_ref is None) or (date_ref == 0): # reference data is year.000 date_ref=float(int(wgts.data[0,0])) __plot_lperiod(plt,lperiod,date_unit,date_ref) # vertical lines option if lvline != []: if date_unit == 'days': if (date_ref is None) or (date_ref == 0): # reference data is year.000 date_ref=float(int(wgts.data[0,0])) ldate_vline_in_date_unit=pyacs.lib.astrotime.day_since_decyear(lvline,date_ref) if date_unit == 'decyear': ldate_vline_in_date_unit=np.array(lvline)-date_ref if date_unit == 'cal': ldate_vline_in_date_unit = __decyear2num(lvline) for odate in ldate_vline_in_date_unit: if odate < xmax_subplot and odate > xmin_subplot: plt.axvline(x= odate ,linewidth=1, color='k', linestyle='-') # Y-label plt.ylabel(unit) # Xticks if date_unit == 'decyear': plt.gca().xaxis.set_major_formatter(fmt) # X Label if date_unit=='decyear': if (date_ref is None) or (date_ref == 0.0): str_xlabel='year' else: str_xlabel=("decimal year since %s" %pyacs.lib.astrotime.decyear2datetime(date_ref)) if date_unit=='days': if (date_ref is None) or (date_ref == 0): # reference data is year.000 date_ref=float(int(wgts.data[0,0])) str_xlabel=("days since %s" %pyacs.lib.astrotime.decyear2datetime(date_ref)) if date_unit=='cal': str_xlabel=("calendar date") plt.xlabel(str_xlabel) plt.suptitle(stitle) # tight_layout plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=hspace) #plt.tight_layout() # end of subplots # save file as png if save option is set if save is not None: # import import os import os.path # case save is a string if isinstance(save, str): # check whether save includes a path or simply a file name if os.path.sep in save: # save includes a path information fname = save else: # else fname = os.path.normpath(save_dir_plots+'/'+save) elif save: # save is simply set to True for automatic output file naming fname = os.path.normpath( save_dir_plots + '/' + self.code+'.png' ) # saving output file if verbose: print("-- Saving file to ",fname) # creates the directory of it does not exist if os.path.sep in fname: os.makedirs(os.path.dirname(fname), exist_ok=True) plt.savefig(fname, dpi=150, facecolor='w', edgecolor='w', orientation='portrait', papertype='a4', format='png',transparent=False, pad_inches=0.1) # show if show: # do not block program execution when displaying figures plt.ion() plt.show() # returns the Gts for pyacs convention compatibility new_Gts=self.copy() return(new_Gts)
def extract_dates(self, dates, tol=0.05, in_place=False, verbose=True): ################################################################### """ Returns a time series extracted for a given list of dates :param dates: dates either as a list or 1D numpy array of decimal dates :param tol: date tolerance in days to assert that two dates are equal (default 0.05 day) :param in_place: if True, will make change in place, if False, return s a new time series :param verbose: boolean, verbose mode """ # import import inspect import numpy as np import pyacs.gts # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) # working gts new_gts = self.copy() # case .data_xyz is None if new_gts.data_xyz is None: new_gts.neu2xyz(corr=True) else: # check data/data_xyz consistency try: if not new_gts.cdata(data=True): # raise exception from pyacs.gts.lib.errors import GtsCDataError raise GtsCDataError(inspect.stack()[0][3], __name__, self) except GtsCDataError as error: print(error) return (self) new_data = None # extract dates index = np.array( pyacs.gts.Gts.get_index_from_dates(dates, self.data, tol=tol)) if verbose: print('-- Extracting ', index.shape[0], ' entries from Gts or code: ', self.code) if index.shape[0] > 0: new_data_xyz = self.data_xyz[index, :] new_sigma = self.data[index, 4:] else: new_data = None if verbose: print("-- time series ", self.code, " does not have dates at the requested dates ") # handles outliers if new_data is not None: ldate_outliers = self.data[:, 0][self.outliers] lupdated_outliers = pyacs.gts.Gts.get_index_from_dates(ldate_outliers, new_data, tol=tol) else: lupdated_outliers = [] if verbose: print('-- Transmitting ', len(lupdated_outliers), ' outliers to the extracted Gts ') # case observations new_gts.data_xyz = new_data_xyz # handle outliers ldate_outliers = self.data[:, 0][self.outliers] lupdated_outliers = pyacs.gts.Gts.get_index_from_dates(ldate_outliers, new_data_xyz, tol=0.05) # handles offsets_date upd_offsets = [] for offset_date in self.offsets_dates: if offset_date >= new_data_xyz[0, 0] and offset_date <= new_data_xyz[-1, 0]: upd_offsets.append(offset_date) # handles X0,Y0,Z0 new_gts.X0 = new_data_xyz[0, 1] new_gts.Y0 = new_data_xyz[0, 2] new_gts.Z0 = new_data_xyz[0, 3] # re-generate NEU time series new_gts.xyz2neu(corr=False) # re-populate the uncertainties columns new_gts.data[:, 4:] = new_sigma # offsets & outliers new_gts.offsets_dates = upd_offsets new_gts.outliers = lupdated_outliers if in_place: self = new_gts return (self) else: return (new_gts)
def make_model(self, option='detrend', method='L2', loutlier=None, in_place=False): """ Estimate linear model parameters using least squares input: data: Gts format option are: 'detrend'/'detrend_annual'/'detrend_seasonal' output: new Gts object: time series is now the residuals wrt to the model and its associated values (vel, annual, semi-annual etc) """ import numpy as np from pyacs.gts.Gts import Gts import inspect # after this method .data and .data_xyz are not consistent so .data_xyz is set to None self.data_xyz = None ########################################################################### # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) ########################################################################### import pyacs.lib.glinalg as glinalg import pyacs.lib.robustestimators as RobEst # from gts_estimators import least_square data = np.copy(self.data) ### REMOVE OUTLIERS FOR LINEAR ESTIMATION if loutlier: data = np.delete(data, loutlier, axis=0) ### REMOVE OFFSET DATES OUTSIDE THE TIME SPAN OF THE TIME SERIES if self.offsets_dates is not None and len(self.offsets_dates) > 0: # keep offsets_dates only within the time series sel_offsets_dates = \ [self.offsets_dates[i] for i in range(len(self.offsets_dates)) if (self.offsets_dates[i] > self.data[0, 0] and self.offsets_dates[i] < self.data[-1, 0])] noffset = len(sel_offsets_dates) ### offsets_values = [time_offsets offsets_NEU s_offsets_NEU] offsets_values = np.zeros((noffset, 7)) offsets_values[:, 0] = self.offsets_dates else: noffset = 0 offsets_values = None # REF DATES t_ref = self.data[0, 0] t_ref_seasonal = 2010.0 # INDEX if option == 'detrend_seasonal': del_index = [] n_default_unknown = 6 elif option == 'detrend_annual': del_index = [4, 5] n_default_unknown = 4 elif option == 'detrend': del_index = [2, 3, 4, 5] n_default_unknown = 2 else: print( ' ERROR!!! check the option of estimation: detrend/detrend_seasonal/detrend_annual' ) # INIT VEL, ANNUAL, SEMI_ANNUAL, RESIDUALS ARRAYS ### vel = [vel_N vel_E vel_U svel_N s_vel_E svel_U] vel = np.zeros(6) ### annual = [amplitude_NEU phase_NEU] annual = [] ### semi_annual = [amplitude_NEU phase_NEU] semi_annual = [] residuals = np.zeros(data.shape) residuals[:, 0] = data[:, 0] ndate = len(data[:, 0]) # BUILD LINEAR SYSTEM for k in range(1, 4): ## write matrix A in general case A = np.zeros([ndate, (6 + noffset)], float) for i in range(ndate): ti = data[i, 0] A[i, 0], A[i, 1], A[i, 2], A[i, 3], A[i, 4], A[i, 5] = 1., (ti - t_ref), \ np.cos(2. * np.pi * (ti - t_ref_seasonal)), np.sin( 2. * np.pi * (ti - t_ref_seasonal)), \ np.cos(4. * np.pi * (ti - t_ref_seasonal)), np.sin( 4. * np.pi * (ti - t_ref_seasonal)) ## for offsets for j in range(noffset): if ti > self.offsets_dates[j]: A[i, (6 + j)] = 1. ### take the design matrix A = np.delete(A, del_index, axis=1) # solve (X, COV, V) = glinalg.lsw_full(A, data[:, k], data[:, k + 3]) if method == 'L1': (X, V) = RobEst.Dikin(A, data[:, k], data[:, k + 3], eps=1.0E-4) s_X = np.sqrt(np.diag(COV)) ## calculate residuals, predicted mb_files residuals[:, k] = V residuals[:, k + 3] = data[:, k + 3] ## velocity vel[k - 1] = X[1] vel[k + 2] = s_X[1] ## calculate the offset amplitudes if noffset > 0: offsets_values[:, k] = X[n_default_unknown:(n_default_unknown + noffset)] offsets_values[:, k + 3] = s_X[n_default_unknown:(n_default_unknown + noffset)] if option == 'detrend_annual': ## calculate the annual motion: amplitude & phase annual.append((X[2], X[3])) if option == 'detrend_seasonal': ## calculate the annual motion: amplitude & phase annual.append((X[2], X[3])) ## calculate the annual motion: amplitude & phase semi_annual.append((X[4], X[5])) if len(annual) > 0: annual = np.hstack(annual) else: annual = None if len(semi_annual) > 0: semi_annual = np.hstack(semi_annual) else: semi_annual = None new_Gts = self.copy() new_Gts.offsets_values = offsets_values new_Gts.annual = annual new_Gts.semi_annual = semi_annual new_Gts.velocity = vel new_Gts.data = residuals if in_place: self = new_Gts.copy() del new_Gts return (self) else: return (new_Gts)
def detrend_seasonal_median(self, wl=11, in_place=False, verbose=False): ############################################################################### """ Calculates a velocity using the median of pair of displacements exactly separated by one year, inspired from MIDAS and then removes repeating yearly signal If the time series has less than three years of data, then the time series is kept untouched. """ import numpy as np from pyacs.gts.Gts import Gts import inspect # after this method .data and .data_xyz are not consistent so .data_xyz is set to None self.data_xyz = None ########################################################################### # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) ########################################################################### import pyacs.lib.astrotime duration_in_year = self.data[-1, 0] - self.data[0, 0] if duration_in_year >= 3.: H_year = {} # creates H_year arrays for year in np.arange(int(self.data[0, 0]), int(self.data[-1, 0]) + 1): H_year[year] = np.zeros((365, 3)) H_year[year][:, :] = np.nan # deal with dates np_mjd = pyacs.lib.astrotime.decyear2mjd(self.data[:, 0]) (np_doy, np_year, _np_ut) = pyacs.lib.astrotime.mjd2dayno(np_mjd) np_doy = np_doy.astype(int) np_year = np_year.astype(int) # fill H_year arrays for i in np.arange(np_doy.shape[0]): if np_doy[i] != 366: H_year[np_year[i]][np_doy[i] - 1, :] = self.data[i, 1:4] # stack the velocities np_vel = np.zeros((365 * (len(H_year) - 1), 3)) np_vel[:, :] = np.nan i = 0 for year in sorted(H_year.keys())[0:-1]: np_vel[i * 365:(i + 1) * 365] = H_year[year + 1] - H_year[year] i = i + 1 # calculates the median velocity med_vel = np.nanmedian(np_vel, axis=0) # return detrended time series detrended = self.remove_velocity(med_vel) H_year = {} # creates H_year arrays for year in np.arange(int(detrended.data[0, 0]), int(detrended.data[-1, 0]) + 1): H_year[year] = np.zeros((365, 3)) H_year[year][:, :] = np.nan # deal with dates np_mjd = pyacs.lib.astrotime.decyear2mjd(detrended.data[:, 0]) (np_doy, np_year, _np_ut) = pyacs.lib.astrotime.mjd2dayno(np_mjd) np_doy = np_doy.astype(int) np_year = np_year.astype(int) # fill H_year arrays for i in np.arange(np_doy.shape[0]): if np_doy[i] != 366: H_year[np_year[i]][np_doy[i] - 1, :] = detrended.data[i, 1:4] # center all H_year arrays for year in sorted(H_year.keys()): H_year[year] = H_year[year] - np.nanmedian(H_year[year], axis=0) # plt.plot(H_year[year][:,1]) # create the median daily signal A = np.array(list(H_year.values())) np_doy_median_signal = np.nanmedian(A[:, :, :], axis=0) # plt.plot(np_doy_median_signal[:,1],'ro') # run a median filter on it np_doy_median_signal_3 = np.vstack( (np_doy_median_signal, np_doy_median_signal, np_doy_median_signal)) import scipy.signal np_doy_median_signal[:, 0] = scipy.signal.medfilt( np_doy_median_signal_3[:, 0], wl)[365:2 * 365] np_doy_median_signal[:, 1] = scipy.signal.medfilt( np_doy_median_signal_3[:, 1], wl)[365:2 * 365] np_doy_median_signal[:, 2] = scipy.signal.medfilt( np_doy_median_signal_3[:, 2], wl)[365:2 * 365] # plt.plot(np_doy_median_signal[:,1],'bo') # remove it from the detrended time series detrended_seasonal = detrended.copy() # loop on np_doy for i in np.arange(detrended_seasonal.data.shape[0]): if np_doy[i] != 366: detrended_seasonal.data[i, 1:4] = detrended_seasonal.data[ i, 1:4] - np_doy_median_signal[np_doy[i] - 1, :] else: # time series is shorter than minimum detrended_seasonal = self.copy() return (detrended_seasonal)
def cdata(self, data=False, data_xyz=False, tol=0.001, verbose=False): ############################################################################### """ Check data/data_xyz attributes :param data: boolean, if True, data attribute will be checked :param data_xyz: boolean, if True, data_xyz attribute will be checked :param tol: tolerance in days for two dates to be considered as the same (default 0.001 of day) :param verbose: boolean, verbose mode :return : boolean, True if everything is OK, False otherwise :note : in future, this routine should also whether .data and .data_xyz value are consistent """ # import import inspect import numpy as np from pyacs.gts.lib.errors import GtsInputDataNone, GtsInputData_xyzNone, GtsInputData_allNone from pyacs.gts.lib.errors import GtsInputDataTypeError, GtsInputDataBadDim, GtsInputDataBadNcolumns, GtsInputDataBadNrows from pyacs.gts.lib.errors import GtsInputData_xyzTypeError, GtsInputData_xyzBadDim, GtsInputData_xyzBadNcolumns, GtsInputData_xyzBadNrows from pyacs.gts.lib.errors import GtsInputDataDiffShape, GtsInputDataDiffDate # CASE EITHER data OR data_xyz where asked to be checked # is there the required data try: if (self.data is None) and (data): # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (False) # is there the required data_xyz try: if (self.data_xyz is None) and (data_xyz): # raise exception raise GtsInputData_xyzNone(inspect.stack()[0][3], __name__, self) except GtsInputData_xyzNone as error: # print PYACS WARNING print(error) return (False) # GENERAL CASE # is there some data? try: if (self.data is None) and (self.data_xyz is None): # raise exception raise GtsInputData_allNone(inspect.stack()[0][3], __name__, self) except GtsInputData_allNone as error: # print PYACS WARNING print(error) return (False) # .data case if self.data is not None: # .data numpy array? try: if not isinstance(self.data, np.ndarray): # raise exception raise GtsInputDataTypeError(inspect.stack()[0][3], __name__, self) except GtsInputDataTypeError as error: # print PYACS WARNING print(error) return (False) # .data with right dimensions? try: if self.data.ndim != 2: # raise exception raise GtsInputDataBadDim(inspect.stack()[0][3], __name__, self) except GtsInputDataBadDim as error: # print PYACS WARNING print(error) return (False) # .data with the right shape? try: if self.data.shape[1] not in [7, 10]: # raise exception raise GtsInputDataBadNcolumns(inspect.stack()[0][3], __name__, self) except GtsInputDataBadNcolumns as error: # print PYACS WARNING print(error) return (False) # .data with at least one observation? try: if self.data.shape[0] < 1: # raise exception raise GtsInputDataBadNrows(inspect.stack()[0][3], __name__, self) except GtsInputDataBadNrows as error: # print PYACS WARNING print(error) return (False) # .data_xyz case if self.data_xyz is not None: # .data numpy array? try: if not isinstance(self.data_xyz, np.ndarray): # raise exception raise GtsInputData_xyzTypeError(inspect.stack()[0][3], __name__, self) except GtsInputData_xyzTypeError as error: # print PYACS WARNING print(error) return (False) # .data with right dimensions? try: if self.data_xyz.ndim != 2: # raise exception raise GtsInputData_xyzBadDim(inspect.stack()[0][3], __name__, self) except GtsInputData_xyzBadDim as error: # print PYACS WARNING print(error) return (False) try: if self.data_xyz.shape[1] not in [7, 10]: # raise exception raise GtsInputData_xyzBadNcolumns(inspect.stack()[0][3], __name__, self) except GtsInputData_xyzBadNcolumns as error: # print PYACS WARNING print(error) return (False) try: if self.data_xyz.shape[0] < 1: # raise exception raise GtsInputData_xyzBadNrows(inspect.stack()[0][3], __name__, self) except GtsInputData_xyzBadNrows as error: # print PYACS WARNING print(error) return (False) # check .data and .data_xyz consistency if (self.data is not None) and (self.data_xyz is not None): # check both have the same length try: if self.data.shape[0] != self.data_xyz.shape[0]: raise GtsInputDataDiffShape(inspect.stack()[0][3], __name__, self) except GtsInputDataDiffShape as error: # print PYACS WARNING print(error) return (False) # check both have the same dates try: if np.max( (self.data[:, 0] - self.data_xyz[:, 0])**2) > tol / 365.25: raise GtsInputDataDiffDate(inspect.stack()[0][3], __name__, self) except GtsInputDataDiffDate as error: # print PYACS WARNING print(error) return (False) return (True)
def set_zero_at_date(self, date, offset=None, in_place=False): ################################################################### """ make a translation of a time series, setting to 0 at a given date if the provided date does not exist, uses the next date available :param date: date in decimal year :param offset: an offset (in mm) to be added. Could be a float, a list or 1D numpy array with 3 elements """ # import import inspect import numpy as np # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) if offset is None: [cn, ce, cu] = [0.0, 0.0, 0.0] else: if isinstance(offset, float): [cn, ce, cu] = [offset / 1000., offset / 1000., offset / 1000.] elif isinstance(offset, list): [cn, ce, cu] = [offset[0] / 1000., offset[1] / 1000., offset[2] / 1000.] elif isinstance(offset, np.ndarray): [cn, ce, cu] = [offset[0] / 1000., offset[1] / 1000., offset[2] / 1000.] new_data = self.data.copy() lindex = np.where(new_data[:, 0] >= date) try: index = lindex[0][0] except: print("!!! bad date provided") return () print("-- Removing ", new_data[index, 1], new_data[index, 2], new_data[index, 3]) new_data[:, 1] = new_data[:, 1] - new_data[index, 1] + cn new_data[:, 2] = new_data[:, 2] - new_data[index, 2] + ce new_data[:, 3] = new_data[:, 3] - new_data[index, 3] + cu new_Gts = self.copy(data=new_data) # .data_xyz set to None new_Gts.data_xyz = None # origin XYZ would need to be changed if in_place: self.data = new_Gts.data.copy() return (new_Gts)
def detrend(self, method='L2', in_place=False, periods=[], exclude_periods=[]): ################################################################### """ detrends a time series and save velocity estimates in velocity attribute :param periods : periods used to estimate the velocity :param exclude_periods : periods to be excluded for the velocity estimate :param in_place : if True then replace the current time series :return : the detrended time series :note : outliers from Gts.outliers are omitted in the estimation and offsets given Gts.offsets_dates are estimated simultaneously """ import numpy as np from pyacs.gts.Gts import Gts import inspect # after this method .data and .data_xyz are not consistent so .data_xyz is set to None self.data_xyz = None ########################################################################### # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) ########################################################################### import copy outliers = copy.deepcopy(self.outliers) tmp_ts = self.remove_outliers() if periods != []: tmp_ts = tmp_ts.extract_periods(periods) if exclude_periods != []: tmp_ts = tmp_ts.exclude_periods(periods) detrended = tmp_ts.make_model(option='detrend', method=method) vel = detrended.velocity offsets_values = detrended.offsets_values new_gts = self.copy() new_gts.outliers = outliers new_gts.offsets_values = offsets_values new_gts.velocity = vel model = new_gts.mmodel() new_gts.data[:, 1:4] = new_gts.data[:, 1:4] - model.data[:, 1:4] if in_place: self.data = new_gts.data return (self) else: return (new_gts)
def smooth(self, window_len=11, window='hanning', in_place=False, verbose=False, component='NEU'): """ smooth a time series """ import numpy as np from pyacs.gts.Gts import Gts import inspect ########################################################################### # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return (self) ########################################################################### ################################################################### ## smoothing routines from http://wiki.scipy.org/Cookbook/SignalSmooth # changes numpy to np # JMN 18/07/2014 ################################################################### def smooth_scipy(x, window_len=11, window='hanning'): """smooth the data using a window with requested size. This method is based on the convolution of a scaled window with the signal. The signal is prepared by introducing reflected copies of the signal (with the window size) in both ends so that transient parts are minimized in the begining and end part of the output signal. input: x: the input signal window_len: the dimension of the smoothing window; should be an odd integer window: the type of window from 'flat', 'hanning', 'hamming', 'bartlett', 'blackman' flat window will produce a moving average smoothing. output: the smoothed signal example: t=linspace(-2,2,0.1) x=sin(t)+randn(len(t))*0.1 y=smooth(x) see also: numpy.hanning, numpy.hamming, numpy.bartlett, numpy.blackman, numpy.convolve scipy.signal.lfilter TODO: the window parameter could be the window itself if an array instead of a string NOTE: length(output) != length(input), to correct this: return y[(window_len/2-1):-(window_len/2)] instead of just y. """ if x.ndim != 1: raise ValueError("smooth only accepts 1 dimension arrays.") if x.size < window_len: raise ValueError( "Input vector needs to be bigger than window size.") if window_len < 3: return x if not window in [ 'flat', 'hanning', 'hamming', 'bartlett', 'blackman' ]: raise ValueError( "Window is on of 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'" ) s = np.r_[x[window_len - 1:0:-1], x, x[-1:-window_len:-1]] # print(len(s)) if window == 'flat': # moving average w = np.ones(window_len, 'd') else: w = eval('np.' + window + '(window_len)') y = np.convolve(w / w.sum(), s, mode='valid') return y new_east = smooth_scipy(self.data[:, 1], window_len=window_len, window=window) new_north = smooth_scipy(self.data[:, 2], window_len=window_len, window=window) new_up = smooth_scipy(self.data[:, 3], window_len=window_len, window=window) new_Gts = self.copy() if in_place: return (self) del new_Gts else: new_Gts.data[:, 1] = new_east[window_len // 2 - 1:new_Gts.data[:, 1].shape[0] + window_len // 2 - 1] new_Gts.data[:, 2] = new_north[window_len // 2 - 1:new_Gts.data[:, 1].shape[0] + window_len // 2 - 1] new_Gts.data[:, 3] = new_up[window_len // 2 - 1:new_Gts.data[:, 1].shape[0] + window_len // 2 - 1] return (new_Gts)
def displacement(self, sdate=None, edate=None, window=None, method='median', speriod=[], eperiod=[], rounding='day', verbose=True): ################################################################### """ Calculates displacements between two dates or two periods :param sdate: start date in decimal year :param edate: start date in decimal year :param window: time window in days for searching available dates :param method: method to calculate the position. 'median' or 'mean'. default is 'median'. :param speriod: period for calculating the start position :param eperiod: period for calculating the end position :param rounding: rounding for dates. Choose among 'day','hour','minute' or 'second'. default is 'day'. :param verbose: verbose mode :return: displacement as np.array([dn,de,du,sdn,sde,sdu]) """ # import import inspect import numpy as np import pyacs.lib.astrotime as at from colors import red # check consistency of passed arguments if (sdate is not None) and (speriod != []): print(red("[ERROR] provide sdate or speriod not both")) if (edate is not None) and (eperiod != []): print(red("[ERROR] provide edate or eperiod not both")) # check data is not None from pyacs.gts.lib.errors import GtsInputDataNone try: if self.data is None: # raise exception raise GtsInputDataNone(inspect.stack()[0][3], __name__, self) except GtsInputDataNone as error: # print PYACS WARNING print(error) return () # convert dates to seconds np_seconds = at.decyear2seconds(self.data[:, 0], rounding=rounding) # convert window to seconds if window is not None: window_s = int(window * 86400.) else: window_s = 1 # fills speriod and eperiod if speriod == []: speriod = [ at.decyear2seconds(sdate, rounding=rounding) - window_s, at.decyear2seconds(sdate, rounding=rounding) + window_s ] else: speriod = [ at.decyear2seconds(speriod[0], rounding=rounding) - window_s, at.decyear2seconds(speriod[1], rounding=rounding) + window_s ] if eperiod == []: eperiod = [ at.decyear2seconds(edate, rounding=rounding) - window_s, at.decyear2seconds(edate, rounding=rounding) + window_s ] else: eperiod = [ at.decyear2seconds(eperiod[0], rounding=rounding) - window_s, at.decyear2seconds(eperiod[1], rounding=rounding) + window_s ] # get values lindex = np.argwhere((np_seconds >= speriod[0]) & (np_seconds <= speriod[1])) np_start = self.data[lindex, 1:7].reshape(-1, 6) lindex = np.argwhere((np_seconds >= eperiod[0]) & (np_seconds <= eperiod[1])) np_end = self.data[lindex, 1:7].reshape(-1, 6) if method == 'median': pos_sdate = np.median(np_start, axis=0) pos_edate = np.median(np_end, axis=0) else: pos_sdate = np.mean(np_start, axis=0) pos_edate = np.mean(np_end, axis=0) disp = pos_edate[:3] - pos_sdate[:3] std_disp = np.sqrt(pos_sdate**2 + pos_edate**2)[3:7] displacement = [ disp[0], disp[1], disp[2], std_disp[0], std_disp[1], std_disp[2] ] return (np.array(displacement))