Як я можу використовувати numpy.correlate, щоб зробити автокореляцію?


106

Мені потрібно зробити автокореляцію набору чисел, що, як я розумію, це лише співвіднесення множини з самим собою.

Я спробував це за допомогою кореляційної функції numpy, але я не вірю в результат, оскільки він майже завжди дає вектор, де перше число не є найбільшим, як це повинно бути.

Отже, це справді два питання:

  1. Що саме numpy.correlateробить?
  2. Як я можу використовувати його (або щось інше) для автоматичної кореляції?

Дивіться також: stackoverflow.com/questions/12269834/… для інформації про нормалізовану автокореляцію.
amcnabb

Відповіді:


114

Щоб відповісти на ваше перше запитання, numpy.correlate(a, v, mode)виконується згортання aз реверсом vі надання результатів, відрізаних заданим режимом. Визначення згортки , С (т) = Е -∞ <я <∞ я v т + я де -∞ <т <∞, дозволяє зробити результати від -∞ до ∞, але ви , очевидно , не може зберігати нескінченно довго масив. Тому його потрібно відрізати, і саме там входить режим. Є 3 різних режими: повний, однаковий та дійсний:

  • "Повний" режим повертає результати для кожного, tде обидва aі vмають деяке перекриття.
  • "той самий" режим повертає результат з тією ж довжиною, що і найкоротший вектор ( aабо v).
  • "дійсний" режим повертає результати лише тоді, коли aі vповністю перекриваються один з одним. Документація для numpy.convolveдає більш детальну інформацію про режими.

Що стосується вашого другого питання, я думаю, що numpy.correlate це дає вам автокореляцію, це також дає вам ще трохи. Автокореляція використовується для пошуку того, наскільки подібний сигнал чи функція є самим собі за певної різниці в часі. За різницею в часі 0 автокореляція повинна бути найвищою, оскільки сигнал ідентичний самому собі, тому ви очікували, що перший елемент у масиві результатів автокореляції буде найбільшим. Однак кореляція не починається з різниці в часі 0. Вона починається з від’ємної різниці в часі, закривається на 0, а потім переходить у позитивну. Тобто ви очікували:

автокореляція (a) = ∑ -∞ <i <∞ a i v t + i де 0 <= t <∞

Але ви отримали:

автокореляція (a) = ∑ -∞ <i <∞ a i v t + i де -∞ <t <∞

Що вам потрібно зробити, це взяти останню половину вашого результату кореляції, і це має бути автокореляція, яку ви шукаєте. Проста функція пітона для цього буде:

def autocorr(x):
    result = numpy.correlate(x, x, mode='full')
    return result[result.size/2:]

Звичайно, вам знадобиться перевірка помилок, щоб переконатися, що xце насправді 1-денний масив. Крім того, це пояснення, мабуть, не є найбільш суворим з математики. Я кидаю навколо нескінченностей, тому що визначення їх згортки використовує їх, але це не обов'язково стосується автокореляції. Отже, теоретична частина цього пояснення може бути трохи непростою, але, сподіваємось, практичні результати корисні. Ці сторінки про автокореляцію є дуже корисними, і можуть дати вам набагато кращий теоретичний досвід, якщо ви не заперечуєте з проникненням через позначення та важкі поняття.


6
У поточних складаннях numpy може бути заданий режим "той самий", щоб досягти саме того, що запропонував А. Леві. Тіло функції потім можна було прочитатиreturn numpy.correlate(x, x, mode='same')
Девід Цвікер

13
@DavidZwicker, але результати різні! np.correlate(x,x,mode='full')[len(x)//2:] != np.correlate(x,x,mode='same'). Наприклад, x = [1,2,3,1,2]; np.correlate(x,x,mode='full');{ >>> array([ 2, 5, 11, 13, 19, 13, 11, 5, 2])} np.correlate(x,x,mode='same');{ >>> array([11, 13, 19, 13, 11])}. Правильний: np.correlate(x,x,mode='full')[len(x)-1:];{ >>> array([19, 13, 11, 5, 2])} див . Перший пункт є найбільшим .
Розробник

19
Зауважте, що ця відповідь дає ненормалізовану автокореляцію.
amcnabb

4
Я думаю, що @Developer дає правильне нарізання: [len(x)-1:]починається з 0-відставання. Оскільки fullрежим дає розмір результату 2*len(x)-1, А.Леві [result.size/2:]такий же, як [len(x)-1:]. Краще зробити це int хоч, як [result.size//2:].
Джейсон

Я виявив, що це повинен бути int, принаймні в python 3.7
kevinkayaks

25

Автокореляція буває в двох варіантах: статистичній та згортальній. Вони обидва роблять те ж саме, за винятком невеликих деталей: Статистична версія нормалізується, щоб бути на проміжку [-1,1]. Ось приклад того, як ви робите статистичний:

def acf(x, length=20):
    return numpy.array([1]+[numpy.corrcoef(x[:-i], x[i:])[0,1]  \
        for i in range(1, length)])

9
Ви хочете, щоб numpy.corrcoef[x:-i], x[i:])[0,1]у другому рядку поверталося значення corrcoefматриці 2x2
luispedro

Яка різниця між статистичною та згортковою автокореляцією?
Даніель каже, що повернеться до Моніки

1
@DanielPendergast: Друге речення відповідає на те, що: вони обидва роблять те саме, за винятком невеликих деталей: колишній [статистичний] нормалізується, щоб бути на інтервалі [-1,1]
n1k31t4

21

Використовуйте numpy.corrcoefфункцію замість numpy.correlateобчислення статистичної кореляції для відставання t:

def autocorr(x, t=1):
    return numpy.corrcoef(numpy.array([x[:-t], x[t:]]))

Чи "коефіцієнти кореляції" не відносяться до автокореляції, що використовується при обробці сигналу, а не до автокореляції, яка використовується в статистиці? en.wikipedia.org/wiki/Autocorrelation#Signal_processing
Даніель каже, що відбудеться Моніка

@DanielPendergast Я не так знайомий з обробкою сигналів. Із численних документів: "Поверніть коефіцієнти кореляції Pearson product-moment. Це версія обробки сигналів?
Ramón J Romero y Vigil

18

Я думаю, що в цій темі є дві речі, які додають плутанини:

  1. визначення статистичної проти сигналу: як уже вказували інші, у статистиці ми нормалізуємо автокореляцію на [-1,1].
  2. середня / дисперсія серед часткових та неподільних: коли часові передачі зміщуються на відставання> 0, їх розмір перекриття завжди буде <початкова довжина. Чи використовуємо ми середнє і std оригінального (не часткове) чи завжди обчислюємо нове середнє і std, використовуючи постійно змінюється перекриття (часткове), має значення. (Напевно, формальний термін для цього є, але я зараз буду використовувати "часткове").

Я створив 5 функцій, які обчислюють автоматичну кореляцію 1d масиву, з частковим та неподільним відмінностями. Деякі використовують формулу зі статистики, деякі використовують кореляти в сенсі обробки сигналу, що також можна зробити через FFT. Але всі результати є автоматичними кореляціями у визначенні статистики , тому вони ілюструють, як вони пов'язані один з одним. Код нижче:

import numpy
import matplotlib.pyplot as plt

def autocorr1(x,lags):
    '''numpy.corrcoef, partial'''

    corr=[1. if l==0 else numpy.corrcoef(x[l:],x[:-l])[0][1] for l in lags]
    return numpy.array(corr)

def autocorr2(x,lags):
    '''manualy compute, non partial'''

    mean=numpy.mean(x)
    var=numpy.var(x)
    xp=x-mean
    corr=[1. if l==0 else numpy.sum(xp[l:]*xp[:-l])/len(x)/var for l in lags]

    return numpy.array(corr)

def autocorr3(x,lags):
    '''fft, pad 0s, non partial'''

    n=len(x)
    # pad 0s to 2n-1
    ext_size=2*n-1
    # nearest power of 2
    fsize=2**numpy.ceil(numpy.log2(ext_size)).astype('int')

    xp=x-numpy.mean(x)
    var=numpy.var(x)

    # do fft and ifft
    cf=numpy.fft.fft(xp,fsize)
    sf=cf.conjugate()*cf
    corr=numpy.fft.ifft(sf).real
    corr=corr/var/n

    return corr[:len(lags)]

def autocorr4(x,lags):
    '''fft, don't pad 0s, non partial'''
    mean=x.mean()
    var=numpy.var(x)
    xp=x-mean

    cf=numpy.fft.fft(xp)
    sf=cf.conjugate()*cf
    corr=numpy.fft.ifft(sf).real/var/len(x)

    return corr[:len(lags)]

def autocorr5(x,lags):
    '''numpy.correlate, non partial'''
    mean=x.mean()
    var=numpy.var(x)
    xp=x-mean
    corr=numpy.correlate(xp,xp,'full')[len(x)-1:]/var/len(x)

    return corr[:len(lags)]


if __name__=='__main__':

    y=[28,28,26,19,16,24,26,24,24,29,29,27,31,26,38,23,13,14,28,19,19,\
            17,22,2,4,5,7,8,14,14,23]
    y=numpy.array(y).astype('float')

    lags=range(15)
    fig,ax=plt.subplots()

    for funcii, labelii in zip([autocorr1, autocorr2, autocorr3, autocorr4,
        autocorr5], ['np.corrcoef, partial', 'manual, non-partial',
            'fft, pad 0s, non-partial', 'fft, no padding, non-partial',
            'np.correlate, non-partial']):

        cii=funcii(y,lags)
        print(labelii)
        print(cii)
        ax.plot(lags,cii,label=labelii)

    ax.set_xlabel('lag')
    ax.set_ylabel('correlation coefficient')
    ax.legend()
    plt.show()

Ось вихідний показник:

введіть тут опис зображення

Ми не бачимо всіх 5 рядків, оскільки 3 з них перекриваються (у фіолетовому). Перекриття - це всі не часткові автокореляції. Це відбувається тому, що обчислення методів обробки сигналів ( np.correlate, FFT) не обчислюють різного середнього значення / STD для кожного перекриття.

Також зауважте, що fft, no padding, non-partial(червона лінія) результат відрізняється, тому що він не прокладав таймсери 0 з, перш ніж робити FFT, тому це круговий FFT. Я не можу детально пояснити, чому саме це я дізнався з інших місць.


12

Оскільки я просто зіткнувся з тією ж проблемою, я хотів би поділитися з вами кількома рядками коду. Насправді існує декілька досить схожих дописів про автокореляцію в stackoverflow. Якщо ви визначите автокореляцію як a(x, L) = sum(k=0,N-L-1)((xk-xbar)*(x(k+L)-xbar))/sum(k=0,N-1)((xk-xbar)**2)[це визначення, дане у функції a_correlate IDL, і воно узгоджується з тим, що я бачу у відповіді 2 питання №12269834 ], то, здається, дають правильні результати:

import numpy as np
import matplotlib.pyplot as plt

# generate some data
x = np.arange(0.,6.12,0.01)
y = np.sin(x)
# y = np.random.uniform(size=300)
yunbiased = y-np.mean(y)
ynorm = np.sum(yunbiased**2)
acor = np.correlate(yunbiased, yunbiased, "same")/ynorm
# use only second half
acor = acor[len(acor)/2:]

plt.plot(acor)
plt.show()

Як ви бачите, я перевірив це за допомогою кривої гріха та рівномірного випадкового розподілу, і обидва результати виглядають так, як я б їх очікував. Зауважте, що я використовував mode="same"замість того, mode="full"як робили інші.


9

Ваше запитання 1 вже широко обговорювалося тут у кількох чудових відповідях.

Я думав поділитися з вами декількома рядками коду, які дозволяють обчислити автокореляцію сигналу на основі лише математичних властивостей автокореляції. Тобто автокореляція може бути обчислена таким чином:

  1. відняти середнє від сигналу і отримати неупереджений сигнал

  2. обчислити перетворення Фур'є неупередженого сигналу

  3. обчислюють спектральну густину потужності сигналу, приймаючи квадратну норму кожного значення перетворення Фур'є непідвладного сигналу

  4. обчислити зворотне перетворення Фур'є спектральної щільності потужності

  5. нормалізувати обернене перетворення Фур'є спектральної щільності потужності за сумою квадратів неупередженого сигналу і взяти лише половину отриманого вектора

Код для цього є наступним:

def autocorrelation (x) :
    """
    Compute the autocorrelation of the signal, based on the properties of the
    power spectral density of the signal.
    """
    xp = x-np.mean(x)
    f = np.fft.fft(xp)
    p = np.array([np.real(v)**2+np.imag(v)**2 for v in f])
    pi = np.fft.ifft(p)
    return np.real(pi)[:x.size/2]/np.sum(xp**2)

чи можливо в цьому щось не так? Я не можу зіставити його результати з іншими функціями автоматичної кореляції. Функція виглядає схоже, але здається дещо скуйовдженою.
pindakaas

@pindakaas Ви можете бути більш конкретними? будь ласка, надайте інформацію про те, які виявлені невідповідності, з якими функціями.
Руджеро

Чому б не використовувати p = np.abs(f)?
dylnan

@dylnan Це дало б модулі компонентів f, тоді як тут ми хочемо вектор, що містить квадратні модулі компонентів f.
Руджеро

1
Так, але чи ти зрозумів, що розуміння списку, ймовірно, ще повільніше.
Джейсон

2

Я обчислювальний біолог, і коли мені довелося обчислити автоматичну / перехресну кореляцію між парами часових рядів стохастичних процесів, я зрозумів, що np.correlateне виконую потрібну мені роботу.

Дійсно, чого, здається, бракує np.correlate- усереднення всіх можливих пар часових точок на відстані 𝜏.

Ось як я визначив функцію, виконуючи те, що мені потрібно:

def autocross(x, y):
    c = np.correlate(x, y, "same")
    v = [c[i]/( len(x)-abs( i - (len(x)/2)  ) ) for i in range(len(c))]
    return v

Мені здається, жодна з попередніх відповідей не охоплює цей випадок автоматичної / перехресної кореляції: сподіваюся, що ця відповідь може бути корисною тому, хто працює над стохастичними процесами, як я.


1

Я використовую talib.CORREL для такої автокореляції, я підозрюю, що ви могли б зробити те ж саме з іншими пакетами:

def autocorrelate(x, period):

    # x is a deep indicator array 
    # period of sample and slices of comparison

    # oldest data (period of input array) may be nan; remove it
    x = x[-np.count_nonzero(~np.isnan(x)):]
    # subtract mean to normalize indicator
    x -= np.mean(x)
    # isolate the recent sample to be autocorrelated
    sample = x[-period:]
    # create slices of indicator data
    correls = []
    for n in range((len(x)-1), period, -1):
        alpha = period + n
        slices = (x[-alpha:])[:period]
        # compare each slice to the recent sample
        correls.append(ta.CORREL(slices, sample, period)[-1])
    # fill in zeros for sample overlap period of recent correlations    
    for n in range(period,0,-1):
        correls.append(0)
    # oldest data (autocorrelation period) will be nan; remove it
    correls = np.array(correls[-np.count_nonzero(~np.isnan(correls)):])      

    return correls

# CORRELATION OF BEST FIT
# the highest value correlation    
max_value = np.max(correls)
# index of the best correlation
max_index = np.argmax(correls)

1

Використання перетворення Фур'є та теореми згортки

Складність часу - N * log (N)

def autocorr1(x):
    r2=np.fft.ifft(np.abs(np.fft.fft(x))**2).real
    return r2[:len(x)//2]

Ось нормалізована і неупереджена версія, це також N * log (N)

def autocorr2(x):
    r2=np.fft.ifft(np.abs(np.fft.fft(x))**2).real
    c=(r2/x.shape-np.mean(x)**2)/np.std(x)**2
    return c[:len(x)//2]

Метод, запропонований А. Леві, працює, але я перевірив його на своєму ПК, його часова складність, здається, N * N

def autocorr(x):
    result = numpy.correlate(x, x, mode='full')
    return result[result.size/2:]

1

Альтернатива numpy.correlate доступна в statsmodels.tsa.stattools.acf () . Це дає функцію автокореляції, що постійно зменшується, як та, яку описала ОП. Реалізувати його досить просто:

from statsmodels.tsa import stattools
# x = 1-D array
# Yield normalized autocorrelation function of number lags
autocorr = stattools.acf( x )

# Get autocorrelation coefficient at lag = 1
autocorr_coeff = autocorr[1]

Типова поведінка - зупинятися на 40 лагах, але це можна відрегулювати за допомогою nlag=параметра для вашої конкретної програми. Внизу сторінки наведено цитування статистичних даних за функцією .


0

Я думаю, що справжня відповідь на питання ОП міститься в цьому короткому фрагменті з документації Numpy.correlate:

mode : {'valid', 'same', 'full'}, optional
    Refer to the `convolve` docstring.  Note that the default
    is `valid`, unlike `convolve`, which uses `full`.

Це означає, що при використанні без визначення режиму "функція" Numpy.correlate поверне скаляр, коли йому буде надано один і той же вектор для двох вхідних аргументів (тобто - при використанні для автокореляції).


0

Просте рішення без панд:

import numpy as np

def auto_corrcoef(x):
   return np.corrcoef(x[1:-1], x[2:])[0,1]

0

Накресліть статистичну автокореляцію з урахуванням періоду даних панди Серія повернень:

import matplotlib.pyplot as plt

def plot_autocorr(returns, lags):
    autocorrelation = []
    for lag in range(lags+1):
        corr_lag = returns.corr(returns.shift(-lag)) 
        autocorrelation.append(corr_lag)
    plt.plot(range(lags+1), autocorrelation, '--o')
    plt.xticks(range(lags+1))
    return np.array(autocorrelation)

Чому б не використовувати autocorrelation_plot()в цьому випадку? (пор. stats.stackexchange.com/questions/357300/… )
Qaswed
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.