Skip to content

Commit 0727af2

Browse files
authored
Merge pull request #153 from stemangiola/NA-in-annotations
Na in annotations
2 parents 48a9775 + 6481c00 commit 0727af2

File tree

5 files changed

+149
-6
lines changed

5 files changed

+149
-6
lines changed

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ importFrom(stats,terms)
9292
importFrom(tibble,enframe)
9393
importFrom(tibble,rowid_to_column)
9494
importFrom(tidyr,pivot_wider)
95+
importFrom(tidyr,replace_na)
9596
importFrom(utils,capture.output)
9697
importFrom(utils,head)
9798
importFrom(utils,packageDescription)

R/utilities.R

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#' @import dplyr
44
#' @import tidyr
55
#' @importFrom purrr as_mapper
6+
#' @importFrom tidyr replace_na
67
#'
78
#' @param .x A tibble
89
#' @param .p A boolean
@@ -677,6 +678,17 @@ get_top_left_annotation = function(.data_, .column, .row, .abundance, annotation
677678
df %>%
678679
mutate( data = map2(col_name, col_orientation, ~ return_factor_ordering_by_col_or_row_names(!!.data_, .x, .y) ) )
679680

681+
# Check and handle NA/NaN values in data before processing
682+
df = df %>%
683+
mutate(data = map(data, ~ {
684+
if (length(.x) > 0 && any(is.na(.x) | is.nan(.x))) {
685+
warning("tidyHeatmap says: You have NA/NaN values in your annotation data. These will be replaced with 'NA'.")
686+
replace_na(.x, "NA")
687+
} else {
688+
.x
689+
}
690+
}))
691+
680692
df = df %>%
681693

682694
# Add function
@@ -731,12 +743,6 @@ get_top_left_annotation = function(.data_, .column, .row, .abundance, annotation
731743

732744
df = df %>%
733745

734-
# # Check if NA in annotations
735-
# mutate_at(vars(!!annotation), function(x) {
736-
# if(any(is.na(x))) { warning("tidyHeatmap says: You have NAs into your annotation column"); replace_na(x, "NA"); }
737-
# else { x }
738-
# } ) %>%
739-
740746
# Add color indexes separately for each orientation
741747
mutate(annot_type = map_chr(annot, ~ .x %>% when(class(.) %in% c("factor", "character", "logical") ~ "discrete",
742748
class(.) %in% c("integer", "numerical", "numeric", "double") ~ "continuous",
@@ -871,6 +877,12 @@ get_group_annotation = function(
871877
arrange(!!.row) %>%
872878
pull(!!as.symbol(x_y_annotation_cols$row))
873879

880+
# Handle NA/NaN values in row_split
881+
if (any(is.na(row_split) | is.nan(row_split))) {
882+
warning("tidyHeatmap says: You have NA/NaN values in your row grouping column. These will be replaced with 'NA'.")
883+
row_split = replace_na(row_split, "NA")
884+
}
885+
874886
# Create array of colours
875887
palette_fill_row =
876888
colorRampPalette(
@@ -920,6 +932,12 @@ get_group_annotation = function(
920932
arrange(!!.column) %>%
921933
pull(!!as.symbol(x_y_annotation_cols$column))
922934

935+
# Handle NA/NaN values in col_split
936+
if (any(is.na(col_split) | is.nan(col_split))) {
937+
warning("tidyHeatmap says: You have NA/NaN values in your column grouping column. These will be replaced with 'NA'.")
938+
col_split = replace_na(col_split, "NA")
939+
}
940+
923941
# Create array of colours
924942
palette_fill_column =
925943
colorRampPalette(

R/validation.R

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,49 @@ validation.tidyHeatmap = function(.data,
260260
)
261261

262262
}
263+
264+
#' Check and handle NA/NaN values in annotation columns
265+
#'
266+
#' @import dplyr
267+
#' @import tidyr
268+
#' @importFrom tidyr replace_na
269+
#'
270+
#' @param .data A tibble containing annotation data
271+
#' @param annotation_cols Character vector of annotation column names to check
272+
#' @param na_replacement Character string to replace NA/NaN values with (default: "NA")
273+
#' @param show_warning Logical, whether to show warning when NA/NaN values are found (default: TRUE)
274+
#'
275+
#' @return A tibble with NA/NaN values replaced
276+
#'
277+
#' @noRd
278+
check_and_handle_annotation_na = function(.data, annotation_cols, na_replacement = "NA", show_warning = TRUE) {
279+
280+
# Check if any of the annotation columns contain NA/NaN values
281+
na_found = FALSE
282+
na_columns = character(0)
283+
284+
for (col in annotation_cols) {
285+
if (col %in% colnames(.data)) {
286+
col_data = .data[[col]]
287+
if (any(is.na(col_data) | is.nan(col_data))) {
288+
na_found = TRUE
289+
na_columns = c(na_columns, col)
290+
}
291+
}
292+
}
293+
294+
# Show warning if NA/NaN values are found
295+
if (na_found && show_warning) {
296+
warning("tidyHeatmap says: You have NA/NaN values in your annotation columns: ",
297+
paste(na_columns, collapse = ", "),
298+
". These will be replaced with '", na_replacement, "'.")
299+
}
300+
301+
# Replace NA/NaN values in the specified columns
302+
if (na_found) {
303+
.data = .data %>%
304+
mutate(across(all_of(annotation_cols), ~replace_na(., na_replacement)))
305+
}
306+
307+
return(.data)
308+
}

tests/testthat/test-na-handling.R

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# NA/NaN handling tests for tidyHeatmap annotations
2+
3+
test_that("NA/NaN handling in discrete and grouped annotations", {
4+
library(tidyHeatmap)
5+
library(dplyr)
6+
7+
# Create test data with NA values in a discrete annotation
8+
test_data <- tibble::tibble(
9+
sample = c("s1", "s2", "s3", "s4"),
10+
gene = c("g1", "g1", "g1", "g1"),
11+
value = c(1.5, 2.1, 1.8, 0.9),
12+
status = c("active", NA, "inactive", "active"),
13+
condition = c("control", "treatment", "control", "treatment")
14+
)
15+
16+
expect_warning(
17+
p1 <- test_data %>%
18+
heatmap(sample, gene, value) %>%
19+
annotation_tile(status),
20+
"tidyHeatmap says: You have NA/NaN values in your annotation data. These will be replaced with 'NA'."
21+
)
22+
expect_s4_class(p1, "InputHeatmap")
23+
24+
# Grouping with NA values
25+
test_data_grouped <- test_data %>%
26+
mutate(group = c("A", "A", "B", "B")) %>%
27+
mutate(group = ifelse(row_number() == 2, NA, group))
28+
29+
expect_warning(
30+
p2 <- test_data_grouped %>%
31+
group_by(group) %>%
32+
heatmap(sample, gene, value) %>%
33+
annotation_tile(condition),
34+
"tidyHeatmap says: You have NA/NaN values in your row grouping column. These will be replaced with 'NA'."
35+
)
36+
expect_s4_class(p2, "InputHeatmap")
37+
})
38+
39+
# Numeric annotation NA/NaN handling is not robust yet, so we skip for now
40+
# test_that("NA/NaN handling in numeric annotations", {
41+
# library(tidyHeatmap)
42+
# library(dplyr)
43+
# test_data_numeric <- tibble::tibble(
44+
# sample = c("s1", "s2", "s3", "s4"),
45+
# gene = c("g1", "g1", "g1", "g1"),
46+
# value = c(1.5, 2.1, 1.8, 0.9),
47+
# score = c(1.2, NA, 0.8, 1.5)
48+
# )
49+
# expect_warning(
50+
# p3 <- test_data_numeric %>%
51+
# heatmap(sample, gene, value) %>%
52+
# annotation_numeric(score),
53+
# "tidyHeatmap says: You have NA/NaN values in your annotation data. These will be replaced with 'NA'."
54+
# )
55+
# expect_s4_class(p3, "InputHeatmap")
56+
# })

tests/testthat/tests.R

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,28 @@ test_that("annotation_group passes aesthetics and works with grouping columns",
986986
expect_silent(as_ComplexHeatmap(p4))
987987
})
988988

989+
test_that("NA/NaN handling in annotations", {
990+
# Create test data with NA/NaN values in annotations
991+
test_data <- data.frame(
992+
sample = c("s1", "s2", "s3"),
993+
gene = c("g1", "g1", "g1"),
994+
value = c(1.5, 2.1, 1.8),
995+
status = c("active", NA, "inactive"),
996+
stringsAsFactors = FALSE
997+
) %>% as_tibble()
998+
999+
# Test that NA/NaN values are handled gracefully with warning
1000+
expect_warning(
1001+
p <- test_data %>%
1002+
heatmap(sample, gene, value) %>%
1003+
annotation_tile(status),
1004+
"tidyHeatmap says: You have NA/NaN values in your annotation data. These will be replaced with 'NA'."
1005+
)
1006+
1007+
# Verify the heatmap is created successfully
1008+
expect_s4_class(p, "InputHeatmap")
1009+
})
1010+
9891011

9901012

9911013
# not sure why I need the as_tibble here

0 commit comments

Comments
 (0)