web-dev-qa-db-fra.com

Dplyr rejoindre le par = (a = b), où a et b sont des variables contenant des chaînes?

J'essaie d'effectuer une jointure interne à l'aide de dplyr à l'aide de deux tables et je pense que des règles d'évaluation non standard m'entraînent. Lorsque vous utilisez l'argument by = ("a" = "b"), tout fonctionne comme prévu lorsque "a" et "b" sont des chaînes réelles. Voici un exemple de jouet qui fonctionne:

library(dplyr)
data(iris)

inner_join(iris, iris, by=c("Sepal.Length" = "Sepal.Width"))

Mais disons que je mettais inner_join dans une fonction:

library(dplyr)
data(iris)

myfn <- function(xname, yname) {
    data(iris)
    inner_join(iris, iris, by=c(xname = yname))
}

myfn("Sepal.Length", "Sepal.Width")

Cela renvoie l'erreur suivante:

Error: cannot join on columns 'xname' x 'Sepal.Width': index out of bounds

Je soupçonne que je pourrais faire quelque chose d'extraordinaire, de déparser, de citer ou de ne pas citer, pour que cela fonctionne, mais je suis un peu confus sur ces détails.

24
Peter

Vous pouvez utiliser 

myfn <- function(xname, yname) {
    data(iris)
    inner_join(iris, iris, by=setNames(yname, xname))
}

La syntaxe suggérée dans la documentation ?inner_join de 

by = c("a"="b")   # same as by = c(a="b")

est légèrement trompeur car ces deux valeurs ne sont pas des valeurs de caractère correctes. Vous êtes en fait créé un vecteur de caractère nommé. Définir de manière dynamique les valeurs à gauche du signe égal est différent de celles à droite. Vous pouvez utiliser setNames() pour définir les noms du vecteur de manière dynamique.

28
MrFlick

Je sais que je suis en retard à la fête, mais que dire de:

myfn <- function(byvar) {
  data(iris)
  inner_join(iris, iris, by=byvar)
}

De cette façon, vous pouvez faire ce que vous voulez avec:

myfn(c("Sepal.Length"="Sepal.Width"))
2
Felipe Gerard

J'ai fait face à un défi presque identique à celui de @Peter, mais je devais passer plusieurs ensembles différents de paramètres de jointure by = à la fois. J'ai choisi d'utiliser la fonction map() du paquet tidyverse, purrr.

Ceci est le sous-ensemble de la tidyverse que j'ai utilisé.

library(magrittr)
library(dplyr)
library(rlang)
library(purrr)

D'abord, j'ai adapté myfn pour utiliser map() pour le cas posté par Peter. Le commentaire de 42 et la réponse de Felipe Gerard expliquent clairement que l'argument by peut prendre un vecteur nommé. map() nécessite une liste sur laquelle itérer.

    myfn_2 <- function(xname, yname) {
      by_names <- list(setNames(nm = xname, yname ))

      data(iris)

      # map() returns a single-element list. We index to retrieve dataframe.

      map( .x = by_names, 
           .f = ~inner_join(x = iris, 
                            y = iris, 
                            by = .x)) %>% 
        `[[`(1)
    }

myfn_2("Sepal.Length", "Sepal.Width")

J'ai constaté que je n'avais pas besoin de quo_name()/!! pour créer la fonction.

Ensuite, j'ai adapté la fonction pour prendre une liste de paramètres by. Pour chaque by_i dans by_grps, nous pourrions étendre x et y afin d’ajouter des valeurs nommées à rejoindre.

by_grps <- list(  by_1 = list(x = c("Sepal.Length"), y = c("Sepal.Width")), 
                  by_2 = list(x = c("Sepal.Width"), y = c("Petal.Width"))
                )

myfn_3 <- function(by_grps_list, nm_dataset) {
  by_named_vectors_list <- lapply(by_grps_list, 
                                  function(by_grp) setNames(object = by_grp$y,
                                                            nm = by_grp$x))
  map(.x = by_named_vectors_list, 
      .f = ~inner_join(nm_dataset, nm_dataset, by = .x))
}

myfn_3(by_grps, iris)
0
SoFarther

J'aime la réponse de MrFlick et l'addendum de Fber, mais je préfère structure. Pour moi, setNames se sent comme quelque chose au bout d'un tuyau, pas comme un constructeur à la volée. Sur une autre note, setNames et structure permettent l’utilisation de variables dans l’appel de fonction.

myfn <- function(xnames, ynames) {
  data(iris)
  inner_join(iris, iris, by = structure(names = xnames, .Data = ynames))
}

x <- "Sepal.Length"

myfn(x, "Sepal.Width")

Un argument de vecteur nommé rencontrerait des problèmes ici:

myfn <- function(byvars) {
  data(iris)
  inner_join(iris, iris, by = byvars)
}

x <- "Sepal.Length"

myfn(c(x = "Sepal.Width"))

Vous pouvez toutefois résoudre ce problème en utilisant setNames ou structure dans l'appel de fonction.

0
deSKase