Nothing
#' @title Join climate summaries to dendrometer phase windows
#'
#' @description
#' Summarizes climate conditions within each phase window of
#' \code{phase.zg()} or \code{phase.sc()} output and appends those summaries to
#' \code{ZG_cycle} or \code{SC_cycle}.
#'
#' The climate input can be:
#' \itemize{
#' \item the output of \code{read.climate()}
#' \item a raw climate data frame
#' \item a valid climate file path readable by \code{read.climate()}
#' }
#'
#' Climate variables are summarized over each phase interval defined by the
#' \code{Start} and \code{End} columns of the phase-cycle table.
#'
#' @param x Object returned by \code{phase.zg()} or \code{phase.sc()}.
#' @param clim_df Climate input. This can be a standardized climate object
#' returned by \code{read.climate()}, a raw climate data frame, or a valid
#' climate file path.
#' @param mean_vars Character vector of climate variables to summarize by mean
#' within each phase window.
#' @param min_vars Character vector of climate variables to summarize by minimum
#' within each phase window.
#' @param max_vars Character vector of climate variables to summarize by maximum
#' within each phase window.
#' @param sum_vars Character vector of climate variables to summarize by sum
#' within each phase window.
#' @param median_vars Character vector of climate variables to summarize by median
#' within each phase window.
#' @param suffix Character suffix appended to the generated climate summary
#' columns. Default is \code{"_phase"}.
#'
#' @return
#' The same phase object with climate summaries appended to \code{ZG_cycle} or
#' \code{SC_cycle}. The returned object has class
#' \code{c("ZG_output_clim", "ZG_output", ...)} or
#' \code{c("SC_output_clim", "SC_output", ...)}.
#'
#' @examples
#' \donttest{
#' data(gf_nepa17)
#' data(ktm_clim_hourly)
#'
#' zg <- phase.zg(df = gf_nepa17[1:800, ], TreeNum = 1)
#'
#' zg_clim <- dm_join_phase_clim(
#' zg,
#' ktm_clim_hourly,
#' mean_vars = c("temp", "VPD", "RH"),
#' max_vars = c("temp", "VPD"),
#' sum_vars = c("prec")
#' )
#'
#' head(zg_clim$ZG_cycle)
#' }
#'
#' @importFrom dplyr bind_cols
#' @importFrom tibble as_tibble
#' @importFrom stats median
#' @export
dm_join_phase_clim <- function(x,
clim_df,
mean_vars = NULL,
min_vars = NULL,
max_vars = NULL,
sum_vars = NULL,
median_vars = NULL,
suffix = "_phase") {
if (!inherits(x, "ZG_output") && !inherits(x, "SC_output")) {
stop("'x' must be an object returned by phase.zg() or phase.sc().")
}
# standardize climate input
clim <- if (inherits(clim_df, "dm_clim")) {
tibble::as_tibble(clim_df)
} else if (is.data.frame(clim_df) || (is.character(clim_df) && length(clim_df) == 1 && file.exists(clim_df))) {
tibble::as_tibble(read.climate(clim_df, verbose = FALSE))
} else {
stop(
"'clim_df' must be one of: output of read.climate(), ",
"a raw climate data frame, or a valid climate file path."
)
}
if (!"TIME" %in% names(clim)) {
stop("Climate input must contain a 'TIME' column after standardization.")
}
clim$TIME <- as.POSIXct(clim$TIME)
numeric_vars <- setdiff(names(clim)[vapply(clim, is.numeric, logical(1))], "TIME")
if (all(c(
is.null(mean_vars), is.null(min_vars), is.null(max_vars),
is.null(sum_vars), is.null(median_vars)
))) {
mean_vars <- numeric_vars
}
check_vars <- function(vars, allowed, arg_name) {
if (is.null(vars)) return(character(0))
miss <- setdiff(vars, allowed)
if (length(miss) > 0) {
stop(sprintf(
"Unknown or non-numeric variable(s) in %s: %s",
arg_name, paste(miss, collapse = ", ")
))
}
vars
}
mean_vars <- check_vars(mean_vars, numeric_vars, "mean_vars")
min_vars <- check_vars(min_vars, numeric_vars, "min_vars")
max_vars <- check_vars(max_vars, numeric_vars, "max_vars")
sum_vars <- check_vars(sum_vars, numeric_vars, "sum_vars")
median_vars <- check_vars(median_vars, numeric_vars, "median_vars")
cycle_name <- if (inherits(x, "ZG_output")) "ZG_cycle" else "SC_cycle"
cyc <- tibble::as_tibble(x[[cycle_name]])
if (!all(c("Start", "End") %in% names(cyc))) {
stop(sprintf("'%s' must contain 'Start' and 'End' columns.", cycle_name))
}
cyc$Start <- as.POSIXct(cyc$Start)
cyc$End <- as.POSIXct(cyc$End)
safe_agg <- function(z, FUN) {
if (length(z) == 0 || all(is.na(z))) return(NA_real_)
FUN(z, na.rm = TRUE)
}
summarise_window <- function(start_time, end_time) {
idx <- clim$TIME >= start_time & clim$TIME <= end_time
sub <- clim[idx, , drop = FALSE]
out <- list(n_clim = sum(idx, na.rm = TRUE))
for (v in mean_vars) {
out[[paste0(v, "_mean", suffix)]] <- safe_agg(sub[[v]], mean)
}
for (v in min_vars) {
out[[paste0(v, "_min", suffix)]] <- safe_agg(sub[[v]], min)
}
for (v in max_vars) {
out[[paste0(v, "_max", suffix)]] <- safe_agg(sub[[v]], max)
}
for (v in sum_vars) {
out[[paste0(v, "_sum", suffix)]] <- safe_agg(sub[[v]], sum)
}
for (v in median_vars) {
out[[paste0(v, "_median", suffix)]] <- safe_agg(sub[[v]], stats::median)
}
tibble::as_tibble(out)
}
clim_stats <- dplyr::bind_rows(
lapply(seq_len(nrow(cyc)), function(i) {
summarise_window(cyc$Start[i], cyc$End[i])
})
)
out <- x
out[[cycle_name]] <- dplyr::bind_cols(cyc, clim_stats)
if (inherits(x, "ZG_output")) {
class(out) <- unique(c("ZG_output_clim", class(x)))
} else {
class(out) <- unique(c("SC_output_clim", class(x)))
}
out
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.