Skip to content

Commit a9e6512

Browse files
authored
Add power spectrum probability estimator (#94)
* add code and docstring for spectral entropy * add docs
1 parent 47189f0 commit a9e6512

File tree

5 files changed

+48
-22
lines changed

5 files changed

+48
-22
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ version = "1.3.0"
77
[deps]
88
DelayEmbeddings = "5732040d-69e3-5649-938a-b6b4f237613f"
99
Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
10+
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
1011
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1112
Neighborhood = "645ca80c-8b79-4109-87ea-e1f58159d116"
1213
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
@@ -22,6 +23,7 @@ Wavelets = "29a6e085-ba6d-5f35-a997-948ac2efa89a"
2223
[compat]
2324
DelayEmbeddings = "2"
2425
Distances = "0.9, 0.10"
26+
FFTW = "1"
2527
Neighborhood = "0.1, 0.2"
2628
QuadGK = "2"
2729
Scratch = "1"

docs/src/probabilities.md

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -56,29 +56,9 @@ transfermatrix
5656
NaiveKernel
5757
```
5858

59-
## Example
60-
61-
Here, we draw some random points from a 2D normal distribution. Then, we use kernel
62-
density estimation to associate a probability to each point `p`, measured by how many
63-
points are within radius `1.5` of `p`. Plotting the actual points, along with their
64-
associated probabilities estimated by the KDE procedure, we get the following surface
65-
plot.
66-
67-
```@example MAIN
68-
using DynamicalSystems, CairoMakie, Distributions
69-
𝒩 = MvNormal([1, -4], 2)
70-
N = 500
71-
D = Dataset(sort([rand(𝒩) for i = 1:N]))
72-
x, y = columns(D)
73-
p = probabilities(D, NaiveKernel(1.5))
74-
fig, ax = surface(x, y, p.p; axis=(type=Axis3,))
75-
ax.zlabel = "P"
76-
ax.zticklabelsvisible = false
77-
fig
78-
```
79-
80-
## Wavelet
59+
## Timescales
8160

8261
```@docs
8362
WaveletOverlap
63+
PowerSpectrum
8464
```
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
export PowerSpectrum
2+
import FFTW
3+
4+
"""
5+
PowerSpectrum() <: ProbabilitiesEstimator
6+
7+
Calculate the power spectrum of a timeseries (amplitude square of its Fourier transform),
8+
and return the spectrum normalized to sum = 1 as probabilities.
9+
The Shannon entropy of these probabilities is typically referred in the literature as
10+
_spectral entropy_, e.g. [^Llanos2016],[^Tian2017].
11+
12+
The simpler the temporal structure of the timeseries, the lower the entropy. However,
13+
you can't compare entropies of timeseries with different length, because the binning
14+
in spectral space depends on the length of the input.
15+
16+
[^Llanos2016]:
17+
Llanos et al., _Power spectral entropy as an information-theoretic correlate of manner
18+
of articulation in American English_, [The Journal of the Acoustical Society of America
19+
141, EL127 (2017); https://doi.org/10.1121/1.4976109]
20+
21+
[^Tian2017]:
22+
Tian et al, _Spectral Entropy Can Predict Changes of Working Memory Performance Reduced
23+
by Short-Time Training in the Delayed-Match-to-Sample Task_, [Front. Hum. Neurosci.](
24+
https://doi.org/10.3389/fnhum.2017.00437)
25+
"""
26+
struct PowerSpectrum <: ProbabilitiesEstimator end
27+
28+
function probabilities(x, ::PowerSpectrum)
29+
@assert x isa AbstractVector{<:Real} "`PowerSpectrum` only works for timeseries input!"
30+
f = FFTW.rfft(x)
31+
Probabilities(abs2.(f))
32+
end
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
include("wavelet_overlap.jl")
2+
include("power_spectrum.jl")

test/timescales.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,15 @@ using Entropies, Test
1414
@test ps isa Probabilities
1515
@test entropy_renyi(x, WaveletOverlap(), q = 1, base = 2) isa Real
1616
end
17+
18+
@testet "Fourier Spectrum" begin
19+
N = 1000
20+
t = range(0, 10π, N)
21+
x = sin.(t)
22+
y = @. sin(t) + sin(sqrt(3)*t)
23+
z = randn(N)
24+
est = PowerSpectrum()
25+
ents = [entropy_renyi(w, est) for w in (x,y,z)]
26+
@test ents[1] < ents[2] < ents[3]
27+
end
1728
end

0 commit comments

Comments
 (0)