diff --git a/.Rbuildignore b/.Rbuildignore index 3929cda..abdd675 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -7,3 +7,4 @@ ^vignettes$ ^inst/font$ ^\.github$ +^\.covrignore$ diff --git a/.covrignore b/.covrignore new file mode 100644 index 0000000..3f20966 --- /dev/null +++ b/.covrignore @@ -0,0 +1,10 @@ +R/addin.R +R/cmd_func.R +R/getwd_clip.R +R/git.R +R/module.R +R/parallel_init.R +R/path_func.R +R/purrrprogress.R +R/set_font.R +R/tools_pdf.R diff --git a/DESCRIPTION b/DESCRIPTION index 5fa6c97..68f83e9 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -41,5 +41,5 @@ Suggests: grid, gridExtra VignetteBuilder: knitr -RoxygenNote: 7.2.2 +RoxygenNote: 7.2.3 Roxygen: list(markdown = TRUE) diff --git a/Ipaper.Rproj b/Ipaper.Rproj index b3d39a6..7db179e 100644 --- a/Ipaper.Rproj +++ b/Ipaper.Rproj @@ -6,12 +6,14 @@ AlwaysSaveHistory: Default EnableCodeIndexing: Yes UseSpacesForTab: Yes -NumSpacesForTab: 4 +NumSpacesForTab: 2 Encoding: UTF-8 RnwWeave: Sweave LaTeX: pdfLaTeX +AutoAppendNewline: Yes +StripTrailingWhitespace: Yes LineEndingConversion: Windows BuildType: Package diff --git a/R/arr_trend.R b/R/arr_trend.R index 088d036..f4380ba 100644 --- a/R/arr_trend.R +++ b/R/arr_trend.R @@ -24,52 +24,6 @@ slope_arr <- function(arr, FUN = rtrend::slope_mk, return.list = FALSE) { } } -# #' calculate MK slope of rast object -# #' -# #' @param r A rast object -# #' @param ... ignored -# #' -# #' @seealso [terra::rast()] -# #' @importFrom terra as.array plot rast ext -# #' @export -# slope_rast <- function(r, period = c(2001, 2020), -# fun = rtrend::slope_mk, -# outfile = NULL, overwrite = FALSE, ...) -# { -# if (is.character(r)) r = rast(r, ...) -# if (!is.null(period)) { -# # `r` should have time information -# year = year(time(r)) -# ind = which(year >= period[1] & year <= period[2]) -# arr = as.array(r[[ind]]) # 3d array -# } else { -# arr = as.array(r) # 3d array -# } -# t = slope_arr(arr, fun = fun, return.list = FALSE) # 3d array -# r_target = rast(r, nlyrs = 2) %>% -# set_names(c("slope", "pvalue")) -# # vals = t,)# vals = ans, -# values(r_target) = t # !note that `t` should be a 3d array -# # `vals`, and `values`, result is different -# if (!is.null(outfile)) { -# if (!file.exists(outfile) || overwrite) { -# # if (file.exists(outfile)) file.remove(outfile) -# writeRaster(r_target, outfile, overwrite = TRUE) -# } -# } -# r_target -# } - -# slope_nc <- function(file, varname = 0) { -# period = c("2001-01-01", "2020-12-31") -# arr <- ncread(file, varname, DatePeriod = period)$data[[1]] -# info = ncdim_get(file) - -# t = slope_arr(arr) -# r = get_grid.lonlat(info$lon, info$lat) -# r@data = t %>% list2df() -# r -# } # list2df <- function(x) { # lapply(x, as.numeric) %>% as.data.frame() diff --git a/R/group_apply.R b/R/group_apply.R index 5bff03e..ad709b6 100644 --- a/R/group_apply.R +++ b/R/group_apply.R @@ -47,7 +47,9 @@ upper_envelope <- function(x, y, interval = c(.50, .80, .90, .95), nchunk = 50) d_prob = data.table(I = seq_along(interval), interval = sprintf("%d%%", interval*100) %>% {factor(., rev(.))}, - lower = (1 - interval)/2, upper = 1 - (1 - interval)/2, mid = 0.5) + lower = (1 - interval)/2, + upper = 1 - (1 - interval)/2, + mid = 0.5) rows = (1:nrow(d_prob)) %>% set_names(., .) d = foreach(i = rows) %do% { diff --git a/R/label_tag.R b/R/label_tag.R index 603cb25..ade4082 100644 --- a/R/label_tag.R +++ b/R/label_tag.R @@ -9,10 +9,10 @@ #' @export label_tag <- function(labels, tag = TRUE, expression = TRUE, letter_begin = 1) { n <- length(labels) - tags = c(letters, LETTERS) + tags <- c(letters, LETTERS) if (expression) { sapply(seq_along(labels), function(i) { - name = labels[[i]] + name <- labels[[i]] data <- list(tag = tags[i + letter_begin - 1], x = name) if (tag) { eval(substitute(expression(bold("(" * tag * ")" ~ x)), data)) @@ -40,7 +40,7 @@ char2expr <- function(labels) { #' @param collapse an optional character string to separate the results. Not NA_character_. #' #' @export -char2script <- function(x, collapse = '"') { +char2script <- function(x, collapse = '"', verbose = TRUE) { if (is.list(x)) { x <- names(x) } @@ -52,7 +52,7 @@ char2script <- function(x, collapse = '"') { script <- paste(x, collapse = collapse) %>% paste0(head, ., tail) if (.Platform$OS.type == "windows") writeLines(script, "clipboard") - cat(script) + if (verbose) cat(script) else script } #' @export diff --git a/R/main_dplyr.R b/R/main_dplyr.R index b0478ed..00990ee 100644 --- a/R/main_dplyr.R +++ b/R/main_dplyr.R @@ -7,13 +7,14 @@ NULL #' @rdname dt_tools #' @export dt_round <- function(d, digits = 2) { - mutate(d, across(where(is.double), ~ round(.x, digits))) + mutate(d, across(where(is.double), ~ round(.x, digits))) } #' @rdname dt_tools #' @export -dt_chr2num <- function(d) { - mutate(d, across(where(is.character), ~ as.numeric(.x))) +dt_chr2num <- function(d, fun = as.numeric) { + # as.integer + mutate(d, across(where(is.character), ~ fun(.x))) } # https://stackoverflow.com/questions/54774280/plyrddply-equivalent-in-dplyr @@ -22,19 +23,25 @@ dt_chr2num <- function(d) { #' @importFrom data.table dcast #' @export dcast2 <- function(d, by, value.var = "value", ...) { - vars_left <- setdiff(colnames(d), c(by, value.var)) %>% paste(collapse = "+") - vars_right <- by %>% paste(collapse = "+") - formula <- as.formula(sprintf("%s~%s", vars_left, vars_right)) - dcast(d, formula, value.var = value.var, ...) + vars_left <- setdiff(colnames(d), c(by, value.var)) %>% paste(collapse = "+") + vars_right <- by %>% paste(collapse = "+") + formula <- as.formula(sprintf("%s~%s", vars_left, vars_right)) + dcast(d, formula, value.var = value.var, ...) } -#' @export +#' @export make_dt <- function(..., ncol = 3) { - x <- list(...) - n <- length(x) - nrow <- floor(n / ncol) - lapply(1:nrow, function(i) { - ind <- seq((i - 1) * ncol + 1, i * ncol) - x[ind] %>% as.data.table() - }) %>% do.call(rbind, .) + x <- list(...) + n <- length(x) + nrow <- floor(n / ncol) + lapply(1:nrow, function(i) { + ind <- seq((i - 1) * ncol + 1, i * ncol) + x[ind] %>% as.data.table() + }) %>% do.call(rbind, .) } + +# tribble( +# ~x, ~y, +# "a", 1:3, +# "b", 4:6 +# ) diff --git a/R/stat-quantile.R b/R/stat-quantile.R index 255c237..68b2f30 100644 --- a/R/stat-quantile.R +++ b/R/stat-quantile.R @@ -1,50 +1,47 @@ #' @export -stat_sd <- function(x, digit = 2, ...){ - x <- x[!is.na(x)] - y <- mean(x) - ymedian = median(x) - sd <- sd(x) - fmt <- sprintf("%%.%df±%%.%df", digit, digit) - label <- sprintf(fmt, ymedian, sd) - listk(y, ymin = y-sd, ymax = y+sd, ymedian, sd, label) +stat_sd <- function(x, digit = 2, ...) { + x <- x[!is.na(x)] + y <- mean(x) + ymedian <- median(x) + sd <- sd(x) + fmt <- sprintf("%%.%df±%%.%df", digit, digit) + label <- sprintf(fmt, ymedian, sd) + listk(y, ymin = y - sd, ymax = y + sd, ymedian, sd, label) } # 25% and 75% quantile #' @export -stat_quantile2 <- function(x, probs = c(0.25, 0.75), ...){ - # browser() - x <- x[!is.na(x)] - y <- median(x) - # sd <- sd(x) - r <- quantile(x, probs = probs, na.rm = TRUE) - listk(y = y, ymin = r[[1]], ymax = r[[2]]) +stat_quantile2 <- function(x, probs = c(0.25, 0.75), ...) { + x <- x[!is.na(x)] + y <- median(x) + # sd <- sd(x) + r <- quantile(x, probs = probs, na.rm = TRUE) + listk(y = y, ymin = r[[1]], ymax = r[[2]]) } #' @export -box_qtl <- function(x){ - x <- stats::na.omit(x) - quantile(x, c(0.1, 0.9)) %>% set_names(c("ymin", "ymax")) +box_qtl <- function(x, probs = c(0.1, 0.9), ...) { + x <- stats::na.omit(x) + quantile(x, probs) %>% set_names(c("ymin", "ymax")) } -geom_quantile2 <- function(){ - # browser() - p_attribute <- ggplot(d[variable != "EOS"], aes(variable, perc, fill = variable)) + - stat_boxplot(geom ='errorbar', width = 0.5) + - geom_boxplot2(outlier.size = -1) + - stat_summary(fun.data = FUN_lab, colour = "black", size = fontsize_statistic, geom = "text", vjust = -0.5) + - labs(y = "Relative contribution to EOS change (%)", x = NULL) -} +# geom_quantile2 <- function() { +# ggplot(d[variable != "EOS"], aes(variable, perc, fill = variable)) + +# stat_boxplot(geom = "errorbar", width = 0.5) + +# geom_boxplot2(outlier.size = -1) + +# stat_summary(fun.data = FUN_lab, colour = "black", size = fontsize_statistic, geom = "text", vjust = -0.5) + +# labs(y = "Relative contribution to EOS change (%)", x = NULL) +# } -# stat_summary(fun.data = stat_quantile, colour = "black", size = 1, geom = "errorbar") +# stat_summary(fun.data = stat_quantile2, colour = "black", size = 1, geom = "errorbar") -#' quantile_envelope #' @export -quantile_envelope <- function(x, alpha){ - names <- "ymean" - # if (alpha != 0.5) { - alpha <- c(alpha, 1-alpha) - names <- c("ymin", "ymax") - # } - res <- quantile(x, alpha, na.rm = T) - set_names(res, names) +quantile_envelope <- function(x, alpha = 0.25, ...) { + names <- "ymean" + # if (alpha != 0.5) { + alpha <- c(alpha, 1 - alpha) + names <- c("ymin", "ymax") + # } + res <- quantile(x, alpha, na.rm = T) + set_names(res, names) } diff --git a/R/tools.R b/R/tools.R index 9bd47fc..10f1521 100644 --- a/R/tools.R +++ b/R/tools.R @@ -22,7 +22,7 @@ fprintf <- function(fmt, ...) cat(sprintf(fmt, ...)) #' @export runningId <- function(i, step = 1, N, prefix = "") { perc <- ifelse(missing(N), "", sprintf(", %.1f%% ", i / N * 100)) - if (mod(i, step) == 0) cat(sprintf("[%s] running%s %d ...\n", prefix, perc, i)) + if (mod(i, step) == 0) fprintf("[%s] running%s %d ...\n", prefix, perc, i) } #' @export @@ -74,8 +74,8 @@ unique_length <- function(x) { } #' @export -unique_sort <- function(x) { - unique(x) %>% sort() +unique_sort <- function(x, ..., na.last = TRUE) { + unique(x) %>% sort(..., na.last = na.last) } #' @export diff --git a/man/char2script.Rd b/man/char2script.Rd index 0c106da..29bc03b 100644 --- a/man/char2script.Rd +++ b/man/char2script.Rd @@ -5,9 +5,9 @@ \alias{code_ChrVec} \title{generate R script of character vector} \usage{ -char2script(x, collapse = "\\"") +char2script(x, collapse = "\\"", verbose = TRUE) -code_ChrVec(x, collapse = "\\"") +code_ChrVec(x, collapse = "\\"", verbose = TRUE) } \arguments{ \item{x}{character vector, data.frame or list.} diff --git a/man/dt_ddply.Rd b/man/dt_ddply.Rd index 9310ecd..ef30071 100644 --- a/man/dt_ddply.Rd +++ b/man/dt_ddply.Rd @@ -41,25 +41,18 @@ dt_dlply( \item{.variables}{variables to split data frame by, as \code{\link[plyr]{as.quoted}} variables, a formula or character vector} -\item{.f}{A function, formula, or vector (not necessarily atomic). - -If a \strong{function}, it is used as is. - -If a \strong{formula}, e.g. \code{~ .x + 2}, it is converted to a function. There -are three ways to refer to the arguments: +\item{.f}{A function, specified in one of the following ways: \itemize{ -\item For a single argument function, use \code{.} -\item For a two argument function, use \code{.x} and \code{.y} -\item For more arguments, use \code{..1}, \code{..2}, \code{..3} etc -} - -This syntax allows you to create very compact anonymous functions. - -If \strong{character vector}, \strong{numeric vector}, or \strong{list}, it is -converted to an extractor function. Character vectors index by -name and numeric vectors index by position; use a list to index -by position and name at different levels. If a component is not -present, the value of \code{.default} will be returned.} +\item A named function, e.g. \code{mean}. +\item An anonymous function, e.g. \verb{\\(x) x + 1} or \code{function(x) x + 1}. +\item A formula, e.g. \code{~ .x + 1}. You must use \code{.x} to refer to the first +argument. Only recommended if you require backward compatibility with +older versions of R. +\item A string, integer, or list, e.g. \code{"idx"}, \code{1}, or \code{list("idx", 1)} which +are shorthand for \verb{\\(x) pluck(x, "idx")}, \verb{\\(x) pluck(x, 1)}, and +\verb{\\(x) pluck(x, "idx", 1)} respectively. Optionally supply \code{.default} to +set a default value if the indexed element is \code{NULL} or does not exist. +}} \item{...}{other arguments passed on to \code{.fun}} @@ -71,13 +64,6 @@ input data be preserved (FALSE) or dropped (TRUE, default)} \item{.parallel}{if \code{TRUE}, apply function in parallel, using parallel backend provided by foreach} - -\item{.id}{Either a string or \code{NULL}. If a string, the output will contain -a variable with that name, storing either the name (if \code{.x} is named) or -the index (if \code{.x} is unnamed) of the input. If \code{NULL}, the default, no -variable will be created. - -Only applies to \verb{_dfr} variant.} } \description{ For each subset of a \code{data.table}, apply function then combine results into a \code{data.table}. diff --git a/man/dt_tools.Rd b/man/dt_tools.Rd index f36fda2..dd23185 100644 --- a/man/dt_tools.Rd +++ b/man/dt_tools.Rd @@ -8,7 +8,7 @@ \usage{ dt_round(d, digits = 2) -dt_chr2num(d) +dt_chr2num(d, fun = as.numeric) } \description{ data.frame manipulating function by \code{dplyr::across} diff --git a/man/pro_map.Rd b/man/pro_map.Rd index 008235c..dba9b37 100644 --- a/man/pro_map.Rd +++ b/man/pro_map.Rd @@ -4,32 +4,41 @@ \alias{pro_map} \title{Modified purrr functions with progress bar} \usage{ -pro_map(.x, .f, ...) +pro_map(.x, .f, ..., .progress = FALSE) } \arguments{ \item{.x}{A list or atomic vector.} -\item{.f}{A function, formula, or vector (not necessarily atomic). +\item{.f}{A function, specified in one of the following ways: +\itemize{ +\item A named function, e.g. \code{mean}. +\item An anonymous function, e.g. \verb{\\(x) x + 1} or \code{function(x) x + 1}. +\item A formula, e.g. \code{~ .x + 1}. You must use \code{.x} to refer to the first +argument. Only recommended if you require backward compatibility with +older versions of R. +\item A string, integer, or list, e.g. \code{"idx"}, \code{1}, or \code{list("idx", 1)} which +are shorthand for \verb{\\(x) pluck(x, "idx")}, \verb{\\(x) pluck(x, 1)}, and +\verb{\\(x) pluck(x, "idx", 1)} respectively. Optionally supply \code{.default} to +set a default value if the indexed element is \code{NULL} or does not exist. +}} -If a \strong{function}, it is used as is. +\item{...}{Additional arguments passed on to the mapped function. -If a \strong{formula}, e.g. \code{~ .x + 2}, it is converted to a function. There -are three ways to refer to the arguments: -\itemize{ -\item For a single argument function, use \code{.} -\item For a two argument function, use \code{.x} and \code{.y} -\item For more arguments, use \code{..1}, \code{..2}, \code{..3} etc -} +We now generally recommend against using \code{...} to pass additional +(constant) arguments to \code{.f}. Instead use a shorthand anonymous function: -This syntax allows you to create very compact anonymous functions. +\if{html}{\out{
}}\preformatted{# Instead of +x |> map(f, 1, 2, collapse = ",") +# do: +x |> map(\\(x) f(x, 1, 2, collapse = ",")) +}\if{html}{\out{
}} -If \strong{character vector}, \strong{numeric vector}, or \strong{list}, it is -converted to an extractor function. Character vectors index by -name and numeric vectors index by position; use a list to index -by position and name at different levels. If a component is not -present, the value of \code{.default} will be returned.} +This makes it easier to understand which arguments belong to which +function and will tend to yield better error messages.} -\item{...}{Additional arguments passed on to the mapped function.} +\item{.progress}{Whether to show a progress bar. Use \code{TRUE} to a turn on +a basic progress bar, use a string to give it a name, or see +\link[purrr]{progress_bars} for more details.} } \description{ Modified purrr functions with progress bar diff --git a/man/quantile_envelope.Rd b/man/quantile_envelope.Rd deleted file mode 100644 index c581b26..0000000 --- a/man/quantile_envelope.Rd +++ /dev/null @@ -1,11 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/stat-quantile.R -\name{quantile_envelope} -\alias{quantile_envelope} -\title{quantile_envelope} -\usage{ -quantile_envelope(x, alpha) -} -\description{ -quantile_envelope -} diff --git a/tests/testthat/test-dplyr.R b/tests/testthat/test-dplyr.R new file mode 100644 index 0000000..6b31666 --- /dev/null +++ b/tests/testthat/test-dplyr.R @@ -0,0 +1,15 @@ +test_that("main_dplyr works", { + d1 = make_dt( + pi, 2, 3, "5", + 4, 5, 6, "7", ncol = 4 + ) + d2 = tribble( + ~V1, ~V2, ~V3, ~V4, + pi, 2, 3, "5", + 4, 5, 6, "7" + ) %>% as.data.table() + + expect_equal(d1, d2) + expect_equal(dt_round(d1)$V1[1], 3.14) + expect_equal(dt_chr2num(d1)$V4, c(5, 7)) +}) diff --git a/tests/testthat/test-group_apply.R b/tests/testthat/test-group_apply.R index 34ea8e0..ded88e6 100644 --- a/tests/testthat/test-group_apply.R +++ b/tests/testthat/test-group_apply.R @@ -1,17 +1,21 @@ +library(ggplot2) + test_that("upper_envelope works", { - y = rnorm(1000) - x = seq_along(y) - - - # expect_true({ - # r = group_upperEnvelope(x, y, 20) - # plot(x, y) - # lines(y~x, r, col = "red") - # is.data.table(r) && nrow(r) == 49 - # }) - - expect_true({ - r2 = group_apply(x, y, nchunk = 20) - is.data.table(r2) && nrow(r2) == 20 - }) + y = rnorm(1000) + x = seq_along(y) + + expect_true({ + r = upper_envelope(x, y, nchunk = 20) + + ggplot(r, aes(x, mid)) + + geom_ribbon(aes(ymin = lower, ymax = upper, fill = interval), alpha = 0.5) + + geom_line(linewidth = 1.5, color = "blue") + + is.data.table(r) && nrow(r) == 80 + }) + + expect_true({ + r2 = group_apply(x, y, nchunk = 20) + is.data.table(r2) && nrow(r2) == 20 + }) }) diff --git a/tests/testthat/test-label_tag.R b/tests/testthat/test-label_tag.R new file mode 100644 index 0000000..ea54598 --- /dev/null +++ b/tests/testthat/test-label_tag.R @@ -0,0 +1,14 @@ +test_that("label_tag works", { + expect_equal( + label_tag(1:5), + expression( + bold("(" * "a" * ")" ~ 1L), bold("(" * "b" * ")" ~ + 2L), bold("(" * "c" * ")" ~ 3L), bold("(" * "d" * ")" ~ 4L), + bold("(" * "e" * ")" ~ 5L) + ) + ) + + expect_equal( + char2script(1:5, verbose = FALSE), + "c(\"1\", \"2\", \"3\", \"4\", \"5\")") +}) diff --git a/tests/testthat/test-stat_quantile2.R b/tests/testthat/test-stat_quantile2.R new file mode 100644 index 0000000..65be0a8 --- /dev/null +++ b/tests/testthat/test-stat_quantile2.R @@ -0,0 +1,17 @@ +test_that("stat_quantile2 works", { + + x = 1:10 + r_sd = stat_sd(x) + expect_equal(names(r_sd), c("y", "ymin", "ymax", "ymedian", "sd", "label")) + + probs = c(.25, .75) + expect_equal( + unlist(stat_quantile2(x, probs))[-1], + box_qtl(x, probs) + ) + + expect_equal( + box_qtl(x, probs), + quantile_envelope(x, probs[1]) + ) +}) diff --git a/tests/testthat/test-tools.R b/tests/testthat/test-tools.R new file mode 100644 index 0000000..fe2ba89 --- /dev/null +++ b/tests/testthat/test-tools.R @@ -0,0 +1,25 @@ +test_that("tools works", { + # cut_levels + x <- c(-0.09, -0.4, 0.04, 0.15) + lev <- cut_plevels(x, verbose = TRUE) + expect_equal(length(levels(lev)), 8) + + # runningId + expect_no_error({ + for (i in 1:10) runningId(i, prefix = "Ipaper", 3) + }) + + # print2 + expect_no_error({print2(x, lev)}) + + # match2 + info = match2(x = c(1, 2, 4), y = c(1, 3, 2, 4)) + expect_equal(info$I_x, c(1L, 2L, 3L)) + expect_equal(info$I_y, c(1L, 3L, 4L)) + + # unique_sort + expect_equal(unique_sort(c(2, 2, 3, NA)), c(2, 3, NA)) + + # unique_length + expect_equal(unique_length(c(2, 2, 3, NA)), 3) +})