How to use case_when() on a list with apply or map

Viewed 204

I am trying to use the dplyr function case_when() on a list of annotation so I can assign the name of the marker to my data. I've found a way to do this manually by writing it several times but I tried applying it to a list with apply or map and didn't find a solution.

Here is a reproductible example :

library(tidyverse)
annotation <- list (marker_1 = c("L", "D"),
                    marker_2 = c("C","R"),
                    marker_3 = c("A", "B"),
                    marker_4 = c("E", "F"),
                    marker_5 = c("G", "H"))


df <- tibble(L = 1, D = 2, C = 3, R = 4)


annotation_df <-
  tibble(markers = colnames(df)) %>%
  mutate(
    type = case_when(
      markers %in% annotation[[1]] ~ names(annotation[1]),
      markers %in% annotation[[2]] ~ names(annotation[2]),
      markers %in% annotation[[3]] ~ names(annotation[3]),
      markers %in% annotation[[4]] ~ names(annotation[4]),
      markers %in% annotation[[5]] ~ names(annotation[5])
    )
  ) %>%
  column_to_rownames("markers")
print(annotation_df)
#>       type
#> L marker_1
#> D marker_1
#> C marker_2
#> R marker_2

Created on 2022-04-25 by the reprex package (v2.0.1)

1 Answers

Instead of case_when, a more easier option is a join after converting the named list to a two column tibble with (tibble::enframe)

library(dplyr)
library(tidyr)
library(tibble)
enframe(annotation, name = 'type', value = 'markers') %>% 
   unnest(markers) %>%
    right_join(tibble(markers = colnames(df))) %>%
   relocate(type, .after = 'markers')

-output

# A tibble: 4 × 2
  markers type    
  <chr>   <chr>   
1 L       marker_1
2 D       marker_1
3 C       marker_2
4 R       marker_2

Or another opition is to loop over the list, get the intersecting elements and convert the named list to tibble

library(purrr)
map(annotation, ~ intersect(names(df), .x)) %>%
  keep(lengths(.) > 0) %>%
  enframe(name = 'markers', value = 'type') %>%
  unnest(type)

Or using base R with lapply and stack

lapply(annotation, \(x) intersect(names(df), x)) |>
    Filter(length, x = _) |>
    stack() |> 
    setNames(c("markers", "type")) |>
    subset(select = 2:1)

-output

      type markers
1 marker_1       L
2 marker_1       D
3 marker_2       C
4 marker_2       R
Related