diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 14159b7..d46a617 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -6,7 +6,9 @@ on: pull_request: branches: [main, master] -name: R-CMD-check +name: R-CMD-check.yaml + +permissions: read-all jobs: R-CMD-check: diff --git a/.github/workflows/R-CMD-historic-R-check.yaml b/.github/workflows/R-CMD-historic-R-check.yaml deleted file mode 100644 index 58be7da..0000000 --- a/.github/workflows/R-CMD-historic-R-check.yaml +++ /dev/null @@ -1,46 +0,0 @@ -# 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] - pull_request: - branches: [main, master] - -name: R-CMD-historic-R-check - -jobs: - R-CMD-check: - runs-on: ${{ matrix.config.os }} - - name: ${{ matrix.config.os }} (${{ matrix.config.r }}) - - strategy: - fail-fast: false - matrix: - config: - - {os: ubuntu-latest, r: 'oldrel-2'} - - {os: ubuntu-latest, r: 'oldrel-3'} - - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - R_KEEP_PKG_SOURCE: yes - - steps: - - uses: actions/checkout@v3 - - - 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 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index f60d047..018b8db 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -6,7 +6,9 @@ on: pull_request: branches: [main, master] -name: lint +name: lint.yaml + +permissions: read-all jobs: lint: diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index a7276e8..4bbce75 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -9,7 +9,9 @@ on: types: [published] workflow_dispatch: -name: pkgdown +name: pkgdown.yaml + +permissions: read-all jobs: pkgdown: diff --git a/.github/workflows/pr-commands.yaml b/.github/workflows/pr-commands.yaml deleted file mode 100644 index eea58c5..0000000 --- a/.github/workflows/pr-commands.yaml +++ /dev/null @@ -1,79 +0,0 @@ -# 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: - issue_comment: - types: [created] - -name: Commands - -jobs: - document: - if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} - name: document - runs-on: ubuntu-latest - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - - uses: r-lib/actions/pr-fetch@v2 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - - uses: r-lib/actions/setup-r@v2 - with: - use-public-rspm: true - - - uses: r-lib/actions/setup-r-dependencies@v2 - with: - extra-packages: any::roxygen2 - needs: pr-document - - - name: Document - run: roxygen2::roxygenise() - shell: Rscript {0} - - - name: commit - run: | - git config --local user.name "$GITHUB_ACTOR" - git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" - git add man/\* NAMESPACE - git commit -m 'Document' - - - uses: r-lib/actions/pr-push@v2 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - style: - if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} - name: style - runs-on: ubuntu-latest - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - - uses: r-lib/actions/pr-fetch@v2 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - - uses: r-lib/actions/setup-r@v2 - - - name: Install dependencies - run: install.packages("styler") - shell: Rscript {0} - - - name: Style - run: styler::style_pkg() - shell: Rscript {0} - - - name: commit - run: | - git config --local user.name "$GITHUB_ACTOR" - git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" - git add \*.R - git commit -m 'Style' - - - uses: r-lib/actions/pr-push@v2 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/recheck.yml b/.github/workflows/recheck.yml new file mode 100644 index 0000000..323d2ad --- /dev/null +++ b/.github/workflows/recheck.yml @@ -0,0 +1,18 @@ +on: + workflow_dispatch: + inputs: + which: + type: choice + description: Which dependents to check + options: + - strong + - most + +name: Reverse dependency check + +jobs: + revdep_check: + name: Reverse check ${{ inputs.which }} dependents + uses: r-devel/recheck/.github/workflows/recheck.yml@v1 + with: + which: ${{ inputs.which }} diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index 21b8a93..9882260 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -6,7 +6,9 @@ on: pull_request: branches: [main, master] -name: test-coverage +name: test-coverage.yaml + +permissions: read-all jobs: test-coverage: @@ -23,18 +25,27 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2 with: - extra-packages: any::covr + extra-packages: any::covr, any::xml2 needs: coverage - name: Test coverage run: | - covr::codecov( + cov <- covr::package_coverage( quiet = FALSE, clean = FALSE, install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") ) + covr::to_cobertura(cov) shell: Rscript {0} + - uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }} + file: ./cobertura.xml + plugin: noop + disable_search: true + token: ${{ secrets.CODECOV_TOKEN }} + - name: Show testthat output if: always() run: | diff --git a/DESCRIPTION b/DESCRIPTION index 9199f2a..d7c9075 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -41,7 +41,7 @@ URL: https://larmarange.github.io/labelled/, https://github.com/larmarange/label BugReports: https://github.com/larmarange/labelled/issues VignetteBuilder: knitr LazyData: true -RoxygenNote: 7.3.1 +RoxygenNote: 7.3.2 Roxygen: list(markdown = TRUE) RdMacros: lifecycle Language: en-US diff --git a/NEWS.md b/NEWS.md index b47a31f..1972915 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,11 @@ # labelled (development version) +**New features** + +* in `update_variable_labels_with()`, it is now possible to access the variable + name inside `.fn` by using `names()` (#163) +* `var_label()` gets new options `"na"` and `"empty"` for `null_action` + # labelled 2.13.0 **New features** diff --git a/R/update_with.R b/R/update_with.R index 1d0739c..ef0bd02 100644 --- a/R/update_with.R +++ b/R/update_with.R @@ -4,6 +4,11 @@ #' selected `.cols`. #' @param .cols Columns to update; defaults to all columns. Use tidy selection. #' @param ... additional arguments passed onto `.fn`. +#' @details +#' For `update_variable_labels_with()`, it is possible to access the name of +#' the variable inside `.fn` by using `names()`, i.e. `.fn` receive a named +#' character vector (see example). `.fn` can return `as.character(NA)` to +#' remove a variable label. #' @examples #' df <- iris %>% #' set_variable_labels( @@ -18,6 +23,12 @@ #' df %>% #' update_variable_labels_with(toupper) %>% #' look_for() +#' +#' # accessing variable names with names() +#' df %>% +#' update_variable_labels_with(function(x){tolower(names(x))}) %>% +#' look_for() +#' #' df %>% #' update_variable_labels_with(toupper, .cols = dplyr::starts_with("S")) %>% #' look_for() @@ -40,9 +51,19 @@ update_variable_labels_with.data.frame <- function(.data, .data, allow_rename = FALSE ) - vl <- var_label(.data) + vl <- var_label(.data, null_action = "na") vl <- vl[names(cols)] + + vl <- mapply( + function(variable, label) { + setNames(label, variable) + }, + names(vl), + vl, + SIMPLIFY = FALSE + ) vl <- lapply(vl, .fn, ...) + vl <- lapply(vl, unname) var_label(.data) <- vl .data } diff --git a/R/var_label.R b/R/var_label.R index d598dd0..9afd6be 100644 --- a/R/var_label.R +++ b/R/var_label.R @@ -7,7 +7,8 @@ #' @param unlist for data frames, return a named vector instead of a list #' @param null_action for data frames, by default `NULL` will be returned for #' columns with no variable label. Use `"fill"` to populate with the column name -#' instead, or `"skip"` to remove such values from the returned list. +#' instead, `"skip"` to remove such values from the returned list, `"na"` to +#' populate with `NA` or `"empty"` to populate with an empty string (`""`). #' @param recurse if `TRUE`, will apply `var_label()` on packed columns #' (see [tidyr::pack()]) to return the variable labels of each sub-column; #' otherwise, the label of the group of columns will be returned. @@ -70,7 +71,8 @@ var_label.default <- function(x, ...) { #' @export var_label.data.frame <- function(x, unlist = FALSE, - null_action = c("keep", "fill", "skip"), + null_action = + c("keep", "fill", "skip", "na", "empty"), recurse = FALSE, ...) { if (recurse) { @@ -98,6 +100,24 @@ var_label.data.frame <- function(x, ) } + if (null_action == "empty") { + r <- lapply( + r, + function(x) { + if (is.null(x)) "" else x + } + ) + } + + if (null_action == "na") { + r <- lapply( + r, + function(x) { + if (is.null(x)) as.character(NA) else x + } + ) + } + if (null_action == "skip") { r <- r[!sapply(r, is.null)] } diff --git a/README.md b/README.md index 6371718..bc7e85c 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ stable](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https:// [![CRAN\_Status\_Badge](https://www.r-pkg.org/badges/version/labelled)](https://cran.r-project.org/package=labelled) [![Downloads](https://cranlogs.r-pkg.org/badges/labelled)](https://cran.r-project.org/package=labelled) [![DOI](https://www.zenodo.org/badge/38772078.svg)](https://zenodo.org/badge/latestdoi/38772078) -[![Codecov test coverage](https://codecov.io/gh/larmarange/labelled/branch/main/graph/badge.svg)](https://app.codecov.io/gh/larmarange/labelled?branch=main) +[![Codecov test coverage](https://codecov.io/gh/larmarange/labelled/graph/badge.svg)](https://app.codecov.io/gh/larmarange/labelled) diff --git a/man/update_variable_labels_with.Rd b/man/update_variable_labels_with.Rd index d2f699b..6f07018 100644 --- a/man/update_variable_labels_with.Rd +++ b/man/update_variable_labels_with.Rd @@ -22,6 +22,12 @@ selected \code{.cols}.} \description{ Update variable/value labels with a function } +\details{ +For \code{update_variable_labels_with()}, it is possible to access the name of +the variable inside \code{.fn} by using \code{names()}, i.e. \code{.fn} receive a named +character vector (see example). \code{.fn} can return \code{as.character(NA)} to +remove a variable label. +} \examples{ df <- iris \%>\% set_variable_labels( @@ -36,6 +42,12 @@ df \%>\% look_for() df \%>\% update_variable_labels_with(toupper) \%>\% look_for() + +# accessing variable names with names() +df \%>\% + update_variable_labels_with(function(x){tolower(names(x))}) \%>\% + look_for() + df \%>\% update_variable_labels_with(toupper, .cols = dplyr::starts_with("S")) \%>\% look_for() diff --git a/man/var_label.Rd b/man/var_label.Rd index cd6bb25..2e40f50 100644 --- a/man/var_label.Rd +++ b/man/var_label.Rd @@ -17,7 +17,7 @@ var_label(x, ...) \method{var_label}{data.frame}( x, unlist = FALSE, - null_action = c("keep", "fill", "skip"), + null_action = c("keep", "fill", "skip", "na", "empty"), recurse = FALSE, ... ) @@ -45,7 +45,8 @@ label_attribute(x) <- value \item{null_action}{for data frames, by default \code{NULL} will be returned for columns with no variable label. Use \code{"fill"} to populate with the column name -instead, or \code{"skip"} to remove such values from the returned list.} +instead, \code{"skip"} to remove such values from the returned list, \code{"na"} to +populate with \code{NA} or \code{"empty"} to populate with an empty string (\code{""}).} \item{recurse}{if \code{TRUE}, will apply \code{var_label()} on packed columns (see \code{\link[tidyr:pack]{tidyr::pack()}}) to return the variable labels of each sub-column; diff --git a/tests/testthat/test-labelled.r b/tests/testthat/test-labelled.r index c1ca879..77bdc6d 100644 --- a/tests/testthat/test-labelled.r +++ b/tests/testthat/test-labelled.r @@ -1200,6 +1200,26 @@ test_that("null_action in var_label() works as expected", { Species = "Species" ) ) + expect_equal( + var_label(df, null_action = "na"), + list( + Sepal.Length = NA_character_, + Sepal.Width = NA_character_, + Petal.Length = "length of petal", + Petal.Width = "width of petal", + Species = NA_character_ + ) + ) + expect_equal( + var_label(df, null_action = "empty"), + list( + Sepal.Length = "", + Sepal.Width = "", + Petal.Length = "length of petal", + Petal.Width = "width of petal", + Species = "" + ) + ) expect_equal( var_label(df, null_action = "skip"), list( diff --git a/tests/testthat/test-update_with.r b/tests/testthat/test-update_with.r index 53fc715..09bac17 100644 --- a/tests/testthat/test-update_with.r +++ b/tests/testthat/test-update_with.r @@ -20,6 +20,16 @@ test_that("update_variable_labels_with() works as expected", { expect_equal(var_label(tmp$Species), "SPECIES") expect_equal(var_label(tmp$Sepal.Length), "LENGTH OF SEPAL") expect_equal(var_label(tmp$Petal.Width), "Width of petal") + + tmp <- df %>% + update_variable_labels_with(~ tolower(names(.x))) + expect_equal(var_label(tmp$Species), "species") + expect_equal(var_label(tmp$Sepal.Length), "sepal.length") + + tmp <- iris %>% + update_variable_labels_with(~ tolower(names(.x))) + expect_equal(var_label(tmp$Species), "species") + expect_equal(var_label(tmp$Sepal.Length), "sepal.length") }) test_that("update_value_labels_with() works as expected", {