This is identical to the Z-score we learned before. Effectively, our data now has zero mean and unit standard deviation.
def norm_data(data):# normalize data to have mean=0 and standard_deviation=1return (data - data.mean())/ data.std()s1 = norm_data(signal1)s2 = norm_data(signal2)
If the autocorrelation helps us check how similar a signal is to a lagged version of itself, the cross correlation measures how a signal X is similar to a lagged version of another signal Y.
Let’s write this ourselves:
def cross_corr(s1, s2):""" we assume s1 and s2 to have the same length """ N =len(s1) lags_left = np.arange(-(N-1), 1) lags_right = np.arange(1, N) cc_left = np.array( [np.sum(s2[-i:]*s1[:i]) for i in np.arange(1, len(lags_left)+1)] ) cc_right = np.array( [np.sum(s2[:-i]*s1[i:]) for i in np.arange(1, len(lags_right)+1)] ) lags = np.hstack([lags_left, lags_right]) cc = np.hstack([cc_left, cc_right]) / Nreturn lags, cc
Of course, there are available functions that accomplish the same, let’s compare them with our code:
# Define a function to plot the time series with a shiftdef plot_time_series(shift=-500): sh = shift + N +1 fig, ax = plt.subplots(2, 1, figsize=(8, 6)) fig.subplots_adjust(hspace=0.3) # increase vertical space between panels ax[0].plot(lag_padded * delta_t, s1_padded, label="normalized 1") ax[0].plot(lag_padded * delta_t, s2_padded[2*N-sh:-sh], label="normalized 2", zorder=0) ax[0].set(xlabel="time", ylabel="normalized signal", xlim=[delta_t*lag_padded.min(), delta_t*lag_padded.max()]) ax[1].plot(lags, corr_np, color="black", alpha=0.2) ax[1].plot(lags[:sh], corr_np[:sh], lw=3, color="xkcd:hot pink") ax[1].set(xlabel="lag (pts)", ylabel="CCF", xlim=[lags.min(), lags.max()], ylim=[-1,1]) plt.show()# Use interact to create the widgetinteract(plot_time_series, shift=IntSlider(min=-500, max=499, step=1, value=-500))# Add custom CSS to change the length of the sliderdisplay(HTML("""<style>.widget-slider { width: 100%;}</style>"""))