From e567bf569d6e9783c3e69a6b00c225e4ddf8777e Mon Sep 17 00:00:00 2001 From: Christoph Rust Date: Wed, 12 Mar 2025 08:58:08 +0100 Subject: [PATCH 1/4] feat: include call with result objects --- R/dpca.R | 3 ++- R/spca.R | 2 ++ tests/testthat/test_dpca.R | 5 ++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/R/dpca.R b/R/dpca.R index 57ed4f8..da31ddc 100644 --- a/R/dpca.R +++ b/R/dpca.R @@ -138,6 +138,7 @@ dpca <- function( } nx <- nrow(x) tx <- ncol(x) + cl <- match.call() if (!missing(q) && (length(q) > 1 || floor(abs(q)) != q)) { stop("\"q\" has to be a single positive integer!") @@ -195,13 +196,13 @@ dpca <- function( as.numeric(penalty_scales), PACKAGE = "dpca" ) - ## add cross-sectional means res$xmean <- mx if (select_q) { res$HL_select$penalty_scales <- penalty_scales } + res$call <- cl class(res) <- "dpca" res } diff --git a/R/spca.R b/R/spca.R index 3b440a5..130ab3a 100644 --- a/R/spca.R +++ b/R/spca.R @@ -56,6 +56,7 @@ spca <- function( if (is.null(n_path)) { n_path <- floor(seq(nrow(x) / 2, nrow(x), nrow(x) / 20)) } + cl <- match.call() ## centering mx <- rowMeans(x) @@ -100,6 +101,7 @@ spca <- function( res$HL_select <- hl_select } class(res) <- "spca" + res$call <- cl res } diff --git a/tests/testthat/test_dpca.R b/tests/testthat/test_dpca.R index 96fe324..2d1af25 100644 --- a/tests/testthat/test_dpca.R +++ b/tests/testthat/test_dpca.R @@ -33,7 +33,10 @@ test_that("Test dpca, stepwise", { system.time(res_dpca1 <- dpca::dpca(x = x, q = 4, freqs = freqs, bandwidth = bw, weights = "bartlett")) system.time(res_freqdom <- freqdom::dpca(t(x), bw, freqs, 4L)) res_dpca$xmean <- rowMeans(x) - class(res_dpca1) <- NULL + class(res_dpca) <- "dpca" + res_dpca1$call <- NULL + res_dpca$call <- NULL + expect_equal(res_dpca, res_dpca1) From f512f10d96a48fee3de22cd99a4025b223bc45e4 Mon Sep 17 00:00:00 2001 From: Christoph Rust Date: Wed, 12 Mar 2025 21:08:55 +0100 Subject: [PATCH 2/4] feat: extend s3 methods print and summary --- NAMESPACE | 1 + R/dpca.R | 1 + R/s3methods.R | 62 +++++++++++++++++++++++++++++++++----- man/plot.dpca.Rd | 4 +-- tests/testthat/test_dpca.R | 1 + 5 files changed, 60 insertions(+), 9 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index c30b6a6..101f763 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -4,6 +4,7 @@ S3method(plot,dpca) S3method(plot,spca) S3method(print,dpca) S3method(print,spca) +S3method(print,summary.dpca) S3method(summary,dpca) S3method(summary,spca) export(dpca) diff --git a/R/dpca.R b/R/dpca.R index da31ddc..9398b65 100644 --- a/R/dpca.R +++ b/R/dpca.R @@ -203,6 +203,7 @@ dpca <- function( } res$call <- cl + res$freqs <- freqs class(res) <- "dpca" res } diff --git a/R/s3methods.R b/R/s3methods.R index 7c9831e..f538d25 100644 --- a/R/s3methods.R +++ b/R/s3methods.R @@ -5,23 +5,67 @@ #' @param ... Further pass-through arguments. #' @export print.dpca <- function(x, ...) { - cat("\nDynamic principal component estimation object:\n") - cat(sprintf(" Number of selected dynamic components: %s\n", x$HL_select$q)) + cat("\nDynamic principal component estimation\n") + cat("\nCall:\n", paste(deparse(x$call), sep = "\n", collapse = "\n"), + "\n\n", + sep = "" + ) + cat(paste("Number of dynamic components:", dim(x$ndpc)[1]), "\n") } #' Summary method for object of class \code{dpca}. #' @param object An object of type \code{spca}. #' @param ... Further pass-through arguments. #' @export -summary.dpca <- function(object, ...) print.dpca(object) +summary.dpca <- function(object, ...) { + ps <- object$eig$vectors + lambda <- object$eig$values + ndim <- dim(ps)[2] + spec_chi <- vapply(seq_along(object$freqs), function(i) { + crossprod( + Conj(ps[, , i]) * lambda[, i], ps[, , i] + ) + }, matrix(0i, nrow = ndim, ncol = ndim)) + + z <- list() + z$freqs <- object$freqs + z$gamma <- apply(object$spectrum, c(1, 2), sum) / length(object$freqs) + z$gamma_chi <- apply(spec_chi, c(1, 2), sum) / length(object$freqs) + z$q <- dim(ps)[1] + z$object <- object + + class(z) <- "summary.dpca" + z +} + +#' Summary method for object of class \code{summary.dpca}. +#' +#' @param x An object of type \code{summary.dpca}. +#' @param ... Further pass-through arguments. +#' @export +print.summary.dpca <- function(x, ...) { + trace_chi <- Re(sum(diag(x$gamma_chi))) + trace_x <- Re(sum(diag(x$gamma))) + cat("\nDynamic principal component estimation summary\n\n") + cat("\nCall:\n", paste(deparse(x$object$call), sep = "\n", collapse = "\n"), + "\n\n", + sep = "" + ) + cat(paste("Number of dynamic components:", x$q, "\n")) + cat(paste("Proportion of explained variance: ", round(trace_chi / trace_x, 2), "\n")) +} #' Print method for object of class \code{spca}. #' @param x An object of type \code{spca}. #' @param ... Further pass-through arguments. #' @export print.spca <- function(x, ...) { - cat("\nStatic principal component estimation object:\n") - cat(sprintf(" Number of selected static components: %s\n", x$HL_select$r)) + cat("\nStatic principal component estimation\n") + cat("\nCall:\n", paste(deparse(x$call), sep = "\n", collapse = "\n"), + "\n\n", + sep = "" + ) + cat(paste("Number of dynamic components:", dim(x$factors)[1]), "\n") } #' Summary method for object of class \code{spca}. @@ -30,14 +74,18 @@ print.spca <- function(x, ...) { #' @export summary.spca <- function(object, ...) print.spca(object) -#' Plot q-selection graphcs for a \code{dpca} object. +#' Diagnostics plot for a \code{dpca} object. +#' #' @param x An object of type \code{spca}. #' @param ... Further pass-through arguments. #' @importFrom graphics axis mtext par #' @export plot.dpca <- function(x, ...) { if (is.null(x$HL_select)) { - warning("No data-driven selection of number of dynamic factors for passed object. Nothing to plot!") + warning(paste( + "No data-driven selection of number of dynamic factors for passed object.", + "Nothing to plot!" + )) return(invisible()) } par(mar = c(5, 4, 4, 6)) diff --git a/man/plot.dpca.Rd b/man/plot.dpca.Rd index 9b0d27f..2964bd5 100644 --- a/man/plot.dpca.Rd +++ b/man/plot.dpca.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/s3methods.R \name{plot.dpca} \alias{plot.dpca} -\title{Plot q-selection graphcs for a \code{dpca} object.} +\title{Diagnostics plot for a \code{dpca} object.} \usage{ \method{plot}{dpca}(x, ...) } @@ -12,5 +12,5 @@ \item{...}{Further pass-through arguments.} } \description{ -Plot q-selection graphcs for a \code{dpca} object. +Diagnostics plot for a \code{dpca} object. } diff --git a/tests/testthat/test_dpca.R b/tests/testthat/test_dpca.R index 2d1af25..41ce3c6 100644 --- a/tests/testthat/test_dpca.R +++ b/tests/testthat/test_dpca.R @@ -36,6 +36,7 @@ test_that("Test dpca, stepwise", { class(res_dpca) <- "dpca" res_dpca1$call <- NULL res_dpca$call <- NULL + res_dpca$freqs <- freqs expect_equal(res_dpca, res_dpca1) From 557b413075867f3c18a4e41682d762d13fd5f42d Mon Sep 17 00:00:00 2001 From: Christoph Rust Date: Sat, 5 Apr 2025 10:12:35 +0200 Subject: [PATCH 3/4] feat: add summary and print.summary method for spca --- NAMESPACE | 1 + R/s3methods.R | 44 +++++++++++++++++++++++++++++++++++++++++--- R/spca.R | 2 +- man/plot.dpca.Rd | 2 +- man/spca.Rd | 2 +- 5 files changed, 45 insertions(+), 6 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 101f763..cc00b2f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -5,6 +5,7 @@ S3method(plot,spca) S3method(print,dpca) S3method(print,spca) S3method(print,summary.dpca) +S3method(print,summary.spca) S3method(summary,dpca) S3method(summary,spca) export(dpca) diff --git a/R/s3methods.R b/R/s3methods.R index f538d25..e5f7cab 100644 --- a/R/s3methods.R +++ b/R/s3methods.R @@ -52,7 +52,7 @@ print.summary.dpca <- function(x, ...) { sep = "" ) cat(paste("Number of dynamic components:", x$q, "\n")) - cat(paste("Proportion of explained variance: ", round(trace_chi / trace_x, 2), "\n")) + cat(paste("Proportion of explained total variance: ", round(trace_chi / trace_x, 2), "\n")) } #' Print method for object of class \code{spca}. @@ -72,11 +72,44 @@ print.spca <- function(x, ...) { #' @param object An object of type \code{spca}. #' @param ... Further pass-through arguments. #' @export -summary.spca <- function(object, ...) print.spca(object) +summary.spca <- function(object, ...) { + ps <- object$eig$vectors + lambda <- object$eig$values + cov_chi <- tcrossprod( + t(t(ps) * lambda), ps + ) + + z <- list() + z$cov <- object$cov + z$cov_chi <- cov_chi + z$r <- length(lambda) + z$object <- object + + class(z) <- "summary.spca" + z +} + +#' Summary method for object of class \code{summary.spca}. +#' +#' @param x An object of type \code{summary.spca}. +#' @param ... Further pass-through arguments. +#' @export +print.summary.spca <- function(x, ...) { + trace_chi <- sum(diag(x$cov_chi)) + trace_x <- sum(diag(x$cov)) + cat("basdfadf") + cat("\nStatic principal component estimation summary\n\n") + cat("\nCall:\n", paste(deparse(x$object$call), sep = "\n", collapse = "\n"), + "\n\n", + sep = "" + ) + cat(paste("Number of static components:", x$r, "\n")) + cat(paste("Proportion of explained total variance: ", round(trace_chi / trace_x, 2), "\n")) +} #' Diagnostics plot for a \code{dpca} object. #' -#' @param x An object of type \code{spca}. +#' @param x An object of type \code{dpca}. #' @param ... Further pass-through arguments. #' @importFrom graphics axis mtext par #' @export @@ -110,6 +143,11 @@ plot.dpca <- function(x, ...) { ) } +#' Diagnostics plot for an \code{spca} object. +#' +#' @param x An object of type \code{spca}. +#' @param ... Further pass-through arguments. +#' @importFrom graphics axis mtext par #' @export plot.spca <- function(x, ...) { r_selection <- x$HL_select diff --git a/R/spca.R b/R/spca.R index 130ab3a..161a257 100644 --- a/R/spca.R +++ b/R/spca.R @@ -24,7 +24,7 @@ #' \itemize{ #' \item \code{xmean}: a vector holding the mean of each cross-sectional unit #' \item \code{cov}: variance-covariance-matrix of \code{x} -#' \item \code{eig}: eigen decomposition of \code{cov} +#' \item \code{eig}: truncated eigen decomposition of \code{cov} #' \item \code{factors}: an \eqn{r} times \eqn{T} dimensional matrix with the computed factors #' \item \code{cc}: (static) common component #' \item \code{ic}: (static) idiosyncratic component diff --git a/man/plot.dpca.Rd b/man/plot.dpca.Rd index 2964bd5..3ad6b0d 100644 --- a/man/plot.dpca.Rd +++ b/man/plot.dpca.Rd @@ -7,7 +7,7 @@ \method{plot}{dpca}(x, ...) } \arguments{ -\item{x}{An object of type \code{spca}.} +\item{x}{An object of type \code{dpca}.} \item{...}{Further pass-through arguments.} } diff --git a/man/spca.Rd b/man/spca.Rd index 16b1267..dec2619 100644 --- a/man/spca.Rd +++ b/man/spca.Rd @@ -39,7 +39,7 @@ An object of class "spca", wrapping a list with the entries \itemize{ \item \code{xmean}: a vector holding the mean of each cross-sectional unit \item \code{cov}: variance-covariance-matrix of \code{x} - \item \code{eig}: eigen decomposition of \code{cov} + \item \code{eig}: truncated eigen decomposition of \code{cov} \item \code{factors}: an \eqn{r} times \eqn{T} dimensional matrix with the computed factors \item \code{cc}: (static) common component \item \code{ic}: (static) idiosyncratic component From dc1eebc565b71cb95f6ebcc57a7cf06539f6c174 Mon Sep 17 00:00:00 2001 From: Christoph Rust Date: Sat, 5 Apr 2025 11:39:30 +0200 Subject: [PATCH 4/4] chore: update documentation --- R/dpca.R | 4 +++- man/dpca.Rd | 4 +++- man/plot.spca.Rd | 16 ++++++++++++++++ man/print.summary.dpca.Rd | 16 ++++++++++++++++ man/print.summary.spca.Rd | 16 ++++++++++++++++ 5 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 man/plot.spca.Rd create mode 100644 man/print.summary.dpca.Rd create mode 100644 man/print.summary.spca.Rd diff --git a/R/dpca.R b/R/dpca.R index 9398b65..668d233 100644 --- a/R/dpca.R +++ b/R/dpca.R @@ -53,7 +53,9 @@ #' @return A object of class "dpca" wrapping a list with the entries #' \itemize{ #' \item \code{xmean}: a vector holding the mean of each cross-sectional unit -#' \item \code{spectrum}: the estimated spectral density +#' \item \code{spectrum}: the estimated spectral density without the +#' normalization by a factor of 2 \code{pi}. Therefore, a white noise +#' process would have a spectrum equal to one a.e. #' \item \code{eig}: eigen decomposition of the spectral density #' \item \code{filter}: a list holding the filter coefficients for the filter #' returning input and dynamic common component. diff --git a/man/dpca.Rd b/man/dpca.Rd index af49575..5cae925 100644 --- a/man/dpca.Rd +++ b/man/dpca.Rd @@ -64,7 +64,9 @@ suggested in Hallin & Liska (2007, p. 611) is used: A object of class "dpca" wrapping a list with the entries \itemize{ \item \code{xmean}: a vector holding the mean of each cross-sectional unit - \item \code{spectrum}: the estimated spectral density + \item \code{spectrum}: the estimated spectral density without the + normalization by a factor of 2 \code{pi}. Therefore, a white noise + process would have a spectrum equal to one a.e. \item \code{eig}: eigen decomposition of the spectral density \item \code{filter}: a list holding the filter coefficients for the filter returning input and dynamic common component. diff --git a/man/plot.spca.Rd b/man/plot.spca.Rd new file mode 100644 index 0000000..2bb2900 --- /dev/null +++ b/man/plot.spca.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s3methods.R +\name{plot.spca} +\alias{plot.spca} +\title{Diagnostics plot for an \code{spca} object.} +\usage{ +\method{plot}{spca}(x, ...) +} +\arguments{ +\item{x}{An object of type \code{spca}.} + +\item{...}{Further pass-through arguments.} +} +\description{ +Diagnostics plot for an \code{spca} object. +} diff --git a/man/print.summary.dpca.Rd b/man/print.summary.dpca.Rd new file mode 100644 index 0000000..03f64e6 --- /dev/null +++ b/man/print.summary.dpca.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s3methods.R +\name{print.summary.dpca} +\alias{print.summary.dpca} +\title{Summary method for object of class \code{summary.dpca}.} +\usage{ +\method{print}{summary.dpca}(x, ...) +} +\arguments{ +\item{x}{An object of type \code{summary.dpca}.} + +\item{...}{Further pass-through arguments.} +} +\description{ +Summary method for object of class \code{summary.dpca}. +} diff --git a/man/print.summary.spca.Rd b/man/print.summary.spca.Rd new file mode 100644 index 0000000..89ef7f5 --- /dev/null +++ b/man/print.summary.spca.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/s3methods.R +\name{print.summary.spca} +\alias{print.summary.spca} +\title{Summary method for object of class \code{summary.spca}.} +\usage{ +\method{print}{summary.spca}(x, ...) +} +\arguments{ +\item{x}{An object of type \code{summary.spca}.} + +\item{...}{Further pass-through arguments.} +} +\description{ +Summary method for object of class \code{summary.spca}. +}