Как я могу подмножествовать строки во фрейме данных в R, если какое-либо значение в одной строке соответствует значениям в векторе?

#r #subset

#r #подмножество

Вопрос:

У меня есть набор данных с именем DF1, который выглядит следующим образом:

  V1    V2    V3   V4     V5    V6
A01N  A01N  A01P  Null  Null  Null
C09K  A61K  C09D  C08K  Null  Null                                              
A61K  A61P  A61P  A61K  A61K  A61K                                          
A01D  A01D  A01D  A01D  A01D  Null
E06A  Null  Null  Null  Null  Null                              
 

также вектор с именем V:

 (A01N C09K A01D)
 

Я хочу, чтобы подмножество DF1 основывалось на элементах вектора, если одна строка в DF1 содержит элементы в V, независимо от того, в каком столбце, тогда сохраните строку. если нет, отбросьте его. Результат должен быть:

  V1    V2    V3   V4     V5    V6
A01N  A01N  A01P  Null  Null  Null
C09K  A61K  C09D  C08K  Null  Null                                                                              
 

Я пытаюсь использовать subset(): test_t1 <- subset(DF1, DF1[,1:6] %в% V)

но я просто знаю, как подмножество одного столбца или строки, как обрабатывать несколько столбцов?

Ответ №1:

Попробуйте reshaping использовать tidyverse функции. Вы форматируете столбцы в long, чтобы затем сравнить их с вектором значений. После этого выполните фильтрацию, а затем измените ее на wide. Вот код:

 library(tidyverse)
#Data
vec <- c('A01N','C09K','A01D')
#Code
new <- df %>% mutate(id=row_number()) %>%
  pivot_longer(-id) %>%
  mutate(Flag= (value%in%vec)) %>%
  group_by(id) %>%
  mutate(Sum=sum(Flag)) %>%
  filter(Sum>=1) %>%
  select(-c(Flag,Sum)) %>%
  pivot_wider(names_from = name,values_from=value) %>%
  ungroup %>% select(-id)
 

Вывод:

 # A tibble: 3 x 6
  V1    V2    V3    V4    V5    V6   
  <chr> <chr> <chr> <chr> <chr> <chr>
1 A01N  A01N  A01P  Null  Null  Null 
2 C09K  A61K  C09D  C08K  Null  Null 
3 A01D  A01D  A01D  A01D  A01D  Null 
 

Или использовать base R с apply() :

 #Code2
new <- df[apply(df,1,function(x) ifelse(sum(x %in% vec)>=1,1,0))==1,]
 

Вывод:

     V1   V2   V3   V4   V5   V6
1 A01N A01N A01P Null Null Null
2 C09K A61K C09D C08K Null Null
4 A01D A01D A01D A01D A01D Null
 

Некоторые используемые данные:

 #Data
df <- structure(list(V1 = c("A01N", "C09K", "A61K", "A01D", "E06A"), 
    V2 = c("A01N", "A61K", "A61P", "A01D", "Null"), V3 = c("A01P", 
    "C09D", "A61P", "A01D", "Null"), V4 = c("Null", "C08K", "A61K", 
    "A01D", "Null"), V5 = c("Null", "Null", "A61K", "A01D", "Null"
    ), V6 = c("Null", "Null", "A61K", "Null", "Null")), class = "data.frame", row.names = c(NA, 
-5L))
 

Если слишком много переменных создают проблемы, вот более упрощенная версия кода (большое спасибо GregorThomas):

 #Code1
new <- df %>% mutate(id=row_number()) %>%
  pivot_longer(-id) %>%
  group_by(id) %>%
  filter(sum(value %in% vec) > 0) %>%
  pivot_wider(names_from = name,values_from=value) %>%
  ungroup %>% select(-id)

#Code2
new <- df[apply(df,1,function(x) sum(x %in% vec)>=1),]
 

Комментарии:

1. Если вы не возражаете против общего замечания, вы … склонны делать много дополнительных шагов. Я думаю mutate(Flag= (value%in%vec)) %>% group_by(id) %>% mutate(Sum=sum(Flag)) %>% filter(Sum>=1) %>% select(-c(Flag,Sum)) , можно было бы сократить до group_by(id) %>% filter(sum(value %in% vec) > 0)) .

2. Аналогично в вашей базовой версии, = ifelse(sum(x %in% vec)>=1,1,0))==1 — это долгий путь для записи sum(x %in% vec) >= 1 . Вы получаете желаемый логический результат, затем преобразуете его в числовой с ifelse() помощью , а затем преобразуете обратно в логический с == 1 помощью . Пропустите преобразования.

3. @GregorThomas Отличные предложения, позвольте мне добавить в код, который я использую, чтобы сделать больше шагов для отслеживания того, что происходит!

4. Я согласен, что временные переменные (особенно с красивыми именами, которые у вас есть) могут быть полезны в сложных процессах. Особенно, если они необходимы для более чем одного использования. Но в данном случае все довольно просто, и я думаю, что создание столбцов для использования один раз, а затем удаление делает решение менее понятным, а не более понятным.

Ответ №2:

Это простая однострочная строка в базе R:

 DF1[rowSums(DF1 %in% vec) > 0, ]
 

Ответ №3:

Опция в base R может быть

 subset(DF1, Reduce(` `, lapply(DF1, `%in%`, vec)) > 0)
 

-вывод

 #     V1   V2   V3   V4   V5   V6
#1 A01N A01N A01P Null Null Null
#2 C09K A61K C09D C08K Null Null
#4 A01D A01D A01D A01D A01D Null
 

данные

 DF1 <- structure(list(V1 = c("A01N", "C09K", "A61K", "A01D", "E06A"), 
    V2 = c("A01N", "A61K", "A61P", "A01D", "Null"), V3 = c("A01P", 
    "C09D", "A61P", "A01D", "Null"), V4 = c("Null", "C08K", "A61K", 
    "A01D", "Null"), V5 = c("Null", "Null", "A61K", "A01D", "Null"
    ), V6 = c("Null", "Null", "A61K", "Null", "Null")), 
    class = "data.frame", row.names = c(NA, 
-5L))

vec <-  c('A01N','C09K','A01D')