diff --git a/docs/src/api.md b/docs/src/api.md index 4565b0dd..5ec1d3da 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -7,9 +7,9 @@ IndicatorsChangesConfig SlidingWindowConfig SegmentedWindowConfig estimate_indicator_changes -WindowResults +IndicatorsChangesResults SlidingWindowResults -SegmentWindowResults +SegmentedWindowResults ``` ## Significance testing diff --git a/src/TransitionsInTimeseries.jl b/src/TransitionsInTimeseries.jl index f2349e98..d191ba5d 100644 --- a/src/TransitionsInTimeseries.jl +++ b/src/TransitionsInTimeseries.jl @@ -55,8 +55,8 @@ export difference_of_means # analysis export IndicatorsChangesConfig, SlidingWindowConfig, SegmentedWindowConfig -export SlidingWindowResults, SegmentWindowResults -export estimate_indicator_changes, WindowResults +export SlidingWindowResults, SegmentedWindowResults +export estimate_indicator_changes, IndicatorsChangesResults export TransitionsSignificance, significant_transitions, segmented_significance export QuantileSignificance, SigmaSignificance, SurrogatesSignificance diff --git a/src/analysis/api.jl b/src/analysis/api.jl index 5a8f600b..36a6f56a 100644 --- a/src/analysis/api.jl +++ b/src/analysis/api.jl @@ -10,15 +10,25 @@ Supertype used to define how indicators and their changes are estimated in abstract type IndicatorsChangesConfig end """ - estimate_indicator_changes(config::IndicatorsChangesConfig, x [,t]) → output + estimate_indicator_changes(config::IndicatorsChangesConfig, x [,t]) → result Estimate possible transitions for input timeseries `x` using the approach specified in the configuration type `config`, see [`IndicatorsChangesConfig`](@ref) for possibilities. `t` is the time vector corresponding to `x`, which defaults to `eachindex(x)`. -Return the output as [`WindowResults`](@ref) which can be given to +Return the output as subtype of [`IndicatorsChangesResults`](@ref). +The particular form of the output depends on the `config` and is described in its docstring. +Regardless of type, `result` can always be given to [`significant_transitions`](@ref) to deduce which possible transitions are statistically significant using a variety of significance tests. """ function estimate_indicator_changes end -# The function is extended via multiple dispatch in the specific files \ No newline at end of file +# The function is extended via multiple dispatch in the specific files + +""" + IndicatorsChangesResults + +Supertype used to gather results of [`estimate_indicator_changes`](@ref). +The concrete subtype instances are described in the docstrings of configuration types. +""" +abstract type IndicatorsChangesResults end \ No newline at end of file diff --git a/src/analysis/segmented_window.jl b/src/analysis/segmented_window.jl index e6d92871..0ff64486 100644 --- a/src/analysis/segmented_window.jl +++ b/src/analysis/segmented_window.jl @@ -15,12 +15,13 @@ window segments as follows: (the window segments may overlap, that's okay). `indicators, change_metrics` are identical as in [`SlidingWindowConfig`](@ref). +The results output corresponding to `SlidingWindowConfig` is [`SegmentedWindowResults`](@ref). + ## Keyword arguments -- - `width_ind::Int=100, stride_ind::Int=1, whichtime = midpoint, T = Float64`: keywords identical as in [`SlidingWindowConfig`](@ref). -- `min_width_cha::Int=50`: minimal width required to perform the change metric estimation. - If segment not sufficiently long, return `NaN`. +- `min_width_cha::Int=typemax(Int)`: minimal width required to perform the change metric estimation. + If a segment is not sufficiently long, the change metric is `NaN`. """ struct SegmentedWindowConfig{F, G, W<:Function} <: IndicatorsChangesConfig indicators::F @@ -37,7 +38,7 @@ function SegmentedWindowConfig( indicators, change_metrics, tseg_start, tseg_end; width_ind = 100, stride_ind = 1, - min_width_cha = 50, + min_width_cha = typemax(Int), whichtime = midpoint, T = Float64, ) @@ -94,10 +95,69 @@ function estimate_indicator_changes(config::SegmentedWindowConfig, x, t) end end # put everything together in the output type - return SegmentWindowResults(t, x, t_indicator, x_indicator, t_change, x_change, config) + return SegmentedWindowResults(t, x, t_indicator, x_indicator, t_change, x_change, config) end function segment(t, x, t1, t2) i1, i2 = argmin(abs.(t .- t1)), argmin(abs.(t .- t2)) return t[i1:i2], x[i1:i2] end + + +""" + SegmentedWindowResults <: IndicatorsChangesResults + +A struct containing the output of [`estimate_indicator_changes`](@ref) used with +[`SegmentedWindowConfig`](@ref). It can be used for further analysis, visualization, +or given to [`significant_transitions`](@ref). + +It has the following fields that the user may access + +- `x`: the input timeseries. +- `t`: the time vector of the input timeseries. + +- `x_indicator::Vector{Matrix}`, with `x_indicator[k]` the indicator timeseries (matrix + with each column one indicator) of the `k`-th segment. +- `t_indicator::Vector{Vector}`, with `t_indicator[k]` the time vector of the indicator + timeseries for the `k`-th segment. + +- `x_change::Matrix`, the change metric values with `x[k, i]` the change metric of the + `i`-th indicator for the `k`-th segment. +- `t_change`, the time vector of the change metric. + +- `config::SegmentedWindowConfig`, what was used for the analysis. +""" +struct SegmentedWindowResults{TT, T<:Real, X<:Real, XX<:AbstractVector{X}, + W} <: IndicatorsChangesResults + t::TT # original time vector; most often it is `Base.OneTo`. + x::XX + t_indicator::Vector{Vector{T}} + x_indicator::Vector{Matrix{X}} + t_change::Vector{T} + x_change::Matrix{X} + config::W +end + +# Segmented and Sliding results share their show method +function Base.show(io::IO, ::MIME"text/plain", res::Union{SegmentedWindowResults, SlidingWindowResults}) + println(io, "IndicatorsChangesResults") + descriptors = [ + "input timeseries" => summary(res.x), + "indicators" => [nameof(i) for i in res.config.indicators], + "indicator (window, stride)" => (res.config.width_ind, res.config.stride_ind), + "change metrics" => [nameof(c) for c in res.config.change_metrics], + show_changemetric(res), + ] + padlen = maximum(length(d[1]) for d in descriptors) + 2 + for (desc, val) in descriptors + println(io, rpad(" $(desc): ", padlen), val) + end +end + +function show_changemetric(res::SlidingWindowResults) + return "change metric (window, stride)" => (res.config.width_cha, res.config.stride_cha) +end + +function show_changemetric(res::SegmentedWindowResults) + return "change metric (window)" => ([length(t) for t in res.t_indicator]) +end diff --git a/src/analysis/sliding_window.jl b/src/analysis/sliding_window.jl index e0681210..25741468 100644 --- a/src/analysis/sliding_window.jl +++ b/src/analysis/sliding_window.jl @@ -19,6 +19,8 @@ Both indicators and change metrics are **generic Julia functions** that input an see [making custom indicators/change metrics](@ref own_indicator) in the documentation for more information on possible optimizations. +The results output corresponding to `SlidingWindowConfig` is [`SlidingWindowResults`](@ref). + ## Keyword arguments - `width_ind::Int=100, stride_ind::Int=1`: width and stride given to [`WindowViewer`](@ref) @@ -122,18 +124,7 @@ function estimate_indicator_changes(config::SlidingWindowConfig, x, t = eachinde end """ - WindowResults - -Supertype used to gather results of [`estimate_indicator_changes`](@ref). -Valid subtypes are: - - - [`SlidingWindowResults`](@ref). - - [`SegmentWindowResults`](@ref). -""" -abstract type WindowResults end - -""" - SlidingWindowResults + SlidingWindowResults <: IndicatorsChangesResults A struct containing the output of [`estimate_indicator_changes`](@ref) used with [`SlidingWindowConfig`](@ref). It can be used for further analysis, visualization, @@ -150,10 +141,10 @@ It has the following fields that the user may access - `x_change`, the change metric timeseries (matrix with each column one change metric). - `t_change`, the time vector of the change metric timeseries. -- [`config::SlidingWindowConfig`](@ref), used for the analysis. +- `config::SlidingWindowConfig`, what was used for the analysis. """ struct SlidingWindowResults{TT, T<:Real, X<:Real, XX<:AbstractVector{X}, - W} <: WindowResults + W} <: IndicatorsChangesResults t::TT # original time vector; most often it is `Base.OneTo`. x::XX t_indicator::Vector{T} @@ -162,60 +153,3 @@ struct SlidingWindowResults{TT, T<:Real, X<:Real, XX<:AbstractVector{X}, x_change::Matrix{X} config::W end - -""" - SegmentWindowResults - -A struct containing the output of [`estimate_indicator_changes`](@ref) used with -[`SegmentedWindowConfig`](@ref). It can be used for further analysis, visualization, -or given to [`significant_transitions`](@ref). - -It has the following fields that the user may access - -- `x`: the input timeseries. -- `t`: the time vector of the input timeseries. - -- `x_indicator::Vector{Matrix}`, with `x_indicator[k]` the indicator timeseries (matrix - with each column one indicator) of the `k`-th segment. -- `t_indicator::Vector{Vector}`, with `t_indicator[k]` the time vector of the indicator - timeseries for the `k`-th segment. - -- `x_change::Matrix`, the change metric values with `x[k, i]` the change metric of the - `i`-th indicator for the `k`-th segment. -- `t_change`, the time vector of the change metric. - -- [`config::SegmentedWindowConfig`](@ref), used for the analysis. -""" -struct SegmentWindowResults{TT, T<:Real, X<:Real, XX<:AbstractVector{X}, - W} <: WindowResults - t::TT # original time vector; most often it is `Base.OneTo`. - x::XX - t_indicator::Vector{Vector{T}} - x_indicator::Vector{Matrix{X}} - t_change::Vector{T} - x_change::Matrix{X} - config::W -end - -function Base.show(io::IO, ::MIME"text/plain", res::WindowResults) - println(io, "WindowResults") - descriptors = [ - "input timeseries" => summary(res.x), - "indicators" => [nameof(i) for i in res.config.indicators], - "indicator (window, stride)" => (res.config.width_ind, res.config.stride_ind), - "change metrics" => [nameof(c) for c in res.config.change_metrics], - show_changemetric(res), - ] - padlen = maximum(length(d[1]) for d in descriptors) + 2 - for (desc, val) in descriptors - println(io, rpad(" $(desc): ", padlen), val) - end -end - -function show_changemetric(res::SlidingWindowResults) - return "change metric (window, stride)" => (res.config.width_cha, res.config.stride_cha) -end - -function show_changemetric(res::SegmentWindowResults) - return "change metric (window)" => ([length(t) for t in res.t_indicator]) -end \ No newline at end of file diff --git a/src/significance/api_significance.jl b/src/significance/api_significance.jl index e3066e1b..a81306dd 100644 --- a/src/significance/api_significance.jl +++ b/src/significance/api_significance.jl @@ -10,10 +10,10 @@ abstract type TransitionsSignificance end """ - significant_transitions(res::WindowResults, signif::TransitionsSignificance) + significant_transitions(res::IndicatorsChangesResults, signif::TransitionsSignificance) Estimate significant transtions in `res` using the method described by `signif`. Return `flags`, a Boolean matrix with identical size as `res.x_change`. It contains trues wherever a change metric of `res` is deemed significant. """ -function significant_transitions(::WindowResults, ::TransitionsSignificance) end \ No newline at end of file +function significant_transitions(::IndicatorsChangesResults, ::TransitionsSignificance) end \ No newline at end of file diff --git a/src/significance/quantile_significance.jl b/src/significance/quantile_significance.jl index 3dfa98bc..332cf5ac 100644 --- a/src/significance/quantile_significance.jl +++ b/src/significance/quantile_significance.jl @@ -2,7 +2,7 @@ QuantileSignificance(; p = 0.95, tail = :both) <: TransitionsSignificance A configuration struct for significance testing [`significant_transitions`](@ref). -When used with [`WindowResults`](@ref), significance is estimated +When used with [`IndicatorsChangesResults`](@ref), significance is estimated by comparing the value of each change metric with its `p`-quantile. Values that exceed the `p`-quantile (if `tail = :right`) or subseed the `1-p`-quantile (if `tail = :left`) @@ -20,7 +20,7 @@ end using Statistics: quantile -function significant_transitions(res::WindowResults, signif::QuantileSignificance) +function significant_transitions(res::IndicatorsChangesResults, signif::QuantileSignificance) flags = similar(res.x_change, Bool) for (i, x) in enumerate(eachcol(res.x_change)) qmin, qmax = quantile(x, (1 - signif.p, signif.p)) @@ -42,7 +42,7 @@ end SigmaSignificance(; factor = 3.0, tail = :both) <: TransitionsSignificance A configuration struct for significance testing [`significant_transitions`](@ref). -When used with [`WindowResults`](@ref), significance is estimated +When used with [`IndicatorsChangesResults`](@ref), significance is estimated by comparing how many standard deviations (`σ`) the value exceeds the mean value (`μ`). Values that exceed (if `tail = :right`) `μ + factor*σ`, or subseed (if `tail = :left`) `μ - factor*σ` are deemed significant. @@ -60,7 +60,7 @@ end using Statistics: std, mean -function significant_transitions(res::WindowResults, signif::SigmaSignificance) +function significant_transitions(res::IndicatorsChangesResults, signif::SigmaSignificance) flags = similar(res.x_change, Bool) for (i, x) in enumerate(eachcol(res.x_change)) μ = mean(x) diff --git a/src/significance/surrogates_significance.jl b/src/significance/surrogates_significance.jl index 8533bd38..ac954a0b 100644 --- a/src/significance/surrogates_significance.jl +++ b/src/significance/surrogates_significance.jl @@ -15,7 +15,7 @@ using timeseries surrogates. ## Description -When used with [`WindowResults`](@ref), significance is estimated as follows: +When used with [`IndicatorsChangesResults`](@ref), significance is estimated as follows: `n` surrogates from the input timeseries are generated using `surromethod`, which is any `Surrogate` subtype provided by [TimeseriesSurrogates.jl](https://juliadynamics.github.io/TimeseriesSurrogates.jl/dev/api/). @@ -34,7 +34,7 @@ higher change metric, discriminatory statistic values. This is the case for stat that quantify entropy. For statistics that quantify autocorrelation, use `tail = :right` instead. For anything else, use the default `tail = :both`. An iterable of `tail` values can also be given, in which case a specific `tail` -is used for each change metric in [`WindowResults`](@ref). +is used for each change metric in [`IndicatorsChangesResults`](@ref). Note that the raw p-values can be accessed in the field `.pvalues`, after calling the [`significant_transitions`](@ref) function with `SurrogatesSignificance`, in case you wish @@ -90,7 +90,7 @@ function significant_transitions(res::SlidingWindowResults, signif::SurrogatesSi return pvalues .< signif.p end -function significant_transitions(res::SegmentWindowResults, signif::SurrogatesSignificance) +function significant_transitions(res::SegmentedWindowResults, signif::SurrogatesSignificance) # Unpack and sanity checks X = eltype(res.x_change) (; indicators, change_metrics, tseg_start, tseg_end) = res.config @@ -182,7 +182,7 @@ function segmented_surrogates_loop!( pval_right = zeros(length(pval)) pval_left = copy(pval_right) end - + # parallelized surrogate loop Threads.@threads for _ in 1:n_surrogates id = Threads.threadid()