-
Notifications
You must be signed in to change notification settings - Fork 19
/
dfa.py
99 lines (90 loc) · 3.26 KB
/
dfa.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# author: Dominik Krzeminski (dokato)
import numpy as np
import matplotlib.pyplot as plt
import scipy.signal as ss
# detrended fluctuation analysis
def calc_rms(x, scale):
"""
windowed Root Mean Square (RMS) with linear detrending.
Args:
-----
*x* : numpy.array
one dimensional data vector
*scale* : int
length of the window in which RMS will be calculaed
Returns:
--------
*rms* : numpy.array
RMS data in each window with length len(x)//scale
"""
# making an array with data divided in windows
shape = (x.shape[0]//scale, scale)
X = np.lib.stride_tricks.as_strided(x,shape=shape)
# vector of x-axis points to regression
scale_ax = np.arange(scale)
rms = np.zeros(X.shape[0])
for e, xcut in enumerate(X):
coeff = np.polyfit(scale_ax, xcut, 1)
xfit = np.polyval(coeff, scale_ax)
# detrending and computing RMS of each window
rms[e] = np.sqrt(np.mean((xcut-xfit)**2))
return rms
def dfa(x, scale_lim=[5,9], scale_dens=0.25, show=False):
"""
Detrended Fluctuation Analysis - measures power law scaling coefficient
of the given signal *x*.
More details about the algorithm you can find e.g. here:
Hardstone, R. et al. Detrended fluctuation analysis: A scale-free
view on neuronal oscillations, (2012).
Args:
-----
*x* : numpy.array
one dimensional data vector
*scale_lim* = [5,9] : list of length 2
boundaries of the scale, where scale means windows among which RMS
is calculated. Numbers from list are exponents of 2 to the power
of X, eg. [5,9] is in fact [2**5, 2**9].
You can think of it that if your signal is sampled with F_s = 128 Hz,
then the lowest considered scale would be 2**5/128 = 32/128 = 0.25,
so 250 ms.
*scale_dens* = 0.25 : float
density of scale divisions, eg. for 0.25 we get 2**[5, 5.25, 5.5, ... ]
*show* = False
if True it shows matplotlib log-log plot.
Returns:
--------
*scales* : numpy.array
vector of scales (x axis)
*fluct* : numpy.array
fluctuation function values (y axis)
*alpha* : float
estimation of DFA exponent
"""
# cumulative sum of data with substracted offset
y = np.cumsum(x - np.mean(x))
scales = (2**np.arange(scale_lim[0], scale_lim[1], scale_dens)).astype(np.int)
fluct = np.zeros(len(scales))
# computing RMS for each window
for e, sc in enumerate(scales):
fluct[e] = np.sqrt(np.mean(calc_rms(y, sc)**2))
# fitting a line to rms data
coeff = np.polyfit(np.log2(scales), np.log2(fluct), 1)
if show:
fluctfit = 2**np.polyval(coeff,np.log2(scales))
plt.loglog(scales, fluct, 'bo')
plt.loglog(scales, fluctfit, 'r', label=r'$\alpha$ = %0.2f'%coeff[0])
plt.title('DFA')
plt.xlabel(r'$\log_{10}$(time window)')
plt.ylabel(r'$\log_{10}$<F(t)>')
plt.legend()
plt.show()
return scales, fluct, coeff[0]
if __name__=='__main__':
n = 1000
x = np.random.randn(n)
# computing DFA of signal envelope
x = np.abs(ss.hilbert(x))
scales, fluct, alpha = dfa(x, show=1)
print(scales)
print(fluct)
print("DFA exponent: {}".format(alpha))