Присоединяйте данные, но игнорируйте пропущенные значения - PullRequest
3 голосов
/ 29 марта 2019

У меня возникли проблемы с соединением фреймов данных с помощью dplyr, где я хотел бы игнорировать NA.

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

id <- c("id1", "id2", "id3", "id4")
A <- c("E", "F", "G", NA)
B <- c("T", NA, "N", "T")
C <- c(NA, "T", "U", NA)

df <- data.frame(A, B, C)

     id    A    B    C
1    id1   E    T    NA
2    id2   F    NA   T
3    id3   G    N    U
4    id4   NA   T    NA

У меня есть запись, которую я хотел бы сопоставить с df, например:

df2 <- data.frame(A = "E", B = "T", C = "M")

    A    B    C
1   E    T    M

В результате я хотел бы получить все строки из df, которые соответствуют df2, ноНС следует игнорировать.Таким образом, результат должен выглядеть следующим образом:

     id    A    B    C
1    id1   E    T    NA
2    id4   NA   T    NA

Я пытался сделать это с помощью semi_join, но пока это не сработало:

result <- df %>%
  group_by(n = seq(n())) %>%
  do(modify_if(., is.na, ~NULL) %>%
       semi_join(df2, by = c("A", "B", "C"))) %>%
  ungroup %>%
  select(-n)

Что приводит к:

Error: `by` can't contain join column `C` which is missing from LHS
Call `rlang::last_error()` to see a backtrace

Кто знает ответ?

Ответы [ 3 ]

1 голос
/ 29 марта 2019

Если вам нужно сделать это только для одного набора значений, это, вероятно, самый простой подход:

d[A %in% c("E",NA) & B %in%c("T",NA) & C %in% c("M",NA),]
1 голос
/ 29 марта 2019

Вот решение с сочетанием Tidyverse и Base R. Я думаю, что это довольно ясно, но я был бы заинтересован в чистой реализации Tidyverse, которая не полностью придумана.

Идея состоит в том, чтобы сначала развернуть все записи в df и df2, а затем отфильтровать все столбцы с помощью цикла.

Данные:

id <- c("id1", "id2", "id3", "id4")
A <- c("E", "F", "G", NA)
B <- c("T", NA, "N", "T")
C <- c(NA, "T", "U", NA)

df <- data.frame(id, A, B, C, stringsAsFactors = F) # Make sure to use strings not factors
df2 <- data.frame(A = "E", B = "T", C = "M", stringsAsFactors = F)

Код:

library(tidyr)
results <- crossing(df, df2)
select_columns <- c("A", "B", "C")
for(col in select_columns) {
  keep <- is.na(results[[col]]) | results[[col]] == results[[paste0(col, 1)]]
  results <- results[keep,, drop=F]
}
results <- results %>% dplyr::select(id, A:C) %>% distinct
results

   id    A B    C
1 id1    E T <NA>
2 id4 <NA> T <NA>
0 голосов
/ 29 марта 2019

Другой пример использования tidyverse и base (dplyr, tidyr, base):

В этом я преобразую ваш df2 в кадр данных, который включает в себя все комбинации значений, которые вы хотите принять ((E или NA) & (T или NA) & (M или NA)), а затем я выполняю внутреннее соединение с этим полный набор. Существуют и другие способы создания фрейма данных со всеми возможными комбинациями, но этот способ довольно легко использует тидир.

library(dplyr)
library(tidyr)

id <- c("id1", "id2", "id3", "id4")
A <- c("E", "F", "G", NA)
B <- c("T", NA, "N", "T")
C <- c(NA, "T", "U", NA)

df <- data.frame(A, B, C, stringsAsFactors = FALSE)

df2 <- data.frame(A = "E", B = "T", C = "M",stringsAsFactors = FALSE)

df2_expanded <- df2 %>%
  rowwise() %>%
  mutate(combinations = list(expand.grid(A = c(A,NA),B = c(B,NA),C = c(C,NA),stringsAsFactors = FALSE))) %>%
  select(-A,-B,-C) %>%
  unnest(combinations)

# A tibble: 8 x 3
#   A     B     C    
# <chr> <chr> <chr>
# 1 E     T     M    
# 2 NA    T     M    
# 3 E     NA    M    
# 4 NA    NA    M    
# 5 E     T     NA   
# 6 NA    T     NA   
# 7 E     NA    NA   
# 8 NA    NA    NA   

df %>%
  inner_join(df2_expanded)

#      A B    C
# 1    E T <NA>
# 2 <NA> T <NA>
...