3
votes

Joindre les données mais ignorer les valeurs manquantes

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


7 commentaires

Pouvez-vous expliquer un peu plus les résultats souhaités? Pourquoi les lignes E T NA et NA 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 instruction apply ou quelque chose comme ça pour vérifier si des valeurs de la ligne sont FALSE et supprimez-les.


Il semble que vous essayez de capturer toutes les lignes correspondantes qui pourraient correspondre à df2. Où NA est traité comme inconnu.


3 Réponses :


1
votes

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)


0 commentaires

1
votes

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),]


0 commentaires

0
votes

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>


0 commentaires