def stack(stream, length=None, move=None): """ Stack traces in stream by correlation id :param stream: |Stream| object with correlations :param length: time span of one trace in the stack in seconds (alternatively a string consisting of a number and a unit -- ``'d'`` for days and ``'h'`` for hours -- can be specified, i.e. ``'3d'`` stacks together all traces inside a three days time window, default: None, which stacks together all traces) :param move: define a moving stack, float or string, default: None -- no moving stack, if specified move usually is smaller than length to get an overlap in the stacked traces :return: |Stream| object with stacked correlations """ stream.sort() stream_stack = obspy.Stream() ids = {_corr_id(tr) for tr in stream} ids.discard(None) for id_ in ids: traces = [tr for tr in stream if _corr_id(tr) == id_] if length is None: data = np.mean([tr.data for tr in traces], dtype=float, axis=0) tr_stack = obspy.Trace(data, header=traces[0].stats) tr_stack.stats.key = tr_stack.stats.key + '_s' if 'num' in traces[0].stats: tr_stack.stats.num = sum(tr.stats.num for tr in traces) else: tr_stack.stats.num = len(traces) stream_stack.append(tr_stack) else: t1 = traces[0].stats.starttime lensec = _time2sec(length) movesec = _time2sec(move) if move else lensec if (lensec % (24 * 3600) == 0 or isinstance(length, str) and 'd' in length): t1 = UTC(t1.year, t1.month, t1.day) elif (lensec % 3600 == 0 or isinstance(length, str) and 'm' in length): t1 = UTC(t1.year, t1.month, t1.day, t1.hour) t2 = max(t1, traces[-1].stats.endtime - lensec) for t in IterTime(t1, t2, dt=movesec): sel = [ tr for tr in traces if -0.1 <= tr.stats.starttime - t <= lensec + 0.1 ] if len(sel) == 0: continue data = np.mean([tr.data for tr in sel], dtype=float, axis=0) tr_stack = obspy.Trace(data, header=sel[0].stats) key_add = '_s%s' % length + (move is not None) * ('m%s' % move) tr_stack.stats.key = tr_stack.stats.key + key_add tr_stack.stats.starttime = t if 'num' in traces[0].stats: tr_stack.stats.num = sum(tr.stats.num for tr in sel) else: tr_stack.stats.num = len(sel) stream_stack.append(tr_stack) return stream_stack
def plot_corr_vs_dist(stream, fname, figsize=(10, 5), ext='png', dpi=None, components='ZZ', line_style='k', scale=1, dist_unit='km', xlim=None, ylim=None, time_period=None): """ Plot stacked correlations versus inter-station distance .. image:: _static/corr_vs_dist.png :width: 30% This plot can be created from the command line with ``--plottype vs_dist``. :param components: component combination to plot :param scale: scale wiggles (default 1) :param dist_unit: one of ``('km', 'm', 'deg')`` :time_period: use correlations only from this time span (tuple of dates) """ # scale relative to axis traces = [ tr for tr in stream if _corr_id(tr).split('-')[0][-1] + _corr_id(tr)[-1] == components ] stream.traces = traces _trim_time_period(stream, time_period) stack = yam.stack.stack(stream) if len(stack) == 0: msg = 'Not plotting anything. No traces in stack with components %s' warn(msg % components) return fig = plt.figure(figsize=figsize) ax = fig.add_subplot(111) dist_scale = (1 if dist_unit == 'm' else 1000 * 111.2 if dist_unit == 'deg' else 1000) max_dist = max(tr.stats.dist / dist_scale for tr in stack) for tr in stack: lag_times = _trim(tr, xlim) scaled_data = tr.stats.dist / dist_scale + tr.data * max_dist * scale ax.plot(lag_times, scaled_data, line_style) fname = '%s_%s' % (fname, components) label = os.path.basename(fname) ax.set_ylim(ylim) ax.annotate(label, (0, 1), (10, 10), 'axes fraction', 'offset points', annotation_clip=False, va='bottom') ax.set_ylabel('distance (%s)' % dist_unit) ax.set_xlabel('time (s)') fig.savefig(fname + '.' + ext, dpi=dpi)
def plot_corr_vs_time_wiggle(stream, fname=None, figsize=(10, 5), ext='png', dpi=None, xlim=None, ylim=None, scale=20, plot_kw={}): """ Plot correlation wiggles versus time .. image:: _static/corr_vs_time_wiggle.png :width: 30% .. image:: _static/corr_vs_time_wiggle2.png :width: 40% This plot can be created from the command line with ``--plottype wiggle``. :param scale: scale of wiggles (default 20) """ plot_kw = plot_kw.copy() plot_kw.setdefault('lw', 0.5) plot_kw.setdefault('color', 'black') # scale relative to neighboring wiggles ids = {_corr_id(tr) for tr in stream} if len(ids) != 1: warn('Different ids in stream: %s' % ids) stream.sort(['starttime']) _trim_time_period(stream, ylim) times = [tr.stats.starttime.matplotlib_date for tr in stream] dt = np.median(np.diff(times)) fig = plt.figure(figsize=figsize) ax = fig.add_subplot(111) for tr in stream: lag_times = _trim(tr, xlim) scaled_data = tr.stats.starttime.matplotlib_date + tr.data * dt * scale ax.plot(lag_times, scaled_data, **plot_kw) label = '' if fname is None else os.path.basename(fname) ax.annotate(label, (0, 1), (10, 10), 'axes fraction', 'offset points', annotation_clip=False, va='bottom') ax.set_ylabel('date') ax.set_xlabel('time (s)') ax.yaxis_date() if fname is not None: fig.savefig(fname + '.' + ext, dpi=dpi)
def stretch(stream, max_stretch, num_stretch, tw, tw_relative=None, reftr=None, sides='both', max_lag=None, time_period=None): """ Stretch traces in stream and return dictionary with results See e.g. Richter et al. (2015) for a description of the procedure. :param stream: |Stream| object with correlations :param float max_stretch: stretching range in percent :param int num_stretch: number of values in stretching vector :param tw: definition of the time window in the correlation -- tuple of length 2 with start and end time in seconds (positive) :param tw_relative: time windows can be defined relative to a velocity, default None or 0 -- time windows relative to zero lag time, otherwise velocity is given in km/s :param reftr: reference trace, by default the stack of stream is used as reference :param sides: one of left, right, both :param max_lag: max lag time in seconds, stream is trimmed to ``(-max_lag, max_lag)`` before stretching :param time_period: use correlations only from this time span (tuple of dates) """ if len(stream) <= 1: log.warning('For stretch the stream must have a minimum length of 2') return ids = {_corr_id(tr) for tr in stream} if None in ids: ids.discard(None) stream.traces = [tr for tr in stream if _corr_id(tr) is not None] if len(ids) != 1: warn('Different ids in stream: %s' % ids) _trim_time_period(stream, time_period) stream.sort() tr0 = stream[0] if max_lag is not None: for tr in stream: _trim(tr, (-max_lag, max_lag)) rel = 0 if tw_relative is None else tr0.stats.dist / 1000 / tw_relative twabs = rel + np.array(tw) starttime = tr0.stats.starttime mid = starttime + (tr0.stats.endtime - starttime) / 2 times = tr0.times(reftime=mid) data = np.array([tr.data for tr in stream]) data[np.isnan(data)] = 0 # bug fix data[np.isinf(data)] = 0 if reftr is None: reftr = yam.stack.stack(stream)[0] stretch_vector_perc = np.linspace(-max_stretch, max_stretch, num_stretch) stretch_factor = 1 + stretch_vector_perc / 100 # MIIC approximation: #stretch_factor = np.exp(stretch_vector_percent / 100) mask1 = np.logical_and(times >= twabs[0], times <= twabs[1]) mask2 = np.logical_and(times <= -twabs[0], times >= -twabs[1]) if sides == 'left': mask = mask1 elif sides == 'right': mask = mask2 elif sides == 'both': mask = np.logical_or(mask1, mask2) else: raise ValueError('sides = %s not a valid option' % sides) sim_mat = _stretch_helper(data, reftr.data, mask, stretch_factor) times = np.array([str(tr.stats.starttime)[:19] for tr in stream], dtype='S19') result = { 'sim_mat': sim_mat, 'velchange_values': stretch_vector_perc, 'times': times, 'tw': twabs, 'attrs': { 'num_stretch': num_stretch, 'max_stretch': max_stretch, 'sides': sides, 'starttime': stream[0].stats.starttime, 'endtime': stream[-1].stats.starttime } } _update_result(result) for k in ('network1', 'network2', 'station1', 'station2', 'location1', 'location2', 'channel1', 'channel2', 'sampling_rate', 'dist', 'azi', 'baz'): if k in tr0.stats: result['attrs'][k] = tr0.stats[k] return result
def stretch(stream, reftr=None, str_range=10, nstr=100, time_windows=None, time_windows_relative=None, sides='right', max_lag=None, time_period=None ): """ Stretch traces in stream and return dictionary with results See e.g. Richter et al. (2015) for a description of the procedure. :param stream: |Stream| object with correlations :param reftr: reference trace -- not implemented yet, the reference is the stack of the stream :param float str_range: stretching range in percent :param int nstr: number of values in stretching vector :param time_windows: definition of time windows in the correlation -- tuple of length 2, first entry: tuple of start times in seconds, second entry: length in seconds, e.g. ``((5, 10, 15), 5)`` defines three time windows of length 5 seconds starting at 5, 10, 15 seconds :param time_windows_relative: time windows can be defined relative to a velocity, default None or 0 -- time windows relative to zero leg time, otherwise velocity is given in km/s :param max_lag: max lag time in seconds, stream is trimmed to ``(-max_lag, max_lag)`` before stretching :param time_period: use correlations only from this time span (tuple of dates) """ if len(stream) <= 1: log.warning('For stretch the stream must have a minimum length of 2') return ids = {_corr_id(tr) for tr in stream} if None in ids: ids.discard(None) stream.traces = [tr for tr in stream if _corr_id(tr) is not None] if len(ids) != 1: warn('Different ids in stream: %s' % ids) _trim_time_period(stream, time_period) stream.sort() sr = stream[0].stats.sampling_rate rel = 0. if time_windows_relative is not None: rel = np.round(stream[0].stats.dist / 1000 / time_windows_relative) if time_windows is not None and isinstance(time_windows[1], (float, int)): args = ((rel + np.array(time_windows[0])) * int(sr), time_windows[1] * int(sr)) tw_mat = time_windows_creation(*args) else: raise ValueError('Wrong format for time_window') if max_lag is not None: for tr in stream: _trim(tr, (-max_lag, max_lag)) data = np.array([tr.data for tr in stream]) if np.min(tw_mat) < 0 or data.shape[1] < np.max(tw_mat): msg = ('Defined time window outside of data. ' 'shape, mintw index, maxtw index: %s, %s, %s') raise ValueError(msg % (data.shape[1], np.min(tw_mat), np.max(tw_mat))) data[np.isnan(data)] = 0 # bug fix data[np.isinf(data)] = 0 if reftr is None: reftr = yam.stack.stack(stream)[0] if reftr != 'alternative': if hasattr(reftr, 'stats'): assert reftr.stats.sampling_rate == sr ref_data = reftr.data #ref_data = _stream2matrix(obspy.Stream([reftr]))[0, :] else: ref_data = reftr # log.debug('calculate correlations and time shifts...') # convert str_range from % to decimal tse = time_stretch_estimate( data, ref_trc=ref_data, tw=tw_mat, stretch_range=str_range / 100, stretch_steps=nstr, sides=sides) # else: # assert len(tw_mat) == len(stretch) # tses = [] # log.debug('calculate correlations and time shifts...') # for i in range(len(tw_mat)): # tw = tw_mat[i:i + 1] # st = stretch[i] # sim_mat = time_stretch_apply(data, st, single_sided=False) # ref_data = np.mean(sim_mat, axis=0) # tse = time_stretch_estimate(data, ref_trc=ref_data, tw=tw, # stretch_range=str_range, stretch_steps=nstr, # sides=sides) # tses.append(tse) # for i in ('corr', 'stretch'): # tse[i] = np.hstack([t[i] for t in tses]) # i = 'sim_mat' # tse[i] = np.vstack([t[i] for t in tses]) times = np.array([str(tr.stats.starttime)[:19] for tr in stream], dtype='S19') ltw1 = rel + np.array(time_windows[0]) # convert streching to velocity change # -> minus at several places result = {'sim_mat': tse['sim_mat'][:, ::-1, :], 'velchange_values': -tse['second_axis'][::-1] * 100, 'times': times, #'velchange_vs_time': -tse['value'] * 100, #'corr_vs_time': tse['corr'], 'lag_time_windows': np.transpose([ltw1, ltw1 + time_windows[1]]), 'attrs': {'nstr': nstr, 'str_range': str_range, 'sides': sides, 'starttime': stream[0].stats.starttime, 'endtime': stream[-1].stats.starttime } } _update_result(result) for k in ('network1', 'network2', 'station1', 'station2', 'location1', 'location2', 'channel1', 'channel2', 'sampling_rate', 'dist', 'azi', 'baz'): result['attrs'][k] = stream[0].stats[k] return result
def plot_corr_vs_time(stream, fname, figsize=(10, 5), ext='png', xlim=None, ylim=None, vmax=None, cmap='RdBu_r', show_stack=True, line_style='k', line_width=1): """ Plot correlations versus time .. image:: _static/corr_vs_time.png :width: 30% .. image:: _static/corr_vs_time_zoom.png :width: 30% Default correlation plot. :param vmax: maximum value in colormap :param cmap: used colormap :param show_stack: show a wiggle plot of the stack at top """ ids = {_corr_id(tr) for tr in stream} if len(ids) != 1: warn('Different ids in stream: %s' % ids) stream.sort(['starttime']) _trim_time_period(stream, ylim) for tr in stream: lag_times = _trim(tr, xlim) times = [tr.stats.starttime.matplotlib_date for tr in stream] fig = plt.figure(figsize=figsize) ax = fig.add_axes([0.15, 0.1, 0.75, 0.75]) cax = fig.add_axes([0.91, 0.375, 0.008, 0.25]) data = np.array([tr.data for tr in stream]) if vmax is None: vmax = min(0.8 * np.max(data), 0.1) no_data = _get_times_no_data(times) times = _add_value(times, no_data) times2 = _align_values_for_pcolormesh(times) lag_times2 = _align_values_for_pcolormesh(lag_times) data2 = _add_value(np.transpose(data), no_data, value=0, masked=True) data2 = np.transpose(data2) mesh = ax.pcolormesh(lag_times2, times2, data2, cmap=cmap, vmin=-vmax, vmax=vmax) fig.colorbar(mesh, cax) ax.set_ylabel('date') ax.set_xlabel('time (s)') ax.yaxis_date() ax_ano = ax if show_stack: ax2 = fig.add_axes([0.15, 0.85, 0.75, 0.05], sharex=ax) stack = yam.stack.stack(stream) ax2.plot(lag_times, stack[0].data, line_style, lw=line_width) ax2.set_ylim(-vmax, vmax) ax2.set_ylabel('stack') plt.setp(ax2.get_xticklabels(), visible=False) ax_ano = ax2 ax.set_xlim(lag_times[0], lag_times[-1]) label = os.path.basename(fname) ax_ano.annotate(label, (0, 1), (10, 10), 'axes fraction', 'offset points', annotation_clip=False, va='bottom') fig.savefig(fname + '.' + ext)
def plot_corr_vs_time(stream, fname=None, figsize=(10, 5), ext='png', dpi=None, xlim=None, ylim=None, vmax=None, cmap='RdBu_r', stack_plot_kw={}): """ Plot correlations versus time .. image:: _static/corr_vs_time.png :width: 30% .. image:: _static/corr_vs_time_zoom.png :width: 30% Default correlation plot. :param vmax: maximum value in colormap :param cmap: used colormap """ ids = {_corr_id(tr) for tr in stream} srs = {tr.stats.sampling_rate for tr in stream} lens = {len(tr) for tr in stream} if len(ids) != 1: warn('Different ids in stream: %s' % ids) # These checks should be done when saving the correlations to hdf5. # On the other hand side, these problems will not occur often. if len(srs) != 1: sr = np.median([tr.stats.sampling_rate for tr in stream]) msg = 'Different sampling rates in stream: %s -> set %s Hz (%s)' warn(msg % (srs, sr, stream[0].id)) for tr in stream: tr.stats.sampling_rate = sr if len(lens) != 1: msg = ('Different lengths of traces in stream: %s (%s)' '-> Plese use xlim parameter to trim traces') warn(msg % (lens, stream[0].id)) stream.sort(['starttime']) _trim_time_period(stream, ylim) for tr in stream: lag_times = _trim(tr, xlim) times = [tr.stats.starttime.matplotlib_date for tr in stream] fig = plt.figure(figsize=figsize) ax = fig.add_axes([0.15, 0.1, 0.75, 0.75]) cax = fig.add_axes([0.91, 0.375, 0.008, 0.25]) data = np.array([tr.data for tr in stream]) if vmax is None: vmax = min(0.8 * np.max(data), 0.1) no_data = _get_times_no_data(times) times = _add_value(times, no_data) times2 = _align_values_for_pcolormesh(times) lag_times2 = _align_values_for_pcolormesh(lag_times) data2 = _add_value(np.transpose(data), no_data, value=0, masked=True) data2 = np.transpose(data2) mesh = ax.pcolormesh(lag_times2, times2, data2, cmap=cmap, vmin=-vmax, vmax=vmax) fig.colorbar(mesh, cax) ax.set_ylabel('date') ax.set_xlabel('time (s)') ax.yaxis_date() ax_ano = ax if stack_plot_kw is not None: stack_plot_kw = stack_plot_kw.copy() stack_plot_kw.setdefault('color', 'black') stack_plot_kw.setdefault('lw', 1) ax2 = fig.add_axes([0.15, 0.85, 0.75, 0.05], sharex=ax) stack = yam.stack.stack(stream) ax2.plot(lag_times, stack[0].data, **stack_plot_kw) ax2.set_ylim(-vmax, vmax) ax2.set_ylabel('stack') plt.setp(ax2.get_xticklabels(), visible=False) ax_ano = ax2 ax.set_xlim(lag_times[0], lag_times[-1]) label = '' if fname is None else os.path.basename(fname) ax_ano.annotate(label, (0, 1), (10, 10), 'axes fraction', 'offset points', annotation_clip=False, va='bottom') if fname is not None: fig.savefig(fname + '.' + ext, dpi=dpi)