diff --git a/R/plots_featureimportance.R b/R/plots_featureimportance.R index b7c9b4c..6c79849 100644 --- a/R/plots_featureimportance.R +++ b/R/plots_featureimportance.R @@ -284,6 +284,29 @@ makeFeatureImportancePlot <- function( } +#' Strip standalone "NA" tokens from a COG_name string +#' +#' The annotation source stores unnamed COG name slots as the literal string +#' "NA". Splits on `;` first so tokens adjacent to a semicolon get caught too. +#' +#' @param x Character vector of `COG_name` values. +#' @return Cleaned vector; `NA_character_` where no real name remains. +#' @keywords internal +#' @noRd +.clean_cog_name <- function(x) { + clean_one <- function(s) { + if (is.na(s)) return(NA_character_) + kept <- character(0) + for (p in trimws(strsplit(s, ";", fixed = TRUE)[[1]])) { + toks <- setdiff(strsplit(p, "[[:space:]]+")[[1]], c("NA", "")) + if (length(toks)) kept <- c(kept, paste(toks, collapse = " ")) + } + if (!length(kept)) NA_character_ else paste(kept, collapse = "; ") + } + vapply(x, clean_one, character(1), USE.NAMES = FALSE) +} + + #' Horizontal bar chart of the most common COGs #' #' Counts COG occurrences across the features in an annotation-enriched @@ -314,9 +337,14 @@ makeCogBarChart <- function(enriched_tbl, top_n = 15) { rows <- do.call(rbind, lapply(seq_len(nrow(cog_df)), function(i) { cogs <- trimws(strsplit(cog_df$COG[i], ",", fixed = TRUE)[[1]]) - names <- if ("COG_name" %in% names(cog_df) && - !is.na(cog_df$COG_name[i])) { - n <- trimws(strsplit(cog_df$COG_name[i], ";", fixed = TRUE)[[1]]) + # .clean_cog_name() strips "NA" placeholder tokens the source stuffs in. + clean <- if ("COG_name" %in% names(cog_df)) { + .clean_cog_name(cog_df$COG_name[i]) + } else { + NA_character_ + } + names <- if (!is.na(clean)) { + n <- trimws(strsplit(clean, ";", fixed = TRUE)[[1]]) rep_len(n, length(cogs)) } else { rep(NA_character_, length(cogs)) diff --git a/tests/testthat/test-makeCogBarChart.R b/tests/testthat/test-makeCogBarChart.R index c3f8482..cde925d 100644 --- a/tests/testthat/test-makeCogBarChart.R +++ b/tests/testthat/test-makeCogBarChart.R @@ -60,3 +60,37 @@ test_that("makeCogBarChart respects top_n parameter", { result <- makeCogBarChart(df, top_n = 5) expect_s3_class(result, "plotly") }) + +test_that(".clean_cog_name strips standalone NA tokens", { + expect_equal(.clean_cog_name("Alanine racemase NA NA NA"), "Alanine racemase") + expect_equal( + .clean_cog_name("D-serine deaminase NA"), "D-serine deaminase" + ) + # NA tokens in the middle collapse too + expect_equal(.clean_cog_name("foo NA bar"), "foo bar") +}) + +test_that(".clean_cog_name returns NA when nothing meaningful remains", { + expect_true(is.na(.clean_cog_name("NA"))) + expect_true(is.na(.clean_cog_name("NA NA NA"))) + expect_true(is.na(.clean_cog_name(NA_character_))) +}) + +test_that(".clean_cog_name strips NA tokens adjacent to semicolons", { + expect_equal( + .clean_cog_name("Foo NA NA NA; Bar NA NA"), + "Foo; Bar" + ) + expect_equal( + .clean_cog_name("Foo NA; NA; Bar"), + "Foo; Bar" + ) +}) + +test_that(".clean_cog_name leaves clean names untouched and is vectorised", { + expect_equal(.clean_cog_name("Beta-lactamase class C"), "Beta-lactamase class C") + expect_equal( + .clean_cog_name(c("Helicase NA", "NA", "Transcription")), + c("Helicase", NA, "Transcription") + ) +})