web-dev-qa-db-fra.com

dplyr join définit les valeurs NA

Puis-je définir une valeur de "remplissage" pour NA dans la jointure dplyr? Par exemple, dans la jointure, définissez que toutes les valeurs NA doivent être 1?

require(dplyr)
lookup <- data.frame(cbind(c("USD","MYR"),c(0.9,1.1)))
names(lookup) <- c("rate","value")
fx <- data.frame(c("USD","MYR","USD","MYR","XXX","YYY"))
names(fx)[1] <- "rate"
left_join(x=fx,y=lookup,by=c("rate"))

Le code ci-dessus créera NA pour les valeurs "XXX" et "YYY". Dans mon cas, je rejoins un grand nombre de colonnes et il y aura beaucoup de non-correspondances. Toutes les non-correspondances doivent avoir la même valeur. Je sais que je peux le faire en plusieurs étapes, mais la question est: tout peut-il être fait en une seule? Merci!

19
Triamus

Tout d'abord, je voudrais recommander de ne pas utiliser la combinaison data.frame(cbind(...)). Voici pourquoi: cbind crée un matrix par défaut si vous ne lui passez que des vecteurs atomiques. Et les matrices dans R ne peuvent avoir qu'un seul type de données (pensez aux matrices comme un vecteur avec un attribut de dimension, c'est-à-dire le nombre de lignes et de colonnes). Par conséquent, votre code

cbind(c("USD","MYR"),c(0.9,1.1))

crée une matrice de caractères:

str(cbind(c("USD","MYR"),c(0.9,1.1)))
# chr [1:2, 1:2] "USD" "MYR" "0.9" "1.1"

bien que vous vous attendiez probablement à une trame de données finale avec une colonne de caractère ou de facteur (taux) et une colonne numérique (valeur). Mais ce que vous obtenez est:

str(data.frame(cbind(c("USD","MYR"),c(0.9,1.1))))
#'data.frame':  2 obs. of  2 variables:
# $ X1: Factor w/ 2 levels "MYR","USD": 2 1
# $ X2: Factor w/ 2 levels "0.9","1.1": 1 2

car les chaînes (caractères) sont converties en facteurs lors de l'utilisation de data.frame par défaut (vous pouvez contourner cela en spécifiant stringsAsFactors = FALSE dans l'appel data.frame()).

Je suggère l'approche alternative suivante pour créer les exemples de données (notez également que vous pouvez facilement spécifier les noms de colonne dans le même appel):

lookup <- data.frame(rate = c("USD","MYR"), 
                     value = c(0.9,1.1))

fx <- data.frame(rate = c("USD","MYR","USD","MYR","XXX","YYY"))

Maintenant, pour votre question réelle, si je comprends bien, vous voulez remplacer tous les NAs par un 1 Dans les données jointes. Si c'est correct, voici une fonction personnalisée utilisant left_join Et mutate_each Pour ce faire:

library(dplyr)
left_join_NA <- function(x, y, ...) {
  left_join(x = x, y = y, by = ...) %>% 
    mutate_each(funs(replace(., which(is.na(.)), 1)))
}

Vous pouvez maintenant l'appliquer à vos données comme ceci:

> left_join_NA(x = fx, y = lookup, by = "rate")
#  rate value
#1  USD   0.9
#2  MYR   1.1
#3  USD   0.9
#4  MYR   1.1
#5  XXX   1.0
#6  YYY   1.0
#Warning message:
#joining factors with different levels, coercing to character vector 

Notez que vous vous retrouvez avec une colonne de caractères (taux) et une colonne numérique (valeur) et toutes les NA sont remplacées par 1.

str(left_join_NA(x = fx, y = lookup, by = "rate"))
#'data.frame':  6 obs. of  2 variables:
# $ rate : chr  "USD" "MYR" "USD" "MYR" ...
# $ value: num  0.9 1.1 0.9 1.1 1 1
17
docendo discimus

Si vous utilisez dplyr de toute façon, vous pourriez tout aussi bien profiter de dplyr::coalesce, et utilisez la syntaxe dplyr pour y passer un 1 ou 0. Je pense que ça a l'air sympa ...

... %>%
mutate_if(is.numeric,coalesce,0)

Où le 0 est l'argument passé à dplyr::coalesce pour remplacer les NA.

Dans l'exemple de la question, il existe des cadres de données avec des facteurs. Je suis convaincu que l'on ne disposerait pas de taux de change comme facteurs ou d'un autre vecteur dans lequel vous remplaceriez NA par zéro, alors j'avance et j'ajoute cette étape ci-dessous juste pour rendre la réponse exécutable après l'exemple fourni.

# replace NAs with zeros for all numeric columns
#
# ... code from question above
left_join(x=fx,y=lookup,by=c("rate")) %>%
    # ignore if factors in value column are because it's a toy example
    mutate(value = as.numeric(as.character(value))) %>%
    # the good stuff here
    mutate_if(is.numeric,coalesce,0)
8
Rafael Zayas

Je suis tombé sur le même problème avec dplyr et j'ai écrit une petite fonction qui a résolu mon problème. (la solution nécessite tidyr et dplyr)

left_join0 <- function(x, y, fill = 0L){
  z <- left_join(x, y)
  tmp <- setdiff(names(z), names(x))
  z <- replace_na(z, setNames(as.list(rep(fill,   length(tmp))), tmp))
  z
}

Réponse initiale à: R Jointure externe gauche avec remplissage 0 au lieu de NA tout en conservant des NA valides dans le tableau de gauche

2
Fernando Macedo