Динамическое создание переменных на основе символьного вектора и оценка числового вектора по переменной в R

#r #variables #dplyr

Вопрос:

Работа над сценарием для анализа результатов секвенирования SARS-CoV-2 для импорта в нашу лабораторную информационную систему. Необходимо проверить, включены ли позиции нуклеотидов для ключевых мутаций в данные консенсусной последовательности. Данные о позициях отсутствующих нуклеотидных последовательностей включаются в виде строковой переменной, разделенной запятыми, где диапазоны разделены «-«. Я подумал, что я напишу цикл for, который проверяет каждую позицию ключевого нуклеотида на наличие определенной мутации в соответствии с отсутствующими данными, определенными в строковой переменной. Пока что:

 library(tidyverse)
 

Создание тестовых данных

 subs <- as.character(c("A", "B", "C", "D"))
subs_pos <- as.numeric(c("1", "30","22700", "13500"))
df <- data.frame("id" = letters[1:5], 
                 "missing" = as.character(c("1-13030,13364-13626,13962-15504,15862-26543,26891-29904",
                                            "1-29,21717,29727-29777,29837-29904",
                                            "19276-19571,22627-22822,29837-29904",
                                            "29837-29904",
                                            "1-10,20-30"
                                            )))
 

фрейм данных:

 id                                                 missing
1  a 1-13030,13364-13626,13962-15504,15862-26543,26891-29904
2  b                      1-29,21717,29727-29777,29837-29904
3  c                     19276-19571,22627-22822,29837-29904
4  d                                             29837-29904
5  e                                              1-10,20-30
 

для петли

 for(i in seq_along(subs)) { 
  new_var = as.character(subs[i])
  print(new_var)
  nn = as.numeric(subs_pos[i])
  print(nn)
  df <- df %>% 
    mutate(!!new_var := ifelse(!!nn %in%
                               as.numeric(
                                 source(textConnection(paste("c(", gsub("\-", ":", missing),")")))$value), "I", "N"))
}
 

Печать на экране и результирующий кадр данных:

 >[1] "A"
>[1] 1
>[1] "B"
>[1] 30
>[1] "C"
>[1] 22700
>[1] "D"
>[1] 13500
> df
>  id                                                 missing A B C D
>1  a 1-13030,13364-13626,13962-15504,15862-26543,26891-29904 I I N N
>2  b                      1-29,21717,29727-29777,29837-29904 I I N N
>3  c                     19276-19571,22627-22822,29837-29904 I I N N
>4  d                                             29837-29904 I I N N
>5  e                                              1-10,20-30 I I N N
 

Ожидаемый фрейм данных:

 > id                                                 missing A B C D
> 1  a 1-13030,13364-13626,13962-15504,15862-26543,26891-29904 I I N I
> 2  b                      1-29,21717,29727-29777,29837-29904 I N N N
> 3  c                     19276-19571,22627-22822,29837-29904 N N I N
> 4  d                                             29837-29904 N N N N
> 5  e                                              1-10,20-30 I I N N                                            
 

Если запустить для одного экземпляра, тест сработает

 > 13500 %in% as.numeric(source(textConnection(paste("c(", gsub("\-", ":", df$missing[1]),")")))$value)
[1] TRUE
 

Мне кажется, что результаты моего кода в результатах последней выполняемой оценки применяются ко всем строкам во фрейме данных. Я подтвердил это, изменив данные теста.

Ответ №1:

Мы могли бы разделить «отсутствующие», , чтобы расширить строки, создать новые столбцы «начало», «остановка», разделив разделитель - («df1»)

 library(dplyr)
library(tidyr)

df1 <- df %>%
       separate_rows(missing, sep = ",") %>%
       separate(missing, into = c('start', 'stop'), convert = TRUE) 
 

Теперь мы используем метод OP для создания новых столбцов с вектором ‘subs’

 for(i in seq_along(subs)) {
     df1 <- df1 %>%
          mutate(!! subs[i] :=  case_when(!is.na(stop) amp;
           start <= subs_pos[i] amp;  stop >= subs_pos[i] ~ 'I', TRUE ~'N'))
 
 }
 

Сгруппированные по «id», summarise столбцы «subs» для возврата «I», если есть какие-либо » I » или «N», объедините исходные данные и select столбцы в нужном нам порядке

 df1 %>% 
     group_by(id) %>% 
     summarise(across(c(A:D), ~ case_when('I' %in% . ~ 'I', 
            TRUE ~ 'N'))) %>% 
     right_join(df) %>%
     select(names(df), everything())
 

-выход

 # A tibble: 5 x 6
#  id    missing                                                 A     B     C     D    
#  <chr> <chr>                                                   <chr> <chr> <chr> <chr>
#1 a     1-13030,13364-13626,13962-15504,15862-26543,26891-29904 I     I     I     I    
#2 b     1-29,21717,29727-29777,29837-29904                      I     N     N     N    
#3 c     19276-19571,22627-22822,29837-29904                     N     N     I     N    
#4 d     29837-29904                                             N     N     N     N    
#5 e     1-10,20-30                                              I     I     N     N    


 
 

Ответ №2:

Другое решение, предложенное коллегой:

 for(i in 1:nrow(df)){
  ranges <- as.numeric(source(textConnection(paste("c(", gsub("\-", ":", df$missing[i]),")")))$value)
  for(j in 1:length(subs)){
    if(subs_pos[j] %in% ranges) {
      df[i,][subs[j]] <- "I"
    }
  }
}
 

}