J'ai du mal à joindre des cadres de données avec dplyr, où je voudrais ignorer les NA.
Les données que j'ai sont assez volumineuses, mais une version simplifiée ressemble à:
Error: `by` can't contain join column `C` which is missing from LHS Call `rlang::last_error()` to see a backtrace
J'ai une entrée que je voudrais faire correspondre avec df, qui est par exemple:
result <- df %>% group_by(n = seq(n())) %>% do(modify_if(., is.na, ~NULL) %>% semi_join(df2, by = c("A", "B", "C"))) %>% ungroup %>% select(-n)
En conséquence, je voudrais obtenir toutes les lignes de df qui correspondent à df2, mais les NA doivent être ignorés. Le résultat devrait donc ressembler à ceci:
id A B C 1 id1 E T NA 2 id4 NA T NA
J'essayais de le faire avec semi_join, mais cela n'a pas fonctionné jusqu'à présent:
df2 <- data.frame(A = "E", B = "T", C = "M") A B C 1 E T M
3 Réponses :
Voici une solution avec un mélange de tidyverse et de base R. Je pense que c'est assez clair, mais je serais intéressé par une implémentation pure tidyverse qui n'est pas complètement artificielle.
L'idée est d'abord de tout développer entrées dans df
et df2
puis filtrer à travers toutes les colonnes en utilisant une boucle.
Les données:
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>
Code:
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)
Si vous n'avez besoin de le faire que pour un seul ensemble de valeurs, c'est probablement l'approche la plus simple:
d[A %in% c("E",NA) & B %in%c("T",NA) & C %in% c("M",NA),]
Un autre exemple utilisant tidyverse et base (dplyr, tidyr, base):
Dans ce cas, je convertis votre df2 en un dataframe qui inclut toutes les combinaisons de valeurs que vous souhaitez accepter ((E ou NA) & (T ou NA) ) & (M ou NA)) puis je fais une jointure intérieure avec cet ensemble complet. Il existe d'autres moyens de créer un dataframe de toutes les combinaisons possibles, mais celui-ci utilise tidyr assez facilement.
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>
Pouvez-vous expliquer un peu plus les résultats souhaités? Pourquoi les lignes
E T NA
etNA T NA
seraient renvoyées?Le B dans
df2
devrait-il être "T" au lieu de "M"?Comme d'autres l'ont dit, il semble que votre exemple soit un peu foiré. Dans tous les cas,
semi_join
n'est pas la réponse car il correspond de la même manière que les autres jointures de dplyr fonctionnent. Vous voudrez probablement simplement faire chaque combinaison (produit cartésien), puis filtrer sur au moins une des paires de colonnes ou pas de discordance.@thc vous avez raison, désolé pour ça
@svenhalvorson désolé, je ne comprends pas ce que vous voulez dire, pourriez-vous fournir un lien ou plus d'informations?
Il y a presque certainement une meilleure méthode que celle que j'ai en tête mais je commencerais par renommer toutes les colonnes d'un même bloc de données en A1, B2, C2 ... Ensuite, créez une colonne appelée `` factice '' dans chaque bloc de données qui correspond à la valeur 1. Fusionnez-les sur cette variable, créez des variables pour vérifier si
A == A1
ect. Enfin, utilisez une instructionapply
ou quelque chose comme ça pour vérifier si des valeurs de la ligne sontFALSE
et supprimez-les.Il semble que vous essayez de capturer toutes les lignes correspondantes qui pourraient correspondre à df2. Où NA est traité comme inconnu.