| Title: | Clusters of Effects Curves in Quantile Regression Models |
|---|---|
| Description: | Clustering of curves based on a dissimilarity that combines the concordance of their shapes and their closeness, as proposed by Sottile and Adelfio (2019) <doi:10.1007/s00180-018-0817-8>. The method can be applied to the effects curves of quantile regression coefficients models fitted with 'qrcm', with univariate or multivariate responses, and to curves observed in time, as in functional data analysis. The package also implements the clustering of waveforms by trimmed k-means of functional principal component scores (Adelfio, Chiodi, D'Alessandro and Luzio, 2011). |
| Authors: | Gianluca Sottile [aut, cre] (ORCID: <https://orcid.org/0000-0001-9347-7251>), Giada Adelfio [aut] |
| Maintainer: | Gianluca Sottile <[email protected]> |
| License: | GPL-2 |
| Version: | 1.0.0 |
| Built: | 2026-10-18 11:30:14 UTC |
| Source: | https://github.com/gianluca-sottile/clusteff |
Hierarchical clustering of curves, such as the effects curves
of quantile regression coefficients models or curves
observed in time, based on the shape-distance dissimilarity of Sottile and
Adelfio (2019).
clustEff( curves, lower = NULL, upper = NULL, p = NULL, k = NULL, alpha = NULL, step = c("both", "shape", "distance"), deriv = 2, combine = c("product", "mean"), shape.tol = 1e-06, criterion = c("height", "pi.out", "dist.rel", "silhouette", "conf.int"), method = "ward.D2", diss = NULL, smooth = FALSE, df = NULL, conf.level = 0.9, stand = FALSE, legacy = FALSE, verbose = FALSE, ... ) ## S3 method for class 'clustEff' print(x, ...)clustEff( curves, lower = NULL, upper = NULL, p = NULL, k = NULL, alpha = NULL, step = c("both", "shape", "distance"), deriv = 2, combine = c("product", "mean"), shape.tol = 1e-06, criterion = c("height", "pi.out", "dist.rel", "silhouette", "conf.int"), method = "ward.D2", diss = NULL, smooth = FALSE, df = NULL, conf.level = 0.9, stand = FALSE, legacy = FALSE, verbose = FALSE, ... ) ## S3 method for class 'clustEff' print(x, ...)
curves |
a numeric matrix with one column for each curve and one row
for each value of |
lower, upper
|
optional matrices (same dimensions as |
p |
the grid on which the curves are evaluated (quantile orders, or
times). By default |
k |
the number of clusters: a single value, or a range |
alpha |
the quantile of the pointwise distances used by |
step, deriv, combine, shape.tol
|
arguments of |
criterion |
the criterion used to select the number of clusters when |
method |
the agglomeration method of |
diss |
an optional dissimilarity (an object of class |
smooth |
logical: if |
df |
degrees of freedom of the smoothing splines (default: chosen by generalized cross-validation). |
conf.level |
when the bands are not available, the bands of each cluster
are the pointwise quantiles of order |
stand |
logical: standardize each curve (center and scale over the grid)? |
legacy |
logical: reproduce 'clustEff' 0.3.x (dissimilarity with
|
verbose |
logical: print the progress? |
... |
arguments of 'clustEff' 0.3.x that were removed (they give an informative error, see clustEff-defunct). |
x |
an object of class |
The algorithm (Sottile and Adelfio, 2019, Section 3.2) computes the
dissimilarity matrix with distshape(), builds a dendrogram with
stats::hclust(), selects the number of clusters and computes the
mean curve (and mean bands) of each cluster.
Criteria for the number of clusters :
"height" (default): corresponds to the largest gap between
consecutive heights of the dendrogram.
"pi.out" (effects curves with bands): with and
the mean bands of cluster ,
is the average proportion of the curves lying outside the mean bands of their
cluster (Sottile and Adelfio, 2019, Section 3.1). It decreases with ;
is the elbow, i.e. the value at which the decrease slows down most.
"dist.rel" (waveforms): (Section 3.1), and is the first
number of clusters at which stops decreasing.
"silhouette": maximizes the average silhouette width
(default criterion of 'clustEff' 0.3.x).
"conf.int": criterion "conf.int" of 'clustEff' 0.3.x (coverage of the
mean bands weighted by the silhouette widths).
The paper selects minimizing or
, and writes with the
indicator of the curves inside the bands and a factor . In simulations
with 2 to 4 groups of effects curves these literal rules rarely found the true
number of clusters, while the rules above did better and "height" was the
most accurate; see the vignette.
An object of class "clustEff", a list with items:
clusters |
the cluster of each curve (clusters are numbered by increasing size). |
k |
the number of clusters. |
p, curves, lower, upper
|
the input (possibly smoothed or standardized). |
mean, mean.lower, mean.upper
|
matrices of the mean curves and mean bands of each cluster. |
signif |
logical matrix: are the mean bands of each cluster away from
zero at each p? ( |
diss, hclust, silhouette
|
the dissimilarity, the dendrogram and the
silhouette (see |
criterion |
the values of the criterion for each number of clusters. |
measures |
a data frame with, for each cluster, its size, the average
area between the curves and the mean curve (eq. 2 of the paper), the
correlation distance |
settings |
the options used. |
Sottile, G. and Adelfio, G. (2019). Clusters of effects curves in quantile regression models. Computational Statistics, 34, 551-569. doi:10.1007/s00180-018-0817-8
distshape(), extract_curves(), summary.clustEff(),
plot.clustEff(), fpcac().
## Waveforms: three groups of noisy curves set.seed(1234) tt <- seq(0, 1, length.out = 100) W <- cbind(sapply(1:5, function(i) sin(3 * pi * tt) + rnorm(100, 0, 0.1)), sapply(1:5, function(i) cos(3 * pi * tt) + rnorm(100, 0, 0.1)), sapply(1:5, function(i) sin(3 * pi * tt) * cos(pi * tt) + rnorm(100, 0, 0.1))) fit <- clustEff(W, p = tt, k = c(2, 5), smooth = TRUE) fit summary(fit) plot(fit) ## Effects curves with confidence bands: three groups of four curves p <- seq(0.01, 0.99, 0.01) B <- cbind(sapply(1:4, function(i) 1 + p + rnorm(1, 0, 0.05)), sapply(1:4, function(i) -1 + 2 * p^2 + rnorm(1, 0, 0.05)), sapply(1:4, function(i) 2 - 3 * p^3 + rnorm(1, 0, 0.05))) fit2 <- clustEff(B, lower = B - 0.2, upper = B + 0.2, p = p, k = c(2, 5)) fit2$clusters plot(fit2)## Waveforms: three groups of noisy curves set.seed(1234) tt <- seq(0, 1, length.out = 100) W <- cbind(sapply(1:5, function(i) sin(3 * pi * tt) + rnorm(100, 0, 0.1)), sapply(1:5, function(i) cos(3 * pi * tt) + rnorm(100, 0, 0.1)), sapply(1:5, function(i) sin(3 * pi * tt) * cos(pi * tt) + rnorm(100, 0, 0.1))) fit <- clustEff(W, p = tt, k = c(2, 5), smooth = TRUE) fit summary(fit) plot(fit) ## Effects curves with confidence bands: three groups of four curves p <- seq(0.01, 0.99, 0.01) B <- cbind(sapply(1:4, function(i) 1 + p + rnorm(1, 0, 0.05)), sapply(1:4, function(i) -1 + 2 * p^2 + rnorm(1, 0, 0.05)), sapply(1:4, function(i) 2 - 3 * p^3 + rnorm(1, 0, 0.05))) fit2 <- clustEff(B, lower = B - 0.2, upper = B + 0.2, p = p, k = c(2, 5)) fit2$clusters plot(fit2)
Computes the dissimilarity between curves of Sottile and Adelfio (2019, eq. 1), which combines the concordance of their shapes (the signs of their second derivatives) and their closeness (their distance relative to the distances between all pairs of curves), pointwise on a grid.
distshape( curves, p = NULL, alpha = 0.5, step = c("both", "shape", "distance"), deriv = 2, combine = c("product", "mean"), d = NULL, shape.tol = 1e-06, legacy = FALSE, ... )distshape( curves, p = NULL, alpha = 0.5, step = c("both", "shape", "distance"), deriv = 2, combine = c("product", "mean"), d = NULL, shape.tol = 1e-06, legacy = FALSE, ... )
curves |
a numeric matrix with one column for each curve and one row
for each value of |
p |
the grid on which the curves are evaluated (quantile orders, or
times). By default |
alpha |
the order of the quantile of the pointwise distances between all pairs of curves that defines two curves as close (default 0.5). Sottile and Adelfio (2019) suggest 0.25 for effects curves and 0.5 for waveforms. |
step |
which components are used: |
deriv |
the order of the derivative used for the shape component: 2 (default, convexity, as in the paper) or 1 (monotonicity). |
combine |
how the two components are combined when |
d |
an optional matrix of derivatives of order |
shape.tol |
derivatives smaller than |
legacy |
logical: if |
... |
arguments of 'clustEff' 0.3.x that were removed (they give an informative error). |
For two curves and ,
where is the -quantile of the distances
between all pairs of curves at . The
dissimilarity is
where the integral (normalized by the length of the interval) is computed with
the trapezoidal rule on the grid p. The computations are vectorized over the
pairs of curves and need memory for curves.
The paper defines the shape concordance as ; here derivatives below shape.tol are set to zero
and two zero derivatives are concordant, which avoids that, e.g., two linear
curves are always discordant. The shape component alone is a Hamming distance
between the sign patterns; the distance component, based on a threshold, does
not satisfy the triangle inequality in general. With only two curves the
distance component is always 1, and step = "both" reduces to "shape".
An object of class "dist" (see stats::dist()) with attributes
alpha, step, deriv, combine and legacy.
Sottile, G. and Adelfio, G. (2019). Clusters of effects curves in quantile regression models. Computational Statistics, 34, 551-569. doi:10.1007/s00180-018-0817-8
tt <- seq(0, 1, length.out = 100) curves <- cbind(a1 = sin(3 * pi * tt), a2 = sin(3 * pi * tt) + 0.1, b1 = cos(3 * pi * tt), b2 = cos(3 * pi * tt) - 0.1) distshape(curves, p = tt) distshape(curves, p = tt, step = "shape")tt <- seq(0, 1, length.out = 100) curves <- cbind(a1 = sin(3 * pi * tt), a2 = sin(3 * pi * tt) + 0.1, b1 = cos(3 * pi * tt), b2 = cos(3 * pi * tt) - 0.1) distshape(curves, p = tt) distshape(curves, p = tt, step = "shape")
Extracts the estimated quantile regression coefficient functions
(the effects curves), their pointwise confidence
bands and their second derivatives from one or more models fitted with
qrcm::iqr(), in the format used by clustEff().
extract_curves(object, p = seq(0.01, 0.99, 0.01), which, level = 0.95) ## S3 method for class 'effects_curves' print(x, ...)extract_curves(object, p = seq(0.01, 0.99, 0.01), which, level = 0.95) ## S3 method for class 'effects_curves' print(x, ...)
object |
an object of class |
p |
the grid of quantile orders on which the curves are evaluated. |
which |
the coefficients to extract: a vector of names or of indices of the rows of the coefficient matrix. By default all of them except the intercept. |
level |
confidence level of the pointwise bands (default 0.95). |
x |
an object of class |
... |
not used. |
The bands are ,
with standard errors from predict(object, type = "beta", se = TRUE).
The second derivatives , used by the shape component of
the dissimilarity of distshape(), are computed by central differences of the
smooth fitted functions (step ; exact up to rounding errors when
is a polynomial of degree up to 3 in , and very accurate
otherwise, unlike derivatives of interpolating splines of the estimates).
With a list of models (e.g., the same covariates for different responses, as in
the multivariate case of Sottile and Adelfio, 2019), the curves are named
"model:coefficient", where model is the name of the list element (or its
position).
An object of class "effects_curves", a list with items curves,
lower, upper and d2 (matrices with one row for each value of p and
one column for each curve), p, level and info (a data frame with the
model and the coefficient of each curve).
Sottile, G. and Adelfio, G. (2019). Clusters of effects curves in quantile regression models. Computational Statistics, 34, 551-569. doi:10.1007/s00180-018-0817-8
if (requireNamespace("qrcm", quietly = TRUE)) { set.seed(1) n <- 300 x <- runif(n, 0, 5) # five responses with three different effects of x slopes <- list(function(u) 0.5 + u, function(u) 0.5 + u, function(u) -1 + 0.5 * u, function(u) -1 + 0.5 * u, function(u) 2 * u^2) fits <- lapply(slopes, function(b) { u <- runif(n) y <- qnorm(u) + b(u) * x qrcm::iqr(y ~ x, formula.p = ~ I(p) + I(p^2)) }) names(fits) <- paste0("y", 1:5) ec <- extract_curves(fits, which = "x") ec fit <- clustEff(ec, k = 3) fit$clusters }if (requireNamespace("qrcm", quietly = TRUE)) { set.seed(1) n <- 300 x <- runif(n, 0, 5) # five responses with three different effects of x slopes <- list(function(u) 0.5 + u, function(u) 0.5 + u, function(u) -1 + 0.5 * u, function(u) -1 + 0.5 * u, function(u) 2 * u^2) fits <- lapply(slopes, function(b) { u <- runif(n) y <- qnorm(u) + b(u) * x qrcm::iqr(y ~ x, formula.p = ~ I(p) + I(p^2)) }) names(fits) <- paste0("y", 1:5) ec <- extract_curves(fits, which = "x") ec fit <- clustEff(ec, k = 3) fit$clusters }
Represents each curve on a B-spline basis by least squares and computes the
functional principal components (harmonics) and the scores, as in
Ramsay and Silverman (2005, Chapter 8). It is used by fpcac().
fpca_bspline( curves, t = NULL, nbasis = 5, norder = 3, nharmonics = 3, lambda = 1e-08 )fpca_bspline( curves, t = NULL, nbasis = 5, norder = 3, nharmonics = 3, lambda = 1e-08 )
curves |
a numeric matrix with one column for each curve and one row for each time point. |
t |
the time points (default: equally spaced on [0, 1]). |
nbasis |
number of B-spline basis functions. |
norder |
order of the B-splines (degree + 1; default 3, quadratic). |
nharmonics |
number of principal components. |
lambda |
ridge penalty on the basis coefficients (default 1e-8, for numerical stability). |
A list with items scores (curves x harmonics), values (the
eigenvalues), varprop (proportion of variance explained), coefs (basis
coefficients of the curves), harmonics (basis coefficients of the
harmonics), mean (basis coefficients of the mean curve), fitted (the
smoothed curves on t), basis (the basis matrix on t) and gram (the
Gram matrix of the basis).
Ramsay, J. O. and Silverman, B. W. (2005). Functional Data Analysis, 2nd ed. Springer.
tt <- seq(0, 1, length.out = 100) X <- sapply(1:20, function(i) sin(2 * pi * tt + runif(1)) + rnorm(100, 0, 0.1)) f <- fpca_bspline(X, tt, nbasis = 10, nharmonics = 2) f$varproptt <- seq(0, 1, length.out = 100) X <- sapply(1:20, function(i) sin(2 * pi * tt + runif(1)) + rnorm(100, 0, 0.1)) f <- fpca_bspline(X, tt, nbasis = 10, nharmonics = 2) f$varprop
Clusters curves (e.g., seismic waveforms) by a trimmed k-means applied to their functional principal component scores (Adelfio et al., 2011).
fpcac( curves, t = NULL, K = 2, nbasis = 5, norder = 3, nharmonics = 3, alpha = 0, nstart = 30, iter.max = 25, init = c("kmeans++", "random"), conf.level = 0.9, verbose = FALSE, ... ) ## S3 method for class 'fpcac' print(x, ...)fpcac( curves, t = NULL, K = 2, nbasis = 5, norder = 3, nharmonics = 3, alpha = 0, nstart = 30, iter.max = 25, init = c("kmeans++", "random"), conf.level = 0.9, verbose = FALSE, ... ) ## S3 method for class 'fpcac' print(x, ...)
curves |
a numeric matrix with one column for each curve and one row for each time point. |
t |
the time points (default: equally spaced on [0, 1]). |
K |
the number of clusters. |
nbasis, norder
|
number and order of the B-spline basis functions. |
nharmonics |
number of functional principal components. |
alpha |
trimming proportion: the |
nstart |
number of random starts. |
iter.max |
maximum number of concentration steps for each start. |
init |
initialization of the centers: |
conf.level |
level of the pointwise bands of the curves of each cluster. |
verbose |
logical: print the objective function of each start? |
... |
arguments of 'clustEff' 0.3.x that were removed (informative error). |
x |
an object of class |
Each curve is represented on a B-spline basis and summarized by the scores of
its first nharmonics functional principal components (see
fpca_bspline()). The trimmed k-means of García-Escudero and Gordaliza finds
K centers minimizing the mean squared distance of the closest
curves to their nearest center. Use
set.seed() for reproducible results.
An object of class "fpcac", a list with items clusters (0 =
trimmed), K, centers, obj.function, radius, scores, fpca (the
output of fpca_bspline()), curves, t, mean, mean.lower,
mean.upper (mean curve and pointwise bands of each cluster),
mean.dist (correlation distance of each curve from the mean of its
cluster) and call.
Adelfio, G., Chiodi, M., D'Alessandro, A. and Luzio, D. (2011). FPCA algorithm for waveform clustering. Journal of Communication and Computer, 8, 494-502.
García-Escudero, L. A. and Gordaliza, A. (2005). A proposal for robust curve clustering. Journal of Classification, 22, 185-201. doi:10.1007/s00357-005-0013-x
set.seed(1) tt <- seq(0, 1, length.out = 100) X <- cbind(sapply(1:10, function(i) sin(3 * pi * tt) + rnorm(100, 0, 0.2)), sapply(1:10, function(i) cos(3 * pi * tt) + rnorm(100, 0, 0.2)), sapply(1:2, function(i) rnorm(100, 0, 0.2))) # two outliers fit <- fpcac(X, t = tt, K = 2, nbasis = 10, alpha = 0.1) fit plot(fit)set.seed(1) tt <- seq(0, 1, length.out = 100) X <- cbind(sapply(1:10, function(i) sin(3 * pi * tt) + rnorm(100, 0, 0.2)), sapply(1:10, function(i) cos(3 * pi * tt) + rnorm(100, 0, 0.2)), sapply(1:2, function(i) rnorm(100, 0, 0.2))) # two outliers fit <- fpcac(X, t = tt, K = 2, nbasis = 10, alpha = 0.1) fit plot(fit)
Fits fpcac() for and computes the average
silhouette width or the total within-cluster sum of squares.
opt.fpcac( curves, t = NULL, k.max = 5, criterion = c("silhouette", "wss"), nbasis = 5, norder = 3, nharmonics = 3, alpha = 0, nstart = 30, iter.max = 25, init = c("kmeans++", "random"), diss = NULL, ... ) ## S3 method for class 'opt.fpcac' print(x, ...) ## S3 method for class 'opt.fpcac' plot(x, ...)opt.fpcac( curves, t = NULL, k.max = 5, criterion = c("silhouette", "wss"), nbasis = 5, norder = 3, nharmonics = 3, alpha = 0, nstart = 30, iter.max = 25, init = c("kmeans++", "random"), diss = NULL, ... ) ## S3 method for class 'opt.fpcac' print(x, ...) ## S3 method for class 'opt.fpcac' plot(x, ...)
curves, t, nbasis, norder, nharmonics, alpha, nstart, iter.max, init
|
arguments of |
k.max |
maximum number of clusters. |
criterion |
|
diss |
an optional dissimilarity between the curves (class |
... |
arguments of 'clustEff' 0.3.x that were removed (informative error). |
x |
an object of class |
An object of class "opt.fpcac", a list with items criterion (the
values for each ), K.opt, clusters (matrix of the clusters for
each ), fits (the fpcac() fits) and criterion.name.
Trimmed curves are excluded from the computation of the criteria.
set.seed(1) tt <- seq(0, 1, length.out = 100) X <- cbind(sapply(1:8, function(i) sin(3 * pi * tt) + rnorm(100, 0, 0.2)), sapply(1:8, function(i) cos(3 * pi * tt) + rnorm(100, 0, 0.2)), sapply(1:8, function(i) sin(3 * pi * tt) * cos(pi * tt) + rnorm(100, 0, 0.2))) o <- opt.fpcac(X, t = tt, k.max = 5, nbasis = 10) o$K.opt plot(o)set.seed(1) tt <- seq(0, 1, length.out = 100) X <- cbind(sapply(1:8, function(i) sin(3 * pi * tt) + rnorm(100, 0, 0.2)), sapply(1:8, function(i) cos(3 * pi * tt) + rnorm(100, 0, 0.2)), sapply(1:8, function(i) sin(3 * pi * tt) * cos(pi * tt) + rnorm(100, 0, 0.2))) o <- opt.fpcac(X, t = tt, k.max = 5, nbasis = 10) o$K.opt plot(o)
Plot Clusters of Curves
## S3 method for class 'clustEff' plot( x, type = c("clusters", "dendrogram", "silhouette", "dissimilarity", "criterion"), which, polygon = TRUE, col, ... )## S3 method for class 'clustEff' plot( x, type = c("clusters", "dendrogram", "silhouette", "dissimilarity", "criterion"), which, polygon = TRUE, col, ... )
x |
an object of class |
type |
what to plot: |
which |
the clusters to plot (for |
polygon |
logical: draw the bands as shaded areas (otherwise dashed lines)? |
col |
colours of the clusters. |
... |
further graphical parameters ( |
No return value, called for its side effects.
Plot an FPCAC Fit
## S3 method for class 'fpcac' plot(x, which, polygon = TRUE, col, ...)## S3 method for class 'fpcac' plot(x, which, polygon = TRUE, col, ...)
x |
an object of class |
which |
the clusters to plot. With a single cluster, its curves are drawn. |
polygon |
logical: draw the bands as shaded areas? |
col |
colours of the clusters. |
... |
further graphical parameters ( |
No return value, called for its side effects.
Summarize Clusters of Curves
## S3 method for class 'clustEff' summary(object, ...) ## S3 method for class 'summary.clustEff' print(x, digits = max(3L, getOption("digits") - 3L), ...)## S3 method for class 'clustEff' summary(object, ...) ## S3 method for class 'summary.clustEff' print(x, digits = max(3L, getOption("digits") - 3L), ...)
object |
an object of class |
... |
not used. |
x |
an object of class |
digits |
number of significant digits. |
An object of class "summary.clustEff": a list with the number of
clusters, the number of curves and grid points, the options used, the
clustering table, the within-cluster measures (with their weighted average)
and the criterion used to select the number of clusters.
Summarize an FPCAC Fit
## S3 method for class 'fpcac' summary(object, ...) ## S3 method for class 'summary.fpcac' print(x, digits = max(3L, getOption("digits") - 3L), ...)## S3 method for class 'fpcac' summary(object, ...) ## S3 method for class 'summary.fpcac' print(x, digits = max(3L, getOption("digits") - 3L), ...)
object |
an object of class |
... |
not used. |
x |
an object of class |
digits |
number of significant digits. |
An object of class "summary.fpcac" with the clustering table, the
number of trimmed curves, the proportion of variance of each harmonic and,
for each cluster, the average correlation distance of its curves from the
mean curve.