Skip to content

Commit fc9d6e7

Browse files
committed
Compute auto- and cross-correlations for complex sequences
1 parent 757a271 commit fc9d6e7

File tree

3 files changed

+168
-157
lines changed

3 files changed

+168
-157
lines changed

src/signalcorr.jl

Lines changed: 66 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
default_laglen(lx::Int) = min(lx-1, round(Int,10*log10(lx)))
1515
check_lags(lx::Int, lags::AbstractVector) = (maximum(lags) < lx || error("lags must be less than the sample length."))
1616

17-
function demean_col!(z::RealVector, x::RealMatrix, j::Int, demean::Bool)
17+
function demean_col!(z::AbstractVector, x::AbstractMatrix, j::Int, demean::Bool)
1818
T = eltype(z)
1919
m = size(x, 1)
2020
@assert m == length(z)
@@ -43,8 +43,7 @@ end
4343

4444
default_autolags(lx::Int) = 0 : default_laglen(lx)
4545

46-
_autodot(x::AbstractVector{<:RealFP}, lx::Int, l::Int) = dot(x, 1:(lx-l), x, (1+l):lx)
47-
_autodot(x::RealVector, lx::Int, l::Int) = dot(view(x, 1:(lx-l)), view(x, (1+l):lx))
46+
_autodot(x::AbstractVector, lx::Int, l::Int) = dot(view(x, 1:(lx-l)), view(x, (1+l):lx))
4847

4948

5049
## autocov
@@ -61,34 +60,32 @@ where each column in the result will correspond to a column in `x`.
6160
6261
The output is not normalized. See [`autocor!`](@ref) for a method with normalization.
6362
"""
64-
function autocov!(r::RealVector, x::RealVector, lags::IntegerVector; demean::Bool=true)
63+
function autocov!(
64+
r::AbstractVector,
65+
x::AbstractVector,
66+
lags::AbstractVector{<:Integer},
67+
z=zeros(typeof(zero(eltype(x)) / 1), length(x));
68+
demean::Bool=true
69+
)
6570
lx = length(x)
6671
m = length(lags)
6772
length(r) == m || throw(DimensionMismatch())
6873
check_lags(lx, lags)
69-
7074
T = typeof(zero(eltype(x)) / 1)
71-
z::Vector{T} = demean ? x .- mean(x) : x
75+
z .= x
76+
demean && z .= z .- mean(z)
7277
for k = 1 : m # foreach lag value
7378
r[k] = _autodot(z, lx, lags[k]) / lx
7479
end
7580
return r
7681
end
7782

78-
function autocov!(r::RealMatrix, x::RealMatrix, lags::IntegerVector; demean::Bool=true)
79-
lx = size(x, 1)
80-
ns = size(x, 2)
81-
m = length(lags)
82-
size(r) == (m, ns) || throw(DimensionMismatch())
83-
check_lags(lx, lags)
84-
85-
T = typeof(zero(eltype(x)) / 1)
86-
z = Vector{T}(undef, lx)
87-
for j = 1 : ns
88-
demean_col!(z, x, j, demean)
89-
for k = 1 : m
90-
r[k,j] = _autodot(z, lx, lags[k]) / lx
91-
end
83+
function autocov!(
84+
r::AbstractMatrix, x::AbstractMatrix, lags::AbstractVector{<:Integer}; demean::Bool=true
85+
)
86+
z = zeros(typeof(zero(eltype(x))/1), size(x, 1))
87+
for n in 1:size(x, 2)
88+
autocov!(view(r, :, n), view(x, :, n), lags, z; demean)
9289
end
9390
return r
9491
end
@@ -110,18 +107,18 @@ When left unspecified, the lags used are the integers from 0 to
110107
111108
The output is not normalized. See [`autocor`](@ref) for a function with normalization.
112109
"""
113-
function autocov(x::RealVector, lags::IntegerVector; demean::Bool=true)
110+
function autocov(x::AbstractVector, lags::AbstractVector{<:Integer}; demean::Bool=true)
114111
out = Vector{float(eltype(x))}(undef, length(lags))
115-
autocov!(out, x, lags; demean=demean)
112+
autocov!(out, x, lags; demean)
116113
end
117114

118-
function autocov(x::RealMatrix, lags::IntegerVector; demean::Bool=true)
115+
function autocov(x::AbstractMatrix, lags::AbstractVector{<:Integer}; demean::Bool=true)
119116
out = Matrix{float(eltype(x))}(undef, length(lags), size(x,2))
120-
autocov!(out, x, lags; demean=demean)
117+
autocov!(out, x, lags; demean)
121118
end
122119

123-
autocov(x::AbstractVecOrMat{<:Real}; demean::Bool=true) =
124-
autocov(x, default_autolags(size(x,1)); demean=demean)
120+
autocov(x::AbstractVecOrMat; demean::Bool=true) =
121+
autocov(x, default_autolags(size(x,1)); demean)
125122

126123
## autocor
127124

@@ -139,36 +136,27 @@ where each column in the result will correspond to a column in `x`.
139136
The output is normalized by the variance of `x`, i.e. so that the lag 0
140137
autocorrelation is 1. See [`autocov!`](@ref) for the unnormalized form.
141138
"""
142-
function autocor!(r::RealVector, x::RealVector, lags::IntegerVector; demean::Bool=true)
143-
lx = length(x)
144-
m = length(lags)
145-
length(r) == m || throw(DimensionMismatch())
146-
check_lags(lx, lags)
147-
148-
T = typeof(zero(eltype(x)) / 1)
149-
z::Vector{T} = demean ? x .- mean(x) : x
150-
zz = dot(z, z)
151-
for k = 1 : m # foreach lag value
152-
r[k] = _autodot(z, lx, lags[k]) / zz
153-
end
139+
function autocor!(
140+
r::AbstractVector,
141+
x::AbstractVector,
142+
lags::AbstractVector{<:Integer},
143+
z=zeros(typeof(zero(eltype(x)) / 1), length(x));
144+
demean::Bool=true
145+
)
146+
autocov!(view(r, 1:1), x, 0:0, z; demean)
147+
zz = r[1]
148+
autocov!(r, x, lags, z; demean)
149+
r ./= zz
154150
return r
155151
end
156152

157-
function autocor!(r::RealMatrix, x::RealMatrix, lags::IntegerVector; demean::Bool=true)
158-
lx = size(x, 1)
159-
ns = size(x, 2)
160-
m = length(lags)
161-
size(r) == (m, ns) || throw(DimensionMismatch())
162-
check_lags(lx, lags)
163-
164-
T = typeof(zero(eltype(x)) / 1)
165-
z = Vector{T}(undef, lx)
166-
for j = 1 : ns
167-
demean_col!(z, x, j, demean)
168-
zz = dot(z, z)
169-
for k = 1 : m
170-
r[k,j] = _autodot(z, lx, lags[k]) / zz
171-
end
153+
function autocor!(
154+
r::AbstractMatrix, x::AbstractMatrix, lags::AbstractVector{<:Integer}; demean::Bool=true
155+
)
156+
T = typeof(zero(eltype(x))/1)
157+
z = Vector{T}(undef, size(x, 1))
158+
for n in 1:size(x, 2)
159+
autocor!(view(r, :, n), view(x, :, n), lags, z; demean)
172160
end
173161
return r
174162
end
@@ -191,18 +179,18 @@ When left unspecified, the lags used are the integers from 0 to
191179
The output is normalized by the variance of `x`, i.e. so that the lag 0
192180
autocorrelation is 1. See [`autocov`](@ref) for the unnormalized form.
193181
"""
194-
function autocor(x::RealVector, lags::IntegerVector; demean::Bool=true)
182+
function autocor(x::AbstractVector, lags::AbstractVector{<:Integer}; demean::Bool=true)
195183
out = Vector{float(eltype(x))}(undef, length(lags))
196-
autocor!(out, x, lags; demean=demean)
184+
autocor!(out, x, lags; demean)
197185
end
198186

199-
function autocor(x::RealMatrix, lags::IntegerVector; demean::Bool=true)
187+
function autocor(x::AbstractMatrix, lags::AbstractVector{<:Integer}; demean::Bool=true)
200188
out = Matrix{float(eltype(x))}(undef, length(lags), size(x,2))
201-
autocor!(out, x, lags; demean=demean)
189+
autocor!(out, x, lags; demean)
202190
end
203191

204-
autocor(x::AbstractVecOrMat{<:Real}; demean::Bool=true) =
205-
autocor(x, default_autolags(size(x,1)); demean=demean)
192+
autocor(x::AbstractVecOrMat; demean::Bool=true) =
193+
autocor(x, default_autolags(size(x,1)); demean)
206194

207195

208196
#######################################
@@ -213,14 +201,7 @@ autocor(x::AbstractVecOrMat{<:Real}; demean::Bool=true) =
213201

214202
default_crosslags(lx::Int) = (l=default_laglen(lx); -l:l)
215203

216-
function _crossdot(x::AbstractVector{T}, y::AbstractVector{T}, lx::Int, l::Int) where {T<:RealFP}
217-
if l >= 0
218-
dot(x, 1:(lx-l), y, (1+l):lx)
219-
else
220-
dot(x, (1-l):lx, y, 1:(lx+l))
221-
end
222-
end
223-
function _crossdot(x::RealVector, y::RealVector, lx::Int, l::Int)
204+
function _crossdot(x::AbstractVector, y::AbstractVector, lx::Int, l::Int)
224205
if l >= 0
225206
dot(view(x, 1:(lx-l)), view(y, (1+l):lx))
226207
else
@@ -246,7 +227,7 @@ three-dimensional array of size `(length(lags), size(x, 2), size(y, 2))`.
246227
247228
The output is not normalized. See [`crosscor!`](@ref) for a function with normalization.
248229
"""
249-
function crosscov!(r::RealVector, x::RealVector, y::RealVector, lags::IntegerVector; demean::Bool=true)
230+
function crosscov!(r::AbstractVector, x::AbstractVector, y::AbstractVector, lags::AbstractVector{<:Integer}; demean::Bool=true)
250231
lx = length(x)
251232
m = length(lags)
252233
(length(y) == lx && length(r) == m) || throw(DimensionMismatch())
@@ -262,7 +243,7 @@ function crosscov!(r::RealVector, x::RealVector, y::RealVector, lags::IntegerVec
262243
return r
263244
end
264245

265-
function crosscov!(r::RealMatrix, x::RealMatrix, y::RealVector, lags::IntegerVector; demean::Bool=true)
246+
function crosscov!(r::AbstractMatrix, x::AbstractMatrix, y::AbstractVector, lags::AbstractVector{<:Integer}; demean::Bool=true)
266247
lx = size(x, 1)
267248
ns = size(x, 2)
268249
m = length(lags)
@@ -282,7 +263,7 @@ function crosscov!(r::RealMatrix, x::RealMatrix, y::RealVector, lags::IntegerVec
282263
return r
283264
end
284265

285-
function crosscov!(r::RealMatrix, x::RealVector, y::RealMatrix, lags::IntegerVector; demean::Bool=true)
266+
function crosscov!(r::AbstractMatrix, x::AbstractVector, y::AbstractMatrix, lags::AbstractVector{<:Integer}; demean::Bool=true)
286267
lx = length(x)
287268
ns = size(y, 2)
288269
m = length(lags)
@@ -302,7 +283,7 @@ function crosscov!(r::RealMatrix, x::RealVector, y::RealMatrix, lags::IntegerVec
302283
return r
303284
end
304285

305-
function crosscov!(r::AbstractArray{<:Real,3}, x::RealMatrix, y::RealMatrix, lags::IntegerVector; demean::Bool=true)
286+
function crosscov!(r::AbstractArray{U,3} where U, x::AbstractMatrix, y::AbstractMatrix, lags::AbstractVector{<:Integer}; demean::Bool=true)
306287
lx = size(x, 1)
307288
nx = size(x, 2)
308289
ny = size(y, 2)
@@ -356,27 +337,27 @@ When left unspecified, the lags used are the integers from
356337
357338
The output is not normalized. See [`crosscor`](@ref) for a function with normalization.
358339
"""
359-
function crosscov(x::RealVector, y::RealVector, lags::IntegerVector; demean::Bool=true)
340+
function crosscov(x::AbstractVector, y::AbstractVector, lags::AbstractVector{<:Integer}; demean::Bool=true)
360341
out = Vector{float(Base.promote_eltype(x, y))}(undef, length(lags))
361342
crosscov!(out, x, y, lags; demean=demean)
362343
end
363344

364-
function crosscov(x::RealMatrix, y::RealVector, lags::IntegerVector; demean::Bool=true)
345+
function crosscov(x::AbstractMatrix, y::AbstractVector, lags::AbstractVector{<:Integer}; demean::Bool=true)
365346
out = Matrix{float(Base.promote_eltype(x, y))}(undef, length(lags), size(x,2))
366347
crosscov!(out, x, y, lags; demean=demean)
367348
end
368349

369-
function crosscov(x::RealVector, y::RealMatrix, lags::IntegerVector; demean::Bool=true)
350+
function crosscov(x::AbstractVector, y::AbstractMatrix, lags::AbstractVector{<:Integer}; demean::Bool=true)
370351
out = Matrix{float(Base.promote_eltype(x, y))}(undef, length(lags), size(y,2))
371352
crosscov!(out, x, y, lags; demean=demean)
372353
end
373354

374-
function crosscov(x::RealMatrix, y::RealMatrix, lags::IntegerVector; demean::Bool=true)
355+
function crosscov(x::AbstractMatrix, y::AbstractMatrix, lags::AbstractVector{<:Integer}; demean::Bool=true)
375356
out = Array{float(Base.promote_eltype(x, y)),3}(undef, length(lags), size(x,2), size(y,2))
376357
crosscov!(out, x, y, lags; demean=demean)
377358
end
378359

379-
crosscov(x::AbstractVecOrMat{<:Real}, y::AbstractVecOrMat{<:Real}; demean::Bool=true) =
360+
crosscov(x::AbstractVecOrMat, y::AbstractVecOrMat; demean::Bool=true) =
380361
crosscov(x, y, default_crosslags(size(x,1)); demean=demean)
381362

382363

@@ -397,7 +378,7 @@ three-dimensional array of size `(length(lags), size(x, 2), size(y, 2))`.
397378
The output is normalized by `sqrt(var(x)*var(y))`. See [`crosscov!`](@ref) for the
398379
unnormalized form.
399380
"""
400-
function crosscor!(r::RealVector, x::RealVector, y::RealVector, lags::IntegerVector; demean::Bool=true)
381+
function crosscor!(r::AbstractVector, x::AbstractVector, y::AbstractVector, lags::AbstractVector{<:Integer}; demean::Bool=true)
401382
lx = length(x)
402383
m = length(lags)
403384
(length(y) == lx && length(r) == m) || throw(DimensionMismatch())
@@ -414,7 +395,7 @@ function crosscor!(r::RealVector, x::RealVector, y::RealVector, lags::IntegerVec
414395
return r
415396
end
416397

417-
function crosscor!(r::RealMatrix, x::RealMatrix, y::RealVector, lags::IntegerVector; demean::Bool=true)
398+
function crosscor!(r::AbstractMatrix, x::AbstractMatrix, y::AbstractVector, lags::AbstractVector{<:Integer}; demean::Bool=true)
418399
lx = size(x, 1)
419400
ns = size(x, 2)
420401
m = length(lags)
@@ -436,7 +417,7 @@ function crosscor!(r::RealMatrix, x::RealMatrix, y::RealVector, lags::IntegerVec
436417
return r
437418
end
438419

439-
function crosscor!(r::RealMatrix, x::RealVector, y::RealMatrix, lags::IntegerVector; demean::Bool=true)
420+
function crosscor!(r::AbstractMatrix, x::AbstractVector, y::AbstractMatrix, lags::AbstractVector{<:Integer}; demean::Bool=true)
440421
lx = length(x)
441422
ns = size(y, 2)
442423
m = length(lags)
@@ -458,7 +439,7 @@ function crosscor!(r::RealMatrix, x::RealVector, y::RealMatrix, lags::IntegerVec
458439
return r
459440
end
460441

461-
function crosscor!(r::AbstractArray{<:Real,3}, x::RealMatrix, y::RealMatrix, lags::IntegerVector; demean::Bool=true)
442+
function crosscor!(r::AbstractArray{U,3} where U, x::AbstractMatrix, y::AbstractMatrix, lags::AbstractVector{<:Integer}; demean::Bool=true)
462443
lx = size(x, 1)
463444
nx = size(x, 2)
464445
ny = size(y, 2)
@@ -517,27 +498,27 @@ When left unspecified, the lags used are the integers from
517498
The output is normalized by `sqrt(var(x)*var(y))`. See [`crosscov`](@ref) for the
518499
unnormalized form.
519500
"""
520-
function crosscor(x::RealVector, y::RealVector, lags::IntegerVector; demean::Bool=true)
501+
function crosscor(x::AbstractVector, y::AbstractVector, lags::AbstractVector{<:Integer}; demean::Bool=true)
521502
out = Vector{float(Base.promote_eltype(x, y))}(undef, length(lags))
522503
crosscor!(out, x, y, lags; demean=demean)
523504
end
524505

525-
function crosscor(x::RealMatrix, y::RealVector, lags::IntegerVector; demean::Bool=true)
506+
function crosscor(x::AbstractMatrix, y::AbstractVector, lags::AbstractVector{<:Integer}; demean::Bool=true)
526507
out = Matrix{float(Base.promote_eltype(x, y))}(undef, length(lags), size(x,2))
527508
crosscor!(out, x, y, lags; demean=demean)
528509
end
529510

530-
function crosscor(x::RealVector, y::RealMatrix, lags::IntegerVector; demean::Bool=true)
511+
function crosscor(x::AbstractVector, y::AbstractMatrix, lags::AbstractVector{<:Integer}; demean::Bool=true)
531512
out = Matrix{float(Base.promote_eltype(x, y))}(undef, length(lags), size(y,2))
532513
crosscor!(out, x, y, lags; demean=demean)
533514
end
534515

535-
function crosscor(x::RealMatrix, y::RealMatrix, lags::IntegerVector; demean::Bool=true)
516+
function crosscor(x::AbstractMatrix, y::AbstractMatrix, lags::AbstractVector{<:Integer}; demean::Bool=true)
536517
out = Array{float(Base.promote_eltype(x, y)),3}(undef, length(lags), size(x,2), size(y,2))
537518
crosscor!(out, x, y, lags; demean=demean)
538519
end
539520

540-
crosscor(x::AbstractVecOrMat{<:Real}, y::AbstractVecOrMat{<:Real}; demean::Bool=true) =
521+
crosscor(x::AbstractVecOrMat, y::AbstractVecOrMat; demean::Bool=true) =
541522
crosscor(x, y, default_crosslags(size(x,1)); demean=demean)
542523

543524

test/rcall_test.jl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using RCall
2+
3+
_r_ccf(x, y, lm) = rcopy(rcall(:ccf, x, y, plot=false, lag=lm, type="covariance"))[:acf]
4+
_r_acf(x, lm) = rcopy(rcall(:acf, x, plot=false, lag=lm, type="covariance"))[:acf]
5+
_r_pacf(x, lm) = rcopy(rcall(:acf, x, plot=false, lag=lm))[:acf]
6+
7+
function _ccf(is_auto::Bool, x::AbstractVector{<:Real}...)
8+
lx = size(x[1], 1)
9+
lag_max = min(lx - 1, round(Int, 10*log10(lx)))
10+
cor = reverse(dropdims(_r_ccf(x[1], x[end], lag_max), dims = (2, 3)))
11+
is_auto ? cor[lx:end] : cor
12+
end
13+
14+
r_autocov(x::AbstractVector) = r_crosscov(x, x, true)
15+
16+
function r_crosscov(x::AbstractVector, y::AbstractVector, is_auto=false)
17+
cv(x,y) = _ccf(is_auto, x, y)
18+
cc_re = cv(real.(x), real.(y)) + cv(imag.(x), imag.(y))
19+
cc_im = cv(real.(x), imag.(y)) - cv(imag.(x), real.(y))
20+
cc_re + im*cc_im
21+
end
22+
23+
r_autocor(x::AbstractVector) = r_crosscor(x, x, true)
24+
25+
function r_crosscor(x::AbstractVector, y::AbstractVector, is_auto=false)
26+
cc = r_crosscov(x, y, is_auto)
27+
sc(z) = _r_ccf(real.(z), real.(z), 0) + _r_ccf(imag.(z), imag.(z), 0)
28+
cc/sqrt(sc(x)*sc(y))
29+
end

0 commit comments

Comments
 (0)