0
$\begingroup$

I would like to know the meaning of an autocorrelation graph of a sine wave. When the time lag is 0, then the autocorrelation should give the highest value of 1 since a copy of the signal is completely correlated to itself. By this logic, after a time equal to the period of the signal, the correlation should again be maximum since the shifted signal is again the signal itself. However, when I plot the correlation in python, I get a function which keeps increasing which goes against my intuition that the correlation function should be periodic. Could anyone please explain why the autocorrelation shows this trend in its graphical form?

import matplotlib.pyplot as plt
import numpy as np
time = np.arange(0, 10, 0.1);
y = np.sin(time)
result = np.correlate(y, y, mode='full')
plt.plot(result[:int(result.size/2 )])
plt.show()

enter image description here

$\endgroup$
  • 1
    $\begingroup$ Perhaps the tool you’re looking for is Statsmodels’s plot_acf and plot_pacf functions? $\endgroup$ – exchez Mar 8 '18 at 9:36
3
$\begingroup$

This is not a real statistical effect, but rather it is due to the way that the numpy.correlate function works, which isn't really suited for what you want to do (at least not without applying some correction afterwards).

First of all, it doesn't compute a correlation coefficient in the typical statistical sense. It starts off the same, computing a dot product between the two input vectors, but then it doesn't normalize them (as in, e.g., a Pearson's correlation coefficient), so the result isn't between [-1, 1]. So the result you get is simply the sum of the pairwise products of the entries in the two vectors.

If you use numpy.correlate with the 'full' setting, it computes this product for every possible 'lag' between the two vectors. In other words, it 'slides' one vector over the other, and takes the dot product between pairs for which both vectors have an entry. The example given in the SciPy documentation is helpful:

>>> np.correlate([1, 2, 3], [0, 1, 0.5], "full")
array([ 0.5,  2. ,  3.5,  3. ,  0. ])

If we examine the output on the second line, we see that the first entry is simply the product between 1 & 0.5, i.e the first and last entries of the two vectors. The second entry is the dot product between the first two entries of the first, and the last two of the second vector, i.e. 1*1 + 2*0.5 = 2, etc.

So in your case, this means that the first value in your graph is just the product between the first value of the sine wave and the last value, which is obviously rather small. As you slide the vectors over each other, you get more and more valid products, which add up to bigger and bigger numbers. So what you're effectively looking at here is the true (periodic) autocorrelation function, multiplied by a line with positive slope (with this line reflecting the increasing number of entries contributing to the dot product), which explains why it seems to 'blow up' at increasing lags.

This really isn't what you want for an autocorrelation function, which should be properly normalized. So I guess the upshot is: you should probably use a different function to compute your correlation that matches the usual statistical definition (e.g. numpy.corrcoef, and/or a wrapper around that function that handles the lags for which you want to compute the autocorrelation), or work out how to normalize the output you get from numpy.corr.

$\endgroup$
  • $\begingroup$ Thanks for the insight! Would this approach of finding autocorrelation be a good way to identify patterns in a periodic signal? My train of thought was, If autocorrelation worked like I thought it did, then i would be able to detect periodic regions in a signal with the length of the time period being the distant between two maximas in the autocorrelation plot. I wanted your opinion on this. In addition to this, do you know any other simple way to detect cycles in a signal? I guess RNNs could do it, but I feel that would be overkill for my application $\endgroup$ – Ashish Rao M Mar 8 '18 at 9:25
  • $\begingroup$ I would try a Fourier transform. The Fourier transform of a signal with some periodic components will show high power at the frequencies of those components (and typically also at the harmonics of those frequencies). A signal with no periodicity will have a more random/homogeneous power spectrum. $\endgroup$ – Ruben van Bergen Mar 8 '18 at 9:55
  • $\begingroup$ A Fourier transform certainly seems like a better alternative to what I was originally trying to do. Thanks a lot for your inputs. $\endgroup$ – Ashish Rao M Mar 8 '18 at 10:21
0
$\begingroup$

As an addition to the excellent answer by Ruben van Bergen, an alternative method to plot the autocorrelation function of a sine:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
time = np.arange(0, 10*np.pi, 0.1);
y = np.sin(time)
result = [pd.Series(y).autocorr(n) for n in range(100)]
plt.plot(time[:100], result)
plt.show()

the autocorrelation of a sine

$\endgroup$
0
$\begingroup$

It's too late to answer the OP's question but I write this in the hope to help someone who might be looking for an answer. Your understanding about autocorrelation function is correct, it should be maximum when the signal is in most agreement with itself, which is at t=0. The result that you see is because you plot only half the values of the autocorrelation. Also, in your plot, the x-axis is not correctly labelled. Usually the autocorrelation is plotted against the 'time delay' or lag between the signals and not the index of the result starting from 0 like you have shown in your plot. If t1 and t2 are the time length of the two signals, then the time delay between the signals ranges from -t1 to t2. If the x-axis is correctly labelled, the values of autocorrelation given by np.correlate() will make a lot of sense.

import matplotlib.pyplot as plt
import numpy as np
time = np.arange(0, 10, 0.1);
y = np.sin(time)
result = np.correlate(y, y, mode='full')
lags = np.arange(-time[-1],time[-1]+0.1,0.1) #adding 0.1 to include the last instant of time also
plt.figure()
plt.plot(lags,result)
plt.xlabel('Lag')
plt.ylabel('autocorrelation')
plt.show()

AutoCorrelation of a sine wave Now it can be seen that the autocorrelation is maximum at a lag of 0 and the next peak of the autocorrelation function is at the time period of the signal, just as expected in the question. The value of the second correlation peak is lesser because of the fixed length of the signal (=10s) and shifting the signal by its own time period (approx. 6.28s) leads to a portion of the signal not having matching values, simply just because the function is not defined outside the range [0,10).

The outputs from np.correlate () or scipy.signal.correlate() do not make it very clear to understand this, indeed the first intiution to understand the correlation result would be to plot the correlation against its index. The documentation and the function outputs of the equivalent MATLAB function xcorr make this clear, it even gives the lags at which the correlation values are computed as one of the outputs.

Hope this helps.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.