diff --git a/.lintr b/.lintr index 7497bbf0..f58ffa6a 100644 --- a/.lintr +++ b/.lintr @@ -1,4 +1,10 @@ linters: linters_with_defaults( line_length_linter = line_length_linter(120L), + any_is_na_linter(), + any_duplicated_linter(), + outer_negation_linter(), + nzchar_linter(), + is_numeric_linter(), + fixed_regex_linter(), object_name_linter = object_name_linter(styles = c("snake_case", "symbols", "CamelCase", "SNAKE_CASE")) ) diff --git a/R/AnnDataView.R b/R/AnnDataView.R index 2926b2f1..32f9926d 100644 --- a/R/AnnDataView.R +++ b/R/AnnDataView.R @@ -368,7 +368,7 @@ convert_to_indices <- function( ) } which(subset) - } else if (is.integer(subset) || is.numeric(subset)) { + } else if (is.numeric(subset)) { subset <- as.integer(subset) if (any(subset < 1 | subset > max_length)) { cli_abort( @@ -385,7 +385,7 @@ convert_to_indices <- function( )) } indices <- match(subset, names_vector) - if (any(is.na(indices))) { + if (anyNA(indices)) { # nolint start: object_usage_linter missing_names <- subset[is.na(indices)] # nolint end: object_usage_linter diff --git a/R/ReticulateAnnData.R b/R/ReticulateAnnData.R index a88a2976..ff0a6836 100644 --- a/R/ReticulateAnnData.R +++ b/R/ReticulateAnnData.R @@ -350,7 +350,7 @@ ReticulateAnnData <- R6::R6Class( # Set proper index names if they don't exist or are auto-generated if ( is.null(rownames(obs)) || - all(rownames(obs) == "") || + !any(nzchar(rownames(obs))) || all(rownames(obs) == as.character(seq_len(nrow(obs)))) ) { py_obs$index <- reticulate::r_to_py(paste0( @@ -360,7 +360,7 @@ ReticulateAnnData <- R6::R6Class( } if ( is.null(rownames(var)) || - all(rownames(var) == "") || + !any(nzchar(rownames(var))) || all(rownames(var) == as.character(seq_len(nrow(var)))) ) { py_var$index <- reticulate::r_to_py(paste0( diff --git a/R/as_Seurat.R b/R/as_Seurat.R index 400a3883..086b91e6 100644 --- a/R/as_Seurat.R +++ b/R/as_Seurat.R @@ -211,7 +211,7 @@ as_Seurat <- function( } else if (!is.null(adata$X)) { layers_mapping[["X"]] <- NA } - if (any(duplicated(names(layers_mapping)))) { + if (anyDuplicated(names(layers_mapping)) > 0) { cli_abort( "{.arg layers_mapping} or {.arg x_mapping} must not contain any duplicate names", "i" = "Found duplicate names: {.val {names(layers_mapping)[duplicated(names(layers_mapping))]}}" diff --git a/R/as_SingleCellExperiment.R b/R/as_SingleCellExperiment.R index bc7e78c8..5aa78fa3 100644 --- a/R/as_SingleCellExperiment.R +++ b/R/as_SingleCellExperiment.R @@ -216,7 +216,7 @@ as_SingleCellExperiment <- function( } else if (!is.null(adata$X)) { assays_mapping[["X"]] <- NA } - if (any(duplicated(names(assays_mapping)))) { + if (anyDuplicated(names(assays_mapping)) > 0) { cli_abort( "{.arg assays_mapping} or {.arg x_mapping} must not contain any duplicate names", "i" = "Found duplicate names: {.val {names(assays_mapping)[duplicated(names(assays_mapping))]}}" diff --git a/R/check_requires.R b/R/check_requires.R index efecc9ae..a08433f0 100644 --- a/R/check_requires.R +++ b/R/check_requires.R @@ -41,7 +41,7 @@ check_python_packages <- function(what, requires) { is_available <- map_lgl(requires, reticulate::py_module_available) - if (any(!is_available)) { + if (!all(is_available)) { missing <- requires[!is_available] # nolint start object_usage_linter missing_str <- format_package_list(missing) @@ -73,7 +73,7 @@ check_python_packages <- function(what, requires) { check_r_packages <- function(what, requires, where) { is_available <- map_lgl(requires, requireNamespace, quietly = TRUE) - if (any(!is_available)) { + if (!all(is_available)) { missing <- requires[!is_available] # nolint start object_usage_linter missing_str <- format_package_list(missing) diff --git a/R/from_Seurat.R b/R/from_Seurat.R index e6fa29f5..2bf5c8ff 100644 --- a/R/from_Seurat.R +++ b/R/from_Seurat.R @@ -527,7 +527,7 @@ from_Seurat <- function( next } - dest_name <- gsub(paste0(assay_name, "_"), "", graph_name) + dest_name <- gsub(paste0(assay_name, "_"), "", graph_name, fixed = TRUE) obsp_mapping[dest_name] <- graph_name } diff --git a/R/from_SingleCellExperiment.R b/R/from_SingleCellExperiment.R index 4897718c..6451413c 100644 --- a/R/from_SingleCellExperiment.R +++ b/R/from_SingleCellExperiment.R @@ -53,7 +53,7 @@ from_SingleCellExperiment <- function( assay_names <- SummarizedExperiment::assayNames(sce) if ( length(SummarizedExperiment::assays(sce)) > 0 && - (is.null(assay_names) || any(assay_names == "")) + (is.null(assay_names) || !all(nzchar(assay_names))) ) { if (is.null(assay_names)) { assay_names <- paste0( @@ -61,7 +61,7 @@ from_SingleCellExperiment <- function( seq_along(SummarizedExperiment::assays(sce)) ) } else { - empty_names <- which(assay_names == "") + empty_names <- which(!nzchar(assay_names)) assay_names[empty_names] <- paste0("assay", empty_names) } diff --git a/R/generate_dataframe.R b/R/generate_dataframe.R index fce3ff31..f7aa1fa5 100644 --- a/R/generate_dataframe.R +++ b/R/generate_dataframe.R @@ -18,5 +18,5 @@ generate_dataframe <- function(num_rows, types = names(vector_generators)) { data <- lapply(types, generate_vector, n = num_rows) names(data) <- types - as.data.frame(data) + list2DF(data) } diff --git a/R/generate_dataset.R b/R/generate_dataset.R index d2e52eff..2ca10ca7 100644 --- a/R/generate_dataset.R +++ b/R/generate_dataset.R @@ -226,14 +226,14 @@ generate_dataset <- function( factor(NA_character_, ordered = TRUE) } else if (uns_type == "scalar_logical_with_nas") { NA_real_ - } else if (grepl("scalar_", uns_type)) { - generate_vector(1L, gsub("scalar_", "", uns_type)) - } else if (grepl("vec_", uns_type)) { - generate_vector(10L, gsub("vec_", "", uns_type)) - } else if (grepl("df_", uns_type)) { - generate_dataframe(10L, gsub("df_", "", uns_type)) - } else if (grepl("mat_", uns_type)) { - generate_matrix(10L, 10L, gsub("mat_", "", uns_type)) + } else if (grepl("scalar_", uns_type, fixed = TRUE)) { + generate_vector(1L, gsub("scalar_", "", uns_type, fixed = TRUE)) + } else if (grepl("vec_", uns_type, fixed = TRUE)) { + generate_vector(10L, gsub("vec_", "", uns_type, fixed = TRUE)) + } else if (grepl("df_", uns_type, fixed = TRUE)) { + generate_dataframe(10L, gsub("df_", "", uns_type, fixed = TRUE)) + } else if (grepl("mat_", uns_type, fixed = TRUE)) { + generate_matrix(10L, 10L, gsub("mat_", "", uns_type, fixed = TRUE)) } else { cli_abort("Unknown {.field uns} type: {.val {uns_type}}") } diff --git a/R/utils.R b/R/utils.R index d1ce170b..84743876 100644 --- a/R/utils.R +++ b/R/utils.R @@ -78,8 +78,8 @@ to_R_matrix <- function(mat) { self_name <- function(x) { if (is.null(names(x))) { x <- setNames(x, x) - } else if (any(names(x) == "")) { - is_missing <- names(x) == "" + } else if (!all(nzchar(names(x)))) { + is_missing <- !nzchar(names(x)) names(x)[is_missing] <- x[is_missing] } diff --git a/R/write_h5ad_helpers.R b/R/write_h5ad_helpers.R index 2f95ed3a..016c040c 100644 --- a/R/write_h5ad_helpers.R +++ b/R/write_h5ad_helpers.R @@ -60,16 +60,14 @@ write_h5ad_element <- function( # Numeric values if (length(value) == 1 && !is.matrix(value)) { write_h5ad_numeric_scalar - } else if ( - is.integer(value) && any(is.na(value)) && length(dim(value)) <= 1 - ) { + } else if (is.integer(value) && anyNA(value) && length(dim(value)) <= 1) { write_h5ad_nullable_integer } else { write_h5ad_dense_array } } else if (is.logical(value)) { # Logical values - if (any(is.na(value))) { + if (anyNA(value)) { write_h5ad_nullable_boolean } else if (length(value) == 1) { # Single Booleans should be written as numeric scalars @@ -214,7 +212,7 @@ write_h5ad_dense_array <- function( value <- as.matrix(value) } - if (is.matrix(value) && any(is.na(value))) { + if (is.matrix(value) && anyNA(value)) { # is.na(value) <- NaN gets ignored na_indices <- is.na(value) value[na_indices] <- NaN diff --git a/R/write_zarr_helpers.R b/R/write_zarr_helpers.R index 8ed9f6e6..f11a4d2c 100644 --- a/R/write_zarr_helpers.R +++ b/R/write_zarr_helpers.R @@ -63,14 +63,14 @@ write_zarr_element <- function( # Numeric values if (length(value) == 1 && !is.matrix(value)) { write_zarr_numeric_scalar - } else if (is.integer(value) && any(is.na(value))) { + } else if (is.integer(value) && anyNA(value)) { write_zarr_nullable_integer } else { write_zarr_dense_array } } else if (is.logical(value)) { # Logical values - if (any(is.na(value))) { + if (anyNA(value)) { write_zarr_nullable_boolean } else if (length(value) == 1) { # Single Booleans should be written as numeric scalars diff --git a/tests/testthat/test-HDF5-read.R b/tests/testthat/test-HDF5-read.R index 17f087cf..26b754df 100644 --- a/tests/testthat/test-HDF5-read.R +++ b/tests/testthat/test-HDF5-read.R @@ -63,7 +63,7 @@ test_that("reading 1D sparse numeric arrays works", { test_that("reading 1D nullable arrays works", { array_1d <- read_h5ad_nullable_integer(file, "obs/IntNA") expect_vector(array_1d, ptype = integer(), size = 50) - expect_true(any(is.na(array_1d))) + expect_true(anyNA(array_1d)) array_1d <- read_h5ad_dense_array(file, "obs/FloatNA") expected <- array(rep(42.42, 50)) @@ -72,11 +72,11 @@ test_that("reading 1D nullable arrays works", { array_1d <- read_h5ad_nullable_boolean(file, "obs/Bool") expect_vector(array_1d, ptype = logical(), size = 50) - expect_false(any(is.na(array_1d))) + expect_false(anyNA(array_1d)) array_1d <- read_h5ad_nullable_boolean(file, "obs/BoolNA") expect_vector(array_1d, ptype = logical(), size = 50) - expect_true(any(is.na(array_1d))) + expect_true(anyNA(array_1d)) }) test_that("reading string scalars works", { diff --git a/tests/testthat/test-Zarr-read.R b/tests/testthat/test-Zarr-read.R index cf6fab81..f361e9d0 100644 --- a/tests/testthat/test-Zarr-read.R +++ b/tests/testthat/test-Zarr-read.R @@ -74,7 +74,7 @@ for (zarr_version in c("v2", "v3")) { test_that(paste("reading Zarr", zarr_version, "1D nullable arrays works"), { array_1d <- read_zarr_nullable_integer(store, "obs/IntNA") expect_vector(array_1d, ptype = integer(), size = 50) - expect_true(any(is.na(array_1d))) + expect_true(anyNA(array_1d)) array_1d <- read_zarr_dense_array(store, "obs/FloatNA") expected <- array(rep(42.42, 50)) @@ -83,7 +83,7 @@ for (zarr_version in c("v2", "v3")) { array_1d <- read_zarr_nullable_boolean(store, "obs/BoolNA") expect_vector(array_1d, ptype = logical(), size = 50) - expect_true(any(is.na(array_1d))) + expect_true(anyNA(array_1d)) }) test_that(paste("reading Zarr", zarr_version, "string scalars works"), { diff --git a/tests/testthat/test-roundtrip-X.R b/tests/testthat/test-roundtrip-X.R index 4380c5eb..e65e4390 100644 --- a/tests/testthat/test-roundtrip-X.R +++ b/tests/testthat/test-roundtrip-X.R @@ -13,7 +13,7 @@ test_names <- names(da$matrix_generators) # X must always be 2-dimensional in AnnData # -> https://github.com/scverse/anndata/blob/2a2c0e3198c298a5c80a73ac343c63203b5ca133/src/anndata/_core/anndata.py#L2164-L2172 # nolint -test_names <- test_names[!grepl("_3d$", test_names)] +test_names <- test_names[!endsWith(test_names, "_3d")] for (fmt in c("h5ad", "zarr")) { fmt_config <- get_fmt_config(fmt) diff --git a/vignettes/articles/development_status.Rmd b/vignettes/articles/development_status.Rmd index 8e86fd87..ff826fe5 100644 --- a/vignettes/articles/development_status.Rmd +++ b/vignettes/articles/development_status.Rmd @@ -28,7 +28,7 @@ source <- list.files(proj_root, pattern = "*.R$", recursive = TRUE) # look for trackstatus comments status_lines <- map_df(source, function(path) { lines <- readLines(paste0(proj_root, "/", path)) - line_numbers <- grep("# trackstatus:", lines) + line_numbers <- grep("# trackstatus:", lines, fixed = TRUE) map_df(line_numbers, function(line_number) { tryCatch( @@ -200,7 +200,7 @@ for (i in seq_along(data)) { " * To investigate: ", data[[i]]$to_investigate, "\n", " * To fix: ", data[[i]]$to_fix, "\n\n", "### Error message\n\n", - paste(paste0(" ", strsplit(data[[i]]$error_message, "\n")[[1]], "\n"), collapse = ""), "\n\n", + paste(paste0(" ", strsplit(data[[i]]$error_message, "\n", fixed = TRUE)[[1]], "\n"), collapse = ""), "\n\n", "### Proposed solution\n\n", data[[i]]$proposed_solution, "\n\n" )