diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 1e93f191..6e7598fe 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -1,85 +1,59 @@ -# For help debugging build failures open an issue on the RStudio community with the 'github-actions' tag. -# https://community.rstudio.com/new-topic?category=Package%20development&tags=github-actions +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: - push: - branches: - - main - - master - - devel - pull_request: - branches: - - main - - master - - devel + push: + branches: + - main + - master + - devel + pull_request: + branches: + - main + - master + - devel -name: R-CMD-check +name: R-CMD-check.yaml -jobs: - R-CMD-check: - runs-on: ${{ matrix.config.os }} - - name: ${{ matrix.config.os }} (${{ matrix.config.r }}) - - strategy: - fail-fast: false - matrix: - config: - - {os: windows-latest, r: 'release'} - - {os: macOS-latest, r: 'release'} - - {os: ubuntu-20.04, r: 'release', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest"} - - {os: ubuntu-20.04, r: 'devel', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest"} - - env: - R_REMOTES_NO_ERRORS_FROM_WARNINGS: true - RSPM: ${{ matrix.config.rspm }} - - steps: - - uses: actions/checkout@v2 - - - uses: r-lib/actions/setup-r@v2 - with: - r-version: ${{ matrix.config.r }} - - - uses: r-lib/actions/setup-pandoc@v2 +permissions: read-all - - name: Query dependencies - run: | - install.packages('remotes') - saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) - writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") - shell: Rscript {0} - - - name: Cache R packages - if: runner.os != 'Windows' - uses: actions/cache@v2 - with: - path: ${{ env.R_LIBS_USER }} - key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }} - restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1- - - - name: Install system dependencies - if: runner.os == 'Linux' - run: | - while read -r cmd - do - eval sudo $cmd - done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') - - - name: Install dependencies - run: | - remotes::install_deps(dependencies = TRUE) - remotes::install_cran("rcmdcheck") - shell: Rscript {0} - - - name: Check - env: - _R_CHECK_CRAN_INCOMING_REMOTE_: false - run: rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "check") - shell: Rscript {0} - - - name: Upload check results - if: failure() - uses: actions/upload-artifact@main - with: - name: ${{ runner.os }}-r${{ matrix.config.r }}-results - path: check +jobs: + R-CMD-check: + runs-on: ${{ matrix.config.os }} + + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + + strategy: + fail-fast: false + matrix: + config: + - {os: windows-latest, r: 'release'} + - {os: macOS-latest, r: 'release'} + - {os: ubuntu-22.04, r: 'release', rspm: "https://packagemanager.posit.co/cran/__linux__/jammy/latest"} + - {os: ubuntu-22.04, r: 'devel', rspm: "https://packagemanager.posit.co/cran/__linux__/jammy/latest"} + - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} + - {os: ubuntu-latest, r: 'release'} + + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes + + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::rcmdcheck + needs: check + + - uses: r-lib/actions/check-r-package@v2 + with: + upload-snapshots: true + build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' \ No newline at end of file diff --git a/.github/workflows/rhub.yaml b/.github/workflows/rhub.yaml new file mode 100644 index 00000000..bdfab195 --- /dev/null +++ b/.github/workflows/rhub.yaml @@ -0,0 +1,95 @@ +# R-hub's generic GitHub Actions workflow file. It's canonical location is at +# https://github.com/r-hub/actions/blob/v1/workflows/rhub.yaml +# You can update this file to a newer version using the rhub2 package: +# +# rhub::rhub_setup() +# +# It is unlikely that you need to modify this file manually. + +name: R-hub +run-name: "${{ github.event.inputs.id }}: ${{ github.event.inputs.name || format('Manually run by {0}', github.triggering_actor) }}" + +on: + workflow_dispatch: + inputs: + config: + description: 'A comma separated list of R-hub platforms to use.' + type: string + default: 'linux,windows,macos' + name: + description: 'Run name. You can leave this empty now.' + type: string + id: + description: 'Unique ID. You can leave this empty now.' + type: string + +jobs: + + setup: + runs-on: ubuntu-latest + outputs: + containers: ${{ steps.rhub-setup.outputs.containers }} + platforms: ${{ steps.rhub-setup.outputs.platforms }} + + steps: + # NO NEED TO CHECKOUT HERE + - uses: r-hub/actions/setup@v1 + with: + config: ${{ github.event.inputs.config }} + id: rhub-setup + + linux-containers: + needs: setup + if: ${{ needs.setup.outputs.containers != '[]' }} + runs-on: ubuntu-latest + name: ${{ matrix.config.label }} + strategy: + fail-fast: false + matrix: + config: ${{ fromJson(needs.setup.outputs.containers) }} + container: + image: ${{ matrix.config.container }} + + steps: + - uses: r-hub/actions/checkout@v1 + - uses: r-hub/actions/platform-info@v1 + with: + token: ${{ secrets.RHUB_TOKEN }} + job-config: ${{ matrix.config.job-config }} + - uses: r-hub/actions/setup-deps@v1 + with: + token: ${{ secrets.RHUB_TOKEN }} + job-config: ${{ matrix.config.job-config }} + - uses: r-hub/actions/run-check@v1 + with: + token: ${{ secrets.RHUB_TOKEN }} + job-config: ${{ matrix.config.job-config }} + + other-platforms: + needs: setup + if: ${{ needs.setup.outputs.platforms != '[]' }} + runs-on: ${{ matrix.config.os }} + name: ${{ matrix.config.label }} + strategy: + fail-fast: false + matrix: + config: ${{ fromJson(needs.setup.outputs.platforms) }} + + steps: + - uses: r-hub/actions/checkout@v1 + - uses: r-hub/actions/setup-r@v1 + with: + job-config: ${{ matrix.config.job-config }} + token: ${{ secrets.RHUB_TOKEN }} + - uses: r-hub/actions/platform-info@v1 + with: + token: ${{ secrets.RHUB_TOKEN }} + job-config: ${{ matrix.config.job-config }} + - uses: r-hub/actions/setup-deps@v1 + with: + job-config: ${{ matrix.config.job-config }} + token: ${{ secrets.RHUB_TOKEN }} + - uses: r-hub/actions/run-check@v1 + with: + job-config: ${{ matrix.config.job-config }} + token: ${{ secrets.RHUB_TOKEN }} \ No newline at end of file diff --git a/DESCRIPTION b/DESCRIPTION index fec79b17..4c636c24 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -71,7 +71,7 @@ Suggests: pharmaRTF, withr VignetteBuilder: knitr -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.2 RdMacros: lifecycle Config/testthat/edition: 3 LazyData: true diff --git a/R/regex.R b/R/regex.R index d259212f..786cca11 100644 --- a/R/regex.R +++ b/R/regex.R @@ -3,7 +3,7 @@ #' This function allows you to extract important regular expressions used inside #' Tplyr. #' -#' There are two important regular expressions used within Tplyr. The +#' There are three important regular expressions used within Tplyr. The #' format_string expression is the expression to parse format strings. This is #' what is used to make sense out of strings like 'xx (XX.x%)' or 'a+1 (A.a+2)' #' by inferring what the user is specifying about number formatting. @@ -11,7 +11,8 @@ #' The 'format_group' regex is the opposite of this, and when given a string of #' numbers, such as ' 5 (34%) \[9]' will return the separate segments of numbers #' broken into their format groups, which in this example would be ' 5', -#' '(34%)', and '\[9]'. +#' '(34%)', and '\[9]'. Lastly, the 'number_group' regex has a similar application +#' to the 'format_group' regex, but targets only numbers #' #' @param rx A character string with either the value 'format_string' or #' 'format_group' @@ -25,14 +26,17 @@ #' get_tplyr_regex('format_string') #' #' get_tplyr_regex('format_group') +#' +#' get_tplyr_regex('number_group') #' -get_tplyr_regex <- function(rx=c("format_string", "format_group")) { +get_tplyr_regex <- function(rx=c("format_string", "format_group", "number_group")) { rx <- match.arg(rx) switch( rx, 'format_string' = get_format_string_regex(), - 'format_group' = get_format_group_regex() + 'format_group' = get_format_group_regex(), + 'number_group' = get_numeric_group_regex() ) } @@ -110,3 +114,17 @@ get_format_group_regex <- function() { regex(paste0(nwsd, ws, num, nws)) } + +#' Return the regex for identifying numbers within an output string +#' +#' This regex targets the individual numbers within the string +#' +#' @return A regular expression +#' @noRd +get_numeric_group_regex <- function() { + #`-?` - Matches an optional negative sign + # `(?:\d*\.\d+|\d+)` - A non-capturing group with two alternatives: + # `\d*\.\d+` - Matches decimals like `.75`, `0.56`, or `123.45` + # `\d+` - Matches integers like `1`, `523`, `56` + regex("-?(?:\\d*\\.\\d+|\\d+)") +} diff --git a/R/riskdiff.R b/R/riskdiff.R index be17c0b8..a6af81fb 100644 --- a/R/riskdiff.R +++ b/R/riskdiff.R @@ -176,7 +176,21 @@ prep_two_way <- function(comp) { msg = paste0("There are no records for the following groups within the variable ", as_name(treat_var), ": ", paste(invalid_groups, collapse=", "))) - two_way <- numeric_data + # create the merge columns + mrg <- as_label(pop_treat_var) + names(mrg) <- as_label(treat_var) + mrg_cols <- append(mrg, map_chr(cols, as_label)) + + two_way <- numeric_data %>% + left_join( + select(header_n, everything(), tot_fill = n), + by = mrg_cols + ) %>% + mutate( + distinct_total = if_else(is.na(distinct_total), tot_fill, distinct_total) + ) + + rm(mrg, mrg_cols) # Nested layers need to plug the NAs left over - needs revision in the future if (is_built_nest && quo_is_symbol(by[[1]])) { @@ -188,7 +202,6 @@ prep_two_way <- function(comp) { ) } - # If distinct is set and distinct values are there, use them if (comp_distinct && !is.null(distinct_by)) { two_way <- two_way %>% diff --git a/R/str_extractors.R b/R/str_extractors.R index cdb73535..f3a9b46e 100644 --- a/R/str_extractors.R +++ b/R/str_extractors.R @@ -22,7 +22,7 @@ #' #' @examples #' -#' string <- c(" 0 (0.0%)", " 8 (9.3%)", "78 (90.7%)") +#' string <- c(" 0 (0.0%)", " 8 (9.3%)", "78 (90.7%)", "-1 (-.56, .75) -523%, 56 | -34") #' #' str_extract_fmt_group(string, 2) #' @@ -31,11 +31,11 @@ str_extract_fmt_group <- function(string, format_group) { if (!inherits(string, "character")) { - stop("Paramter `string` must be a character vector", call.=FALSE) + stop("Parameter `string` must be a character vector", call.=FALSE) } - if (!inherits(format_group, "numeric") || (inherits(format_group, "numeric") && format_group %% 1 != 0)) { - stop("Paramter `format_group` must be an integer", call.=FALSE) + if (!inherits(format_group, c("integer", "numeric")) || (inherits(format_group, "numeric") && format_group %% 1 != 0)) { + stop("Parameter `format_group` must be an integer", call.=FALSE) } # Pull out regex to drive the work @@ -57,15 +57,15 @@ str_extract_fmt_group <- function(string, format_group) { str_extract_num <- function(string, format_group) { if (!inherits(string, "character")) { - stop("Paramter `string` must be a character vector", call.=FALSE) + stop("Parameter `string` must be a character vector", call.=FALSE) } - if (!inherits(format_group, "numeric") || (inherits(format_group, "numeric") && format_group %% 1 != 0)) { - stop("Paramter `format_group` must be an integer", call.=FALSE) + if (!inherits(format_group, c("integer", "numeric")) || (inherits(format_group, "numeric") && format_group %% 1 != 0)) { + stop("Parameter `format_group` must be an integer", call.=FALSE) } # Pull out regex to drive the work - f_grp_rx <- get_format_group_regex() + f_grp_rx <- get_numeric_group_regex() # Pull out all the match groups and then get the numeric for the conditional number match_groups <- str_match_all(string, f_grp_rx) @@ -73,6 +73,6 @@ str_extract_num <- function(string, format_group) { # Get the number upon which the condition will be evaluated map_dbl( match_groups, - ~ if (nrow(.) < format_group) {NA_real_} else {as.double(.[format_group, 2])} + ~ if (nrow(.) < format_group) {NA_real_} else {as.double(.[format_group, 1])} ) } diff --git a/man/Tplyr.Rd b/man/Tplyr.Rd index 06706f33..330856e3 100644 --- a/man/Tplyr.Rd +++ b/man/Tplyr.Rd @@ -2,8 +2,8 @@ % Please edit documentation in R/zzz.R \docType{package} \name{Tplyr} -\alias{Tplyr} \alias{Tplyr-package} +\alias{Tplyr} \title{A grammar of summary data for clinical reports} \description{ `r lifecycle::badge("experimental")` diff --git a/man/get_tplyr_regex.Rd b/man/get_tplyr_regex.Rd index 448057a1..eca594b7 100644 --- a/man/get_tplyr_regex.Rd +++ b/man/get_tplyr_regex.Rd @@ -4,7 +4,7 @@ \alias{get_tplyr_regex} \title{Retrieve one of Tplyr's regular expressions} \usage{ -get_tplyr_regex(rx = c("format_string", "format_group")) +get_tplyr_regex(rx = c("format_string", "format_group", "number_group")) } \arguments{ \item{rx}{A character string with either the value 'format_string' or @@ -18,7 +18,7 @@ This function allows you to extract important regular expressions used inside Tplyr. } \details{ -There are two important regular expressions used within Tplyr. The +There are three important regular expressions used within Tplyr. The format_string expression is the expression to parse format strings. This is what is used to make sense out of strings like 'xx (XX.x\%)' or 'a+1 (A.a+2)' by inferring what the user is specifying about number formatting. @@ -26,7 +26,8 @@ by inferring what the user is specifying about number formatting. The 'format_group' regex is the opposite of this, and when given a string of numbers, such as ' 5 (34\%) [9]' will return the separate segments of numbers broken into their format groups, which in this example would be ' 5', -'(34\%)', and '[9]'. +'(34\%)', and '[9]'. Lastly, the 'number_group' regex has a similar application +to the 'format_group' regex, but targets only numbers } \examples{ @@ -34,4 +35,6 @@ get_tplyr_regex('format_string') get_tplyr_regex('format_group') +get_tplyr_regex('number_group') + } diff --git a/man/str_extractors.Rd b/man/str_extractors.Rd index b8fe8b9b..be09ac4d 100644 --- a/man/str_extractors.Rd +++ b/man/str_extractors.Rd @@ -31,7 +31,7 @@ are ' 5', '(34.4\%)', and '[9]'. } \examples{ -string <- c(" 0 (0.0\%)", " 8 (9.3\%)", "78 (90.7\%)") +string <- c(" 0 (0.0\%)", " 8 (9.3\%)", "78 (90.7\%)", "-1 (-.56, .75) -523\%, 56 | -34") str_extract_fmt_group(string, 2) diff --git a/tests/testthat/_snaps/riskdiff.md b/tests/testthat/_snaps/riskdiff.md index 7a376ad0..b39e111e 100644 --- a/tests/testthat/_snaps/riskdiff.md +++ b/tests/testthat/_snaps/riskdiff.md @@ -22,3 +22,122 @@ Comparison {4, 4} has duplicated values. Comparisons must not be duplicates +# Missing counts don't cause error in comparisons + + Code + head(as.data.frame(build(t))) + Condition + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Warning in `prop.test()`: + Chi-squared approximation may be incorrect + Output + row_label1 row_label2 + 1 SKIN AND SUBCUTANEOUS TISSUE DISORDERS SKIN AND SUBCUTANEOUS TISSUE DISORDERS + 2 SKIN AND SUBCUTANEOUS TISSUE DISORDERS ALOPECIA + 3 SKIN AND SUBCUTANEOUS TISSUE DISORDERS BLISTER + 4 SKIN AND SUBCUTANEOUS TISSUE DISORDERS COLD SWEAT + 5 SKIN AND SUBCUTANEOUS TISSUE DISORDERS DERMATITIS ATOPIC + 6 SKIN AND SUBCUTANEOUS TISSUE DISORDERS DERMATITIS CONTACT + var1_Placebo_F var1_Placebo_M var1_Xanomeline High Dose_F + 1 13 ( 24.5%) 8 ( 24.2%) 0 ( 0.0%) + 2 1 ( 1.9%) 0 ( 0.0%) 0 ( 0.0%) + 3 0 ( 0.0%) 0 ( 0.0%) 0 ( 0.0%) + 4 0 ( 0.0%) 1 ( 3.0%) 0 ( 0.0%) + 5 0 ( 0.0%) 1 ( 3.0%) 0 ( 0.0%) + 6 0 ( 0.0%) 0 ( 0.0%) 0 ( 0.0%) + var1_Xanomeline High Dose_M var1_Xanomeline Low Dose_F + 1 0 ( 0.0%) 24 ( 48.0%) + 2 0 ( 0.0%) 0 ( 0.0%) + 3 0 ( 0.0%) 2 ( 4.0%) + 4 0 ( 0.0%) 0 ( 0.0%) + 5 0 ( 0.0%) 0 ( 0.0%) + 6 0 ( 0.0%) 0 ( 0.0%) + var1_Xanomeline Low Dose_M ord_layer_index + 1 18 ( 52.9%) 1 + 2 0 ( 0.0%) 1 + 3 3 ( 8.8%) 1 + 4 0 ( 0.0%) 1 + 5 0 ( 0.0%) 1 + 6 1 ( 2.9%) 1 + rdiff_Xanomeline High Dose_Placebo_F rdiff_Xanomeline High Dose_Placebo_M + 1 -0.245 (-0.383, -0.108) -0.242 (-0.415, -0.070) + 2 -0.019 (-0.074, 0.037) 0.000 ( 0.000, 0.000) + 3 0.000 ( 0.000, 0.000) 0.000 ( 0.000, 0.000) + 4 0.000 ( 0.000, 0.000) -0.030 (-0.115, 0.055) + 5 0.000 ( 0.000, 0.000) -0.030 (-0.115, 0.055) + 6 0.000 ( 0.000, 0.000) 0.000 ( 0.000, 0.000) + ord_layer_1 ord_layer_2 + 1 1 Inf + 2 1 1 + 3 1 2 + 4 1 3 + 5 1 4 + 6 1 5 + diff --git a/tests/testthat/test-riskdiff.R b/tests/testthat/test-riskdiff.R index 644ffec0..5735edbe 100644 --- a/tests/testthat/test-riskdiff.R +++ b/tests/testthat/test-riskdiff.R @@ -278,3 +278,26 @@ test_that("Error generates when duplicating riskdiff comparison values", { ) }) + +test_that("Missing counts don't cause error in comparisons", { + + +adae <- filter(tplyr_adae, TRTA != "Xanomeline High Dose" & AEDECOD != "ACTINIC KERATOSIS") + +# Create table +t <- tplyr_table(adae, TRTA, cols=SEX) %>% + # Set population + set_pop_data(tplyr_adsl) %>% + set_pop_treat_var(TRT01A) %>% + # Layer 1: Organ System and OCMQ (Narrow) Count Layer + add_layer( + group_count(vars(AEBODSYS, AEDECOD)) %>% + # Set distinct counts per subject + set_distinct_by(USUBJID) %>% + # Add risk differences + add_risk_diff(c("Xanomeline High Dose", "Placebo")) + ) + + # Build the table + expect_snapshot(head(as.data.frame(build(t)))) +}) \ No newline at end of file diff --git a/tests/testthat/test-str_extractors.R b/tests/testthat/test-str_extractors.R index 393a2d67..207f968f 100644 --- a/tests/testthat/test-str_extractors.R +++ b/tests/testthat/test-str_extractors.R @@ -1,57 +1,63 @@ -string <- c(" 0 (0.0%)", " 8 (9.3%)", "78 (90.7%)") +string <- c(" 0 (0.0%)", " 8 (9.3%)", "78 (90.7%)", "-1 (-0.56, -.75) -523%") test_that("String extractor errors generate properly", { expect_error( str_extract_fmt_group(c(1), 1), - "Paramter `string`" + "Parameter `string`" ) expect_error( str_extract_fmt_group(string, "hi"), - "Paramter `format_group`" + "Parameter `format_group`" ) expect_error( str_extract_num(c(1), 1), - "Paramter `string`" + "Parameter `string`" ) expect_error( str_extract_num(string, "hi"), - "Paramter `format_group`" + "Parameter `format_group`" ) }) test_that("Format groups can be extracted", { expect_equal( str_extract_fmt_group(string, 1), - c(' 0', ' 8', '78') + c(' 0', ' 8', '78', "-1") ) expect_equal( str_extract_fmt_group(string, 2), - c("(0.0%)", "(9.3%)", "(90.7%)") + c("(0.0%)", "(9.3%)", "(90.7%)", "(-0.56,") ) expect_equal( str_extract_fmt_group(string, 3), - rep(NA_character_, 3) + c(rep(NA_character_, 3), "-.75)") ) }) test_that("Numbers from format groups can be extracted", { expect_equal( str_extract_num(string, 1), - c(0, 8, 78) + c(0, 8, 78, -1) ) expect_equal( str_extract_num(string, 2), - c(0.0, 9.3, 90.7) + c(0.0, 9.3, 90.7, -.56) ) expect_equal( str_extract_num(string, 3), - rep(NA_real_, 3) + c(rep(NA_real_, 3), -.75) ) + + expect_equal( + str_extract_num(string, 4), + c(rep(NA_real_, 3), -523) + ) + })