Найти разность фаз между двумя (ангармоническими) волнами

У меня есть два набора данных, в которых перечислены средние выходы напряжения двух сборок нейронных сетей в моменты времени t, которые выглядят примерно так:

A = [-80.0, -80.0, -80.0, -80.0, -80.0, -80.0, -79.58, -79.55, -79.08, -78.95, -78.77, -78.45,-77.75, -77.18, -77.08, -77.18, -77.16, -76.6, -76.34, -76.35]

B = [-80.0, -80.0, -80.0, -80.0, -80.0, -80.0, -78.74, -78.65, -78.08, -77.75, -77.31, -76.55, -75.55, -75.18, -75.34, -75.32, -75.43, -74.94, -74.7, -74.68]

Когда две нейронные сборки "находятся в фазе" в разумной степени, это означает, что они взаимосвязаны. Я хочу рассчитать разность фаз между А и В, желательно за все время моделирования. Поскольку две сборки вряд ли будут полностью в фазе, я хочу сравнить эту разность фаз с определенным порогом.

Это ангармонические осцилляторы, и я не знаю их функций, только эти значения, поэтому я понятия не имею, как определить фазу или соответствующую разность фаз.

Я делаю этот проект на Python, используя numpy и scipy (две сборки - массивы numpy).

Любые предложения будут очень благодарны!

EDIT: Добавлены графики

Пример файла данных для сборки 1

Пример файла данных для сборки 2

Вот пример того, как выглядят два набора данных: Plot of two neural assemblies overlappingPlot of two neural assemblies seperate

Ответы

Ответ 1

Возможно, вы ищете кросс-корреляцию:

scipy.​signal.​signaltools.correlate(A, B)

Положение пика в кросс-корреляции будет оценкой разности фаз.

РЕДАКТИРОВАТЬ 3: Обновить сейчас, когда я посмотрел реальные файлы данных. Есть две причины, по которым вы находите фазовый сдвиг нуля. Во-первых, фазовый сдвиг действительно равен нулю между двумя вашими временными рядами. Вы можете это четко увидеть, если увеличить масштаб на графике matplotlib горизонтально. Во-вторых, важно сначала упорядочить данные (что самое главное, вычесть среднее значение), в противном случае эффект нулевого заполнения на концах массивов болотирует реальный сигнал в кросс-корреляции. В следующем примере я проверяю, что я нахожу "истинный" пик, добавляя искусственный сдвиг, а затем проверяя, что я его правильно восстанавливаю.

import numpy, scipy
from scipy.signal import correlate

# Load datasets, taking mean of 100 values in each table row
A = numpy.loadtxt("vb-sync-XReport.txt")[:,1:].mean(axis=1)
B = numpy.loadtxt("vb-sync-YReport.txt")[:,1:].mean(axis=1)

nsamples = A.size

# regularize datasets by subtracting mean and dividing by s.d.
A -= A.mean(); A /= A.std()
B -= B.mean(); B /= B.std()

# Put in an artificial time shift between the two datasets
time_shift = 20
A = numpy.roll(A, time_shift)

# Find cross-correlation
xcorr = correlate(A, B)

# delta time array to match xcorr
dt = numpy.arange(1-nsamples, nsamples)

recovered_time_shift = dt[xcorr.argmax()]

print "Added time shift: %d" % (time_shift)
print "Recovered time shift: %d" % (recovered_time_shift)

# SAMPLE OUTPUT:
# Added time shift: 20
# Recovered time shift: 20

EDIT: Вот пример того, как он работает с поддельными данными.

EDIT 2: Добавлен график примера.

Cross correlation of noisy anharmonic signals

import numpy, scipy
from scipy.signal import square, sawtooth, correlate
from numpy import pi, random

period = 1.0                            # period of oscillations (seconds)
tmax = 10.0                             # length of time series (seconds)
nsamples = 1000
noise_amplitude = 0.6

phase_shift = 0.6*pi                   # in radians

# construct time array
t = numpy.linspace(0.0, tmax, nsamples, endpoint=False)

# Signal A is a square wave (plus some noise)
A = square(2.0*pi*t/period) + noise_amplitude*random.normal(size=(nsamples,))

# Signal B is a phase-shifted saw wave with the same period
B = -sawtooth(phase_shift + 2.0*pi*t/period) + noise_amplitude*random.normal(size=(nsamples,))

# calculate cross correlation of the two signals
xcorr = correlate(A, B)

# The peak of the cross-correlation gives the shift between the two signals
# The xcorr array goes from -nsamples to nsamples
dt = numpy.linspace(-t[-1], t[-1], 2*nsamples-1)
recovered_time_shift = dt[xcorr.argmax()]

# force the phase shift to be in [-pi:pi]
recovered_phase_shift = 2*pi*(((0.5 + recovered_time_shift/period) % 1.0) - 0.5)

relative_error = (recovered_phase_shift - phase_shift)/(2*pi)

print "Original phase shift: %.2f pi" % (phase_shift/pi)
print "Recovered phase shift: %.2f pi" % (recovered_phase_shift/pi)
print "Relative error: %.4f" % (relative_error)

# OUTPUT:
# Original phase shift: 0.25 pi
# Recovered phase shift: 0.24 pi
# Relative error: -0.0050

# Now graph the signals and the cross-correlation

from pyx import canvas, graph, text, color, style, trafo, unit
from pyx.graph import axis, key

text.set(mode="latex")
text.preamble(r"\usepackage{txfonts}")
figwidth = 12
gkey = key.key(pos=None, hpos=0.05, vpos=0.8)
xaxis = axis.linear(title=r"Time, \(t\)")
yaxis = axis.linear(title="Signal", min=-5, max=17)
g = graph.graphxy(width=figwidth, x=xaxis, y=yaxis, key=gkey)
plotdata = [graph.data.values(x=t, y=signal+offset, title=label) for label, signal, offset in (r"\(A(t) = \mathrm{square}(2\pi t/T)\)", A, 2.5), (r"\(B(t) = \mathrm{sawtooth}(\phi + 2 \pi t/T)\)", B, -2.5)]
linestyles = [style.linestyle.solid, style.linejoin.round, style.linewidth.Thick, color.gradient.Rainbow, color.transparency(0.5)]
plotstyles = [graph.style.line(linestyles)]
g.plot(plotdata, plotstyles)
g.text(10*unit.x_pt, 0.56*figwidth, r"\textbf{Cross correlation of noisy anharmonic signals}")
g.text(10*unit.x_pt, 0.33*figwidth, "Phase shift: input \(\phi = %.2f \,\pi\), recovered \(\phi = %.2f \,\pi\)" % (phase_shift/pi, recovered_phase_shift/pi))
xxaxis = axis.linear(title=r"Time Lag, \(\Delta t\)", min=-1.5, max=1.5)
yyaxis = axis.linear(title=r"\(A(t) \star B(t)\)")
gg = graph.graphxy(width=0.2*figwidth, x=xxaxis, y=yyaxis)
plotstyles = [graph.style.line(linestyles + [color.rgb(0.2,0.5,0.2)])]
gg.plot(graph.data.values(x=dt, y=xcorr), plotstyles)
gg.stroke(gg.xgridpath(recovered_time_shift), [style.linewidth.THIck, color.gray(0.5), color.transparency(0.7)])
ggtrafos = [trafo.translate(0.75*figwidth, 0.45*figwidth)]
g.insert(gg, ggtrafos)
g.writePDFfile("so-xcorr-pyx")

Таким образом, он работает очень хорошо, даже для очень шумных данных и очень агармонических волн.

Ответ 2

@deprecated comments - это точный ответ на вопрос, когда дело доходит до решения python с чистым кодом. Комментарии были очень ценными, но я чувствую, что должен добавить несколько заметок для людей, которые ищут ответ в конкретном контексте нейронных сетей.

Когда вы берете средний мембранный потенциал больших сборок нейронов, как и я, корреляция будет относительно слабой. В первую очередь, вы хотите рассмотреть либо корреляцию между шипами, латентностью или возбудимостью (т.е. Синаптической эффективностью) отдельных сборок. Это можно относительно легко найти, просто взглянув на точки, где потенциал превышает определенный порог. Исключительная корреляционная функция на шиповых поездах покажет гораздо более детальную картину взаимозависимости между нейронами или нервными сборками, когда вы дадите ей шипованные поезда, а не фактические потенциалы. Вы также можете посмотреть модуль статистики Брайана, который можно найти здесь:

http://neuralensemble.org/trac/brian/browser/trunk/brian/tools/statistics.py

Что касается разности фаз, то, вероятно, это неадекватная мера, поскольку нейроны не являются гармоническими осцилляторами. Если вы хотите провести очень точные измерения фазы, лучше всего взглянуть на синхронизацию ангармонических осцилляторов. Математическая модель, описывающая такие типы осцилляторов, которая очень полезна в контексте нейронов и нейронных сетей, - это модель Курамото. Существует обширная документация, доступная для модели Kuramoto и интеграции синхронизации и синхронизации, поэтому я оставлю это для этого.