Skip to content

Commit

Permalink
[WIP] Dedicated structs for recurrence matrices (#25)
Browse files Browse the repository at this point in the history
* export Dataset as well, seems useful.

* recurrence matrix type and its pretty printing

* change function return types

* each matrix gets its own type.

* more necessary method propagation

* more elegant method propagation

* update recurrence rate

* enforce as constant that RecurrenceMatrix is symmetric

* correct mistake: Int instead of Int64

* replace all dispatches with ARM

and comment out windowed for now.

* use new types in tests

* update plotting to new types

* remove `AbstractMatrix` methods for diagonal and vertical histograms

* extend iterate

and uncomment windowed

* type stability in trend and recurrencerate

* fix windowed with JointRecurrenceMatrix

* change field from .m to .data

* fix windowed rqa functions

* change gensym by local variable declarations in windowed

* added documentation and comments on windowed

* delete "=  true" in pretty printing of recurrence matrices (#27)

* docstring fixes

* deprecations
  • Loading branch information
Datseris authored Jan 2, 2019
1 parent 17edc3e commit 9dbcb43
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 199 deletions.
14 changes: 2 additions & 12 deletions src/RecurrenceAnalysis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ module RecurrenceAnalysis
using Distances, Statistics, LinearAlgebra, SparseArrays, DelayEmbeddings, StaticArrays
import Base.Meta.parse

export RecurrenceMatrix, CrossRecurrenceMatrix, JointRecurrenceMatrix
export embed,
reconstruct,
Dataset,
distancematrix,
recurrencematrix,
crossrecurrencematrix,
Expand All @@ -21,21 +23,9 @@ export embed,
trappingtime,
maxvert,
rqa,
autocorrelation,
ami,
gmi,
sorteddistances,
@windowed

# column values in sparse matrix (parallel to rowvals)
function colvals(x::SparseMatrixCSC)
cv = zeros(Int,nnz(x))
@inbounds for c=1:size(x,2)
cv[nzrange(x,c)] .= c
end
cv
end

include("matrices.jl")
include("plot.jl")
include("rqa.jl")
Expand Down
96 changes: 80 additions & 16 deletions src/matrices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,66 @@ function _distancematrix(x::Dataset{S,Tx}, y::Dataset{S,Ty},
end


#### Recurrence matrix ####
# Defined as a wrapper of crossrecurrencematrix

#######################
# Type
#######################
abstract type AbstractRecurrenceMatrix end
const ARM = AbstractRecurrenceMatrix
struct RecurrenceMatrix <: AbstractRecurrenceMatrix
data::SparseMatrixCSC{Bool,Int}
end
struct CrossRecurrenceMatrix <: AbstractRecurrenceMatrix
data::SparseMatrixCSC{Bool,Int}
end
struct JointRecurrenceMatrix <: AbstractRecurrenceMatrix
data::SparseMatrixCSC{Bool,Int}
end

function Base.summary(R::AbstractRecurrenceMatrix)
N = nnz(R.data)
return "$(nameof(typeof(R))) of size $(size(R.data)) with $N entries:"
end
function Base.show(io::IO, R::AbstractRecurrenceMatrix)
s = sprint(io -> show(IOContext(io, :limit=>true), MIME"text/plain"(), R.data))
s = split(s, '\n')[2:end]
s = [replace(line, "= true"=>"", count=1) for line in s]
s = join(s, '\n')
tos = summary(R)*"\n"*s
println(io, tos)
end

# Propagate used functions:
begin
extentions = [
(:Base, (:getindex, :size, :length, :view, :iterate)),
(:LinearAlgebra, (:diag, :triu, :tril, :issymmetric)),
(:SparseArrays, (:nnz, :rowvals, :nzrange))
]
for (M, fs) in extentions
for f in fs
@eval $M.$(f)(x::ARM, args...) = $(f)(x.data, args...)
end
end
end
LinearAlgebra.issymmetric(::RecurrenceMatrix) = true
# column values in sparse matrix (parallel to rowvals)
function colvals(x::SparseMatrixCSC)
cv = zeros(Int,nnz(x))
@inbounds for c=1:size(x,2)
cv[nzrange(x,c)] .= c
end
cv
end
colvals(x::ARM) = colvals(x.data)

@deprecate recurrencematrix RecurrenceMatrix
@deprecate crossrecurrencematrix CrossRecurrenceMatrix
@deprecate jointrecurrencematrix JointRecurrenceMatrix


"""
recurrencematrix(x, ε; kwargs...)
RecurrenceMatrix(x, ε; kwargs...)
Create a recurrence matrix from an embedded time series.
Expand All @@ -115,7 +170,7 @@ by the following keyword arguments:
and `scale` is ignored.
* `metric` : metric of the distances, as in [`distancematrix`](@ref).
See also: [`crossrecurrencematrix`](@ref), [`jointrecurrencematrix`](@ref) and
See also: [`CrossRecurrenceMatrix`](@ref), [`JointRecurrenceMatrix`](@ref) and
use [`recurrenceplot`](@ref) to turn the result of these functions into a plottable format.
## References
Expand All @@ -126,13 +181,16 @@ use [`recurrenceplot`](@ref) to turn the result of these functions into a plotta
recurrence quantifications", in: Webber, C.L. & N. Marwan (eds.), *Recurrence
Quantification Analysis. Theory and Best Practices*, Springer, pp. 3-43 (2015).
"""
recurrencematrix(x, ε; kwargs...) = crossrecurrencematrix(x, x, ε; kwargs...)
function RecurrenceMatrix(x, ε; kwargs...)
m = crossrecurrencematrix(x, x, ε; kwargs...)
return RecurrenceMatrix(m)
end


#### Cross recurrence matrix ####

"""
crossrecurrencematrix(x, y, ε; kwargs...)
CrossRecurrenceMatrix(x, y, ε; kwargs...)
Create a cross recurrence matrix from the time series `x` and `y`.
Expand All @@ -141,17 +199,23 @@ For the time series `x`, `y`, of length `n` and `m`, respectively, it is a
sparse `n×m` matrix of Boolean values, such that if `d(x[i], y[j]) ≤ ε`,
then the cell `(i, j)` of the matrix will have a `true` value.
See [`recurrencematrix`](@ref) for details, references and keywords.
See also: [`jointrecurrencematrix`](@ref).
See [`RecurrenceMatrix`](@ref) for details, references and keywords.
See also: [`JointRecurrenceMatrix`](@ref).
"""
function CrossRecurrenceMatrix(x, y, ε; kwargs...)
m = crossrecurrencematrix(x, y, ε; kwargs...)
return CrossRecurrenceMatrix(m)
end

function crossrecurrencematrix(x, y, ε; scale=1, fixedrate=false, metric=Chebyshev())
# Check fixed recurrence rate - ε must be within (0, 1)
if fixedrate
sfun = (m) -> quantile(m[:], ε)
return crossrecurrencematrix(x, y, 1; scale=sfun, fixedrate=false, metric=metric)
else
scale_value = _computescale(scale, x, y, metric)
return _crossrecurrencematrix(x, y, ε*scale_value, metric)
spm = _crossrecurrencematrix(x, y, ε*scale_value, metric)
return spm
end
end

Expand Down Expand Up @@ -195,7 +259,7 @@ end
#### Joint recurrence matrix ####

"""
jointrecurrencematrix(x, y, ε; kwargs...)
JointRecurrenceMatrix(x, y, ε; kwargs...)
Create a joint recurrence matrix from the time series `x` and `y`.
Expand All @@ -205,12 +269,12 @@ simultaneously. It is calculated by the element-wise multiplication
of the recurrence matrices of `x` and `y`. If `x` and `y` are of different
length, the recurrences are only calculated until the length of the shortest one.
See [`recurrencematrix`](@ref) for details, references and keywords.
See also: [`crossrecurrencematrix`](@ref).
See [`RecurrenceMatrix`](@ref) for details, references and keywords.
See also: [`CrossRecurrenceMatrix`](@ref).
"""
function jointrecurrencematrix(x, y, ε; kwargs...)
function JointRecurrenceMatrix(x, y, ε; kwargs...)
n = min(size(x,1), size(y,1))
rm1 = recurrencematrix(x[1:n,:], ε, kwargs...)
rm2 = recurrencematrix(y[1:n,:], ε, kwargs...)
return rm1 .* rm2
rm1 = RecurrenceMatrix( (@view x[1:n,:]), ε, kwargs...)
rm2 = RecurrenceMatrix( (@view y[1:n,:]), ε, kwargs...)
return JointRecurrenceMatrix(rm1.data .* rm2.data)
end
10 changes: 5 additions & 5 deletions src/plot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ function checkgridsize(width::T, height::T, dims::Tuple{T,T}) where T<:Integer
(height,width-1) => height/(width-1))
distances = Dict(d=>(r-ratio) for (d,r) in intratios)
distancesigns = sign.(collect(values(distances)))
if all(distancesigns .>= 0) || all(distancesigns .<= 0)
if all(x -> x >= 0, distancesigns) || all(x -> x <= 0, distancesigns)
width_adj = round(Integer, height*dims[1]/dims[2])
height_adj = round(Integer, width*dims[2]/dims[1])
width = min(width, width_adj)
height = min(height, height_adj)
@warn """the specified dimensions are not proportional to the matrix size
The size of the plot will be $width×$height."""
@warn "The specified dimensions are not proportional to the matrix size. "*
"The size of the plot will be $width×$height."
end
(width, height)
end
Expand All @@ -29,7 +29,7 @@ function overlapgrid(m::T, n::T) where T<:Integer
end

# Calculate the level of "gray" (0=white, 1=black) corresponding to a matrix block
function block2grayscale(x::AbstractMatrix, rind::Tuple{T,T}, cind::Tuple{T,T}) where T<:Integer
function block2grayscale(x, rind::Tuple{T,T}, cind::Tuple{T,T}) where T<:Integer
submat = @view x[rind[1]:rind[2], cind[1]:cind[2]]
ratio = count(!iszero, submat)/prod(size(submat))
end
Expand Down Expand Up @@ -63,7 +63,7 @@ is calculated for each element of the grid, proportional to the number of
recurrent points contained in it. The levels of gray are coded as numbers of the
same type as the black and white codes.
"""
function recurrenceplot(x::AbstractMatrix, bwcode::Tuple{TT,T}=(0.0,1.0);
function recurrenceplot(x, bwcode::Tuple{TT,T}=(0.0,1.0);
exactsize=false, kwargs...) where {TT<:Real, T<:Real}

dims = size(x)
Expand Down
Loading

0 comments on commit 9dbcb43

Please sign in to comment.