Type: Package
Title: Robust Changepoint Detection for Functional and Multivariate Data
Version: 0.2.3
Description: Detect and test for changes in covariance structures of functional data, as well as changepoint detection for multivariate data more generally. Method for detecting non-stationarity in resting state functional Magnetic Resonance Imaging (fMRI) scans as seen in Ramsay, K., & Chenouri, S. (2025) <doi:10.1080/10485252.2025.2503891> is implemented in fmri_changepoints(). Also includes depth- and rank-based implementation of the wild binary segmentation algorithm for detecting multiple changepoints in multivariate data.
License: MIT + file LICENSE
URL: https://github.com/adeeb99/KWCChangepoint, https://adeeb99.github.io/KWCChangepoint/
BugReports: https://github.com/adeeb99/KWCChangepoint/issues
Depends: R (≥ 4.1)
Encoding: UTF-8
Imports: ddalpha, fda.usc, tibble, Rcpp (≥ 1.0.12)
LinkingTo: Rcpp, RcppArmadillo
Suggests: testthat (≥ 3.0.0)
Config/testthat/edition: 3
SystemRequirements: C++17
RoxygenNote: 7.3.2
NeedsCompilation: yes
Packaged: 2025-11-23 00:19:01 UTC; adeeb
Author: Adeeb Rouhani ORCID iD [aut, cre, cph], Kelly Ramsay ORCID iD [aut]
Maintainer: Adeeb Rouhani <adeeb.rouhani@gmail.com>
Repository: CRAN
Date/Publication: 2025-11-26 20:50:02 UTC

KWCChangepoint: Robust Changepoint Detection for Functional and Multivariate Data

Description

Detect and test for changes in covariance structures of functional data, as well as changepoint detection for multivariate data more generally. Method for detecting non-stationarity in resting state functional Magnetic Resonance Imaging (fMRI) scans as seen in Ramsay, K., & Chenouri, S. (2025) doi:10.1080/10485252.2025.2503891 is implemented in fmri_changepoints(). Also includes depth- and rank-based implementation of the wild binary segmentation algorithm for detecting multiple changepoints in multivariate data.

Links

Author(s)

Maintainer: Adeeb Rouhani adeeb.rouhani@gmail.com (ORCID) [copyright holder]

Authors:

See Also

Useful links:


Conduct an AMOC hypothesis test

Description

Conduct an at-most one changepoint hypothesis test for changes in the covariance operator of functional data based on the FKWC (functional Kruskal–Wallis covariance changepoint) procedures outlined by Ramsay and Chenouri (2025).

Usage

amoc_test(data, ranks = NULL, depth = c("RPD", "FM", "LTR", "FMd", "RPDd"))

Arguments

data

Data in matrix or data.frame form, where each row is an observation and each column is a dimension.

ranks

Optional if data is already ranked.

depth

Depth function of choice.

Value

A list consisting of:

Note

The options for the depth argument are as follows:

References

Ramsay, K., & Chenouri, S. (2025). Robust changepoint detection in the variability of multivariate functional data. Journal of Nonparametric Statistics. https://doi.org/10.1080/10485252.2025.2503891

Examples

set.seed(11)
test_data <- rbind(replicate(3,rnorm(200,1,1)), #before changepoint
                   replicate(3,rnorm(200,1,5))) #after changepoint

amoc_test(test_data)

Find changepoints using depth-based wild binary segmentation

Description

Detect multiple changepoints in multivariate data using the depth-based wild binary segmentation algorithm (Ramsay and Chenouri, 2023).

Usage

dwbs(
  data,
  numInt = 10,
  thresh = 1.3584,
  alpha = 1,
  depth = c("spat", "hs", "mahal", "mahal75")
)

Arguments

data

Data in matrix or data.frame form, where each row is an observation and each column is a dimension.

numInt

Number of intervals to be generated.

thresh

Numeric scalar; detection threshold. Larger values make detection more conservative.

alpha

Set as 1 by default, applying a standard SIC penalty. Set to a number larger than 1 for a strengthened SIC.

depth

Depth function.

Value

A list consisting of:

Note

The options for the depth argument are as follows:

References

Fryzlewicz, Piotr. “Wild Binary Segmentation for Multiple Change-Point Detection.” The Annals of Statistics 42, no. 6 (2014). https://doi.org/10.1214/14-AOS1245.

Killick, R., P. Fearnhead, and I. A. Eckley. “Optimal Detection of Changepoints With a Linear Computational Cost.” Journal of the American Statistical Association 107, no. 500 (2012): 1590–98. https://doi.org/10.1080/01621459.2012.737745.

Ramsay, K., & Chenouri, S. (2023). Robust nonparametric multiple changepoint detection for multivariate variability. Econometrics and Statistics. https://doi.org/10.1016/j.ecosta.2023.09.001

Examples

set.seed(11)
exdata <- rbind(replicate(3,rnorm(200)),
                replicate(3,rnorm(200,10)),
                replicate(3,rnorm(200,0.2)))
dwbs(data = exdata)

# Increasing `numInt` will result in more accurate detection
dwbs(data = exdata, numInt = 100)


Test for an epidemic period in data

Description

Test for a temporary change in the covariance operator of functional data using the FKWC (functional Kruskal–Wallis covariance changepoint) procedures outlined by Ramsay and Chenouri (2025).

Usage

epidemic_test(data, ranks = NULL, depth = c("RPD", "FM", "LTR", "FMd", "RPDd"))

Arguments

data

Data in matrix or data.frame form, where each row is an observation and each column is a dimension.

ranks

Optional if data is already ranked.

depth

Depth function of choice.

Value

A list consisting of:

Note

The options for the depth argument are as follows:

References

Ramsay, K., & Chenouri, S. (2025). Robust changepoint detection in the variability of multivariate functional data. Journal of Nonparametric Statistics. https://doi.org/10.1080/10485252.2025.2503891

Examples

set.seed(11)
epi_test <- rbind(replicate(3,rnorm(200)),
                  replicate(3,rnorm(200,10)),
                  replicate(3,rnorm(200,0.2)))

epidemic_test(epi_test)


Detect changepoints in functional data

Description

More specifically, fkwc() uses the functional Kruskal-Wallis tests for covariance changepoint algorithm (FKWC) to detect changes in the covariance operator.

Usage

fkwc(data, depth = c("RPD", "FM", "LTR", "FMd", "RPDd"), k = 0.25)

Arguments

data

Functional data in matrix or data.frame form, where each row is an observation/function and the columns are the grid.

depth

Depth function of choice.

k

Penalty constant passed to pruned exact linear time algorithm.

Value

A list consisting of:

Note

The options for the depth argument are as follows:

The penalty is of the form

3.74 + k\sqrt{n}

where n is the number of observations. In the case that there is potentially correlated observations, the parameter could be set to k=1. More information could be found in the reference.

References

Killick, R., P. Fearnhead, and I. A. Eckley. “Optimal Detection of Changepoints With a Linear Computational Cost.” Journal of the American Statistical Association 107, no. 500 (2012): 1590–98. https://doi.org/10.1080/01621459.2012.737745.

Ramsay, K., & Chenouri, S. (2025). Robust changepoint detection in the variability of multivariate functional data. Journal of Nonparametric Statistics. https://doi.org/10.1080/10485252.2025.2503891

Examples


set.seed(2)
# Generating 80 observations, with a changepoint (in our case a change in
# kernel) at observation 40
n  <- 80
k0 <- 40
T  <- 30
t  <- seq(0, 1, length.out = T)


# Both kernels K1 and K2 are Gaussian (or squared exponential) kernels but
# with different lengthscale values, and thus we hope to detect it.
K_se <- function(s, t, ell) exp(- ( (s - t)^2 ) / (2 * ell^2))
K1   <- outer(t, t, function(a,b) K_se(a,b, ell = 0.20))
K2   <- outer(t, t, function(a,b) K_se(a,b, ell = 0.07))

L1 <- chol(K1 + 1e-8 * diag(T))
L2 <- chol(K2 + 1e-8 * diag(T))

Z1 <- matrix(rnorm(k0 * T),      k0,      T)
Z2 <- matrix(rnorm((n-k0) * T),  n - k0,  T)

# We finally have an 80 x 30 matrix where the rows are the observations and
# the columns are the grid points.
X  <- rbind(Z1 %*% t(L1), Z2 %*% t(L2))

fkwc(X)


Multisample hypothesis test for difference in covariance operators

Description

Executes a multisample hypothesis test for differences in covariance operators using functional Kruskal–Wallis tests for covariance (FKWC) as outlined by Ramsay and Chenouri (2024). The function requires the first order derivative of the functional data in order to better detect changes.

Usage

fkwc_multisample(data, derivs, g, p = 20)

Arguments

data

Functional data in matrix or data.frame form, where each row is an observation/function and the columns are the grid.

derivs

First order derivative of the functional data in matrix or data.frame form.

g

A factor object that indicates which sample each row of data belongs to.

p

Number of random projections to be generated in order to compute random projection depths of the data.

Value

A list consisting of:

References

Ramsay, K., & Chenouri, S. (2024). Robust nonparametric hypothesis tests for differences in the covariance structure of functional data. Canadian Journal of Statistics, 52 (1), 43–78. https://doi.org/10.1002/cjs.11767

See Also

fda.usc::fdata.deriv(): for approximating the first order derivative if unavailable.

fkwc_posthoc(): for a post-hoc version of this test

Examples

set.seed(111)
t <- seq(0, 1, length.out = 200)

### Generating three sets of Brownian curves with different kernels, each
### kernel generating 20 observations
# Brownian process 1
fd1 <- fda.usc::rproc2fdata(n = 20, t = t, sigma = "brownian",
                           par.list = list(scale = 10, theta = 1))
fd1_d <- fda.usc::fdata.deriv(fd1)

# Brownian process 2
fd2 <- fda.usc::rproc2fdata(n = 20, t = t, sigma = "brownian",
                           par.list = list(scale = 1, theta = 1))
fd2_d <- fda.usc::fdata.deriv(fd2)

# Brownian process 3
fd3 <- fda.usc::rproc2fdata(n = 20, t = t, sigma = "brownian",
                           par.list = list(scale = 1, theta = 5))
fd3_d <- fda.usc::fdata.deriv(fd3)

# Functional data in one matrix and first order derivatives in another matrix
funcdata <- rbind(fd1$data, fd2$data, fd3$data)
funcderivs <- rbind(fd1_d$data, fd2_d$data, fd3_d$data)

fkwc_multisample(data = funcdata,
                 derivs = funcderivs,
                 g = factor(rep(1:3, each = 20)),
                 p = 1000)

Post-hoc hypothesis test for difference in covariance operators.

Description

This function is post-hoc, pairwise test version of fkwc_multisample()

Usage

fkwc_posthoc(data, derivs, g, p = 20)

Arguments

data

Functional data in matrix or data.frame form, where each row is an observation/function and the columns are the grid.

derivs

First order derivative of the functional data in matrix or data.frame form.

g

A factor object that indicates which sample each row of data belongs to.

p

Number of random projections to be generated in order to compute random projection depths of the data.

Value

A matrix of p-values for each pairwise comparison with a Šidák correction applied.

References

Ramsay, K., & Chenouri, S. (2024). Robust nonparametric hypothesis tests for differences in the covariance structure of functional data. Canadian Journal of Statistics, 52 (1), 43–78. https://doi.org/10.1002/cjs.11767

See Also

fda.usc::fdata.deriv: for approximating the first order derivative if unavailable.

Examples

set.seed(111)
t <- seq(0, 1, length.out = 200)

### Generating three sets of brownian curves with different kernels
# Brownian process 1
fd1 <- fda.usc::rproc2fdata(n = 20, t = t, sigma = "brownian",
                           par.list = list(scale = 10, theta = 1))
fd1_d <- fda.usc::fdata.deriv(fd1)

# Brownian process 2
fd2 <- fda.usc::rproc2fdata(n = 20, t = t, sigma = "brownian",
                           par.list = list(scale = 1, theta = 1))
fd2_d <- fda.usc::fdata.deriv(fd2)

# Brownian process 3
fd3 <- fda.usc::rproc2fdata(n = 20, t = t, sigma = "brownian",
                           par.list = list(scale = 1, theta = 5))
fd3_d <- fda.usc::fdata.deriv(fd3)

# Functional data in one matrix and first order derivatives in another matrix
funcdata <- rbind(fd1$data, fd2$data, fd3$data)
funcderivs <- rbind(fd1_d$data, fd2_d$data, fd3_d$data)

fkwc_posthoc(data = funcdata,
             derivs = funcderivs,
             g = factor(rep(1:3, each = 20)),
             p = 1000)

Detect changepoints in a resting state fMRI scan

Description

Functional magnetic resonance imaging scans are expected to be stationary after being pre-processed. This function attempts to find potential changepoints using the findings of Ramsay and Chenouri (2025).

Usage

fmri_changepoints(data, p = 100, k = 0.3)

Arguments

data

A four dimensional array, where the fourth dimension is time.

p

Number of random vector projections, set to 100 by default.

k

Penalty constant passed to pruned exact linear time algorithm.

Value

A list consisting of:

Note

The penalty is of the form

3.74 + k\sqrt{n}

where n is the number of observations. In the case that there is potentially correlated observations, the parameter could be set to k=1. More information could be found in the reference.

The example in this document is a simple "toy example", as good fMRI data simulation requires more dependencies. For generating fMRI data, see neuRosim::simVOLfmri(), neuRosim::simTSrestingstate().

References

Ramsay, K., & Chenouri, S. (2025). Robust changepoint detection in the variability of multivariate functional data. Journal of Nonparametric Statistics. https://doi.org/10.1080/10485252.2025.2503891

Examples

# In order to replicate how a changepoint would appear in a resting-state
# fMRI scan in a manner that is not computationally expensive, this example
# constructs an image of a 3D ball taken at 12 time stamps. The noise, and
# therefore the covariance function, changes at time stamp 6.
x_dim <- 24
y_dim <- 24
z_dim <- 10
time_dim <- 12
image_array <- array(0, dim = c(x_dim, y_dim, z_dim, time_dim))

center <- c(x_dim / 2, y_dim / 2, z_dim / 2)
radius <- min(x_dim, y_dim, z_dim) / 4

set.seed(42)

for (t in 1:time_dim) {
  for (x in 1:x_dim) {
    for (y in 1:y_dim) {
      for (z in 1:z_dim) {
        dist_from_center <- sqrt((x - center[1])^2 + (y - center[2])^2 + (z - center[3])^2)
        if (dist_from_center <= radius) {
          # Adding noise with increasing variability at timestamp 6
          if (t <= 6) {
            noise <- rnorm(1, mean = 0, sd = 0.1)  # Low variability noise
          } else {
            noise <- rnorm(1, mean = 0, sd = 2)  # High variability noise
          }
          image_array[x, y, z, t] <- noise
        } else {
          # Add lower intensity noise outside the ball
          image_array[x, y, z, t] <- rnorm(1, mean = 0, sd = 0.005)
        }
      }
    }
  }
}
fmri_changepoints(image_array, k = 0.1, p = 10)


Find changepoints in multivariate data

Description

The mkwp() function detects changepoints in multivariate data using multivariate Kruskal-Wallis PELT (MKWP) algorithm developed by Ramsay and Chenouri (2023).

Usage

mkwp(data, depth = c("spat", "mahal", "mahal75", "hs"), k = 0.2)

Arguments

data

Data in matrix or data.frame form, where each row is an observation and each column is a dimension.

depth

Depth function.

k

Penalty constant passed to pruned exact linear time algorithm.

Value

A list consisting of:

Note

The options for the depth argument are as follows:

The penalty is of the form

3.74 + k\sqrt{n}

where n is the number of observations. In the case that there is potentially correlated observations, the parameter could be set to k=1. More information could be found in the reference.

References

Killick, R., P. Fearnhead, and I. A. Eckley. “Optimal Detection of Changepoints With a Linear Computational Cost.” Journal of the American Statistical Association 107, no. 500 (2012): 1590–98. https://doi.org/10.1080/01621459.2012.737745.

Ramsay, K., & Chenouri, S. (2023). Robust nonparametric multiple changepoint detection for multivariate variability. Econometrics and Statistics. https://doi.org/10.1016/j.ecosta.2023.09.001

Examples

set.seed(111)
multi_data <-rbind(replicate(3,rnorm(200)),
                   replicate(3,rnorm(200,10)),
                   replicate(3,rnorm(200,0.2)))
mkwp(multi_data)


Find mean changes in a univariate sequence

Description

The uni_mean() function ranks the observations from smallest to largest, then applies the pruned exact linear time algorithm with the penalty parameter beta to detect changepoints.

Usage

uni_mean(data, beta = 10)

Arguments

data

A vector or one-dimensional array.

beta

Numeric penalty constant passed to pruned exact linear time algorithm.

Value

A list consisting of:

References

Killick, R., P. Fearnhead, and I. A. Eckley. “Optimal Detection of Changepoints With a Linear Computational Cost.” Journal of the American Statistical Association 107, no. 500 (2012): 1590–98. https://doi.org/10.1080/01621459.2012.737745.

Examples

set.seed(11)
mean_test <- c(rnorm(100, mean = 0), # before change in mean
               rnorm(100, mean = 5)) # after change in mean
uni_mean(mean_test)



Find scale changes in a univariate sequence

Description

The uni_scale() function ranks the observations based on their distance from the mean, then applies the pruned exact linear time algorithm with the penalty parameter beta to detect changepoints.

Usage

uni_scale(data, beta = 10)

Arguments

data

A vector or one-dimensional array.

beta

Numeric penalty constant passed to pruned exact linear time algorithm, 10 by default.

Value

A list consisting of:

References

Killick, R., P. Fearnhead, and I. A. Eckley. “Optimal Detection of Changepoints With a Linear Computational Cost.” Journal of the American Statistical Association 107, no. 500 (2012): 1590–98. https://doi.org/10.1080/01621459.2012.737745.

Examples

set.seed(11)
scale_test <- c(rnorm(100, sd=5), # before change in sale
                rnorm(100, sd=1)) # after change in scale
uni_scale(scale_test)