Найти строки в фрейме данных, которые содержат все элементы строки другого фрейма данных

#r #dataframe

#r #фрейм данных

Вопрос:

У меня есть фрейм данных, который содержит три столбца, и второй, который содержит два столбца.

 df1 <- data.frame(X1 = c('A', 'A', 'A', 'A', 'A', 'A', 'B'), 
                  X2 = c('B', 'B', 'B', 'C', 'C', 'D', 'C'), 
                  X3 = c('C', 'D', 'E', 'D', 'E', 'E', 'D'))

df2 <- data.frame(X1 = c('A', 'A'), 
                  X2 = c('B', 'D'))
 

Вопросы:

  1. Как мне найти строки, в df1 которых содержатся все элементы строки df2 ? т.е. строки 1: 3 df1 содержат оба A и B (первая строка df2 ). Я ищу, чтобы удалить любые строки df1 , которые содержат оба элемента строк df2 . Итак, в примере я хотел бы удалить строки 1, 2, 3, 4 и 6 df1 , поскольку они включают A и B ИЛИ A и D .
  2. Есть ли быстрый способ подсчитать количество строк для каждой строки df2 без зацикливания? т.е. df2 строка 1 будет иметь количество 3, а строка 2 — количество 3.

Ответ №1:

Вот базовая опция R, использующая outer intersect

 mat <- lengths(
  outer(
    asplit(df1, 1),
    asplit(df2, 1),
    Vectorize(intersect)
  )
) >= ncol(df2)
 

и вы получите

 > subset(df1, !rowSums(mat))
  X1 X2 X3
5  A  C  E
7  B  C  D

> within(df2, cnt <- colSums(mat))
  X1 X2 cnt
1  A  B   3
2  A  D   3
 
  • asplit разбивает фреймы данных по строкам
  • outer создает все комбинации строк из df1 и df2
  • intersect выдает пересекающиеся элементы строк из двух фреймов данных
  • subset выбирает строки, в которых меньше одного общего элемента

Ответ №2:

С помощью apply:

 df1[ !apply(df1, 1, function(i) any(apply(df2, 1, function(j) all(j %in% i)))), ]
#   X1 X2 X3
# 5  A  C  E
# 7  B  C  D
 

Выполните аналогичные циклы для подсчета совпадений df2:

 cbind(df2, 
      cnt = apply(df2, 1, function(i) sum(apply(df1, 1, function(j) all(i %in% j)))))
#   X1 X2 cnt
# 1  A  B   3
# 2  A  D   3
 

Ответ №3:

Вам нужно как-то выполнить цикл. Вот один из способов сделать это с помощью dplyr и purrr :

1.

 for(iRow in seq_len(nrow(df2))){
  
  df1 <- df1 %>% 
    rowwise() %>% 
    filter(!all(as.character(df2[iRow,]) %in% c_across(everything())))
}
 

2.

 df2 %>% 
  rowwise() %>% 
  mutate(n = sum(map_int(transpose(df1), ~all(c_across(everything()) %in% .x))))
 

Просто не забудьте выполнить 2-ю часть перед 1-й, потому что 1-я часть удаляет строки. Также вы можете сначала определить, какие строки нужно удалить для каждой строки df2 . Таким образом, вы можете посчитать их, а затем удалить.

 df2 <- df2 %>% 
  rowwise() %>% 
  mutate(
    indices = list(which(map_lgl(transpose(df1), ~all(c_across(everything()) %in% .x))))
  ) %>%
  ungroup() %>%
  mutate(n = map_int(indices, length))

df1 <- df2[["indices"]] %>%
  unlist() %>%
  unique() %>%
  "*"(-1) %>%
  df1[.,]

df2 <- df2 %>% select(-indices)