Question Dplyr se joint à par = (a = b), où a et b sont des variables contenant des chaînes?


J'essaie de réaliser une jointure interne de deux tables à l'aide de dplyr, et je pense que les règles d'évaluation non standard m'emballent. 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 retourne 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 de compliqué pour faire fonctionner cette expression fantaisiste, déparser, citer ou non, mais je suis un peu trouble sur ces détails.


23
2018-02-08 20:34


origine


Réponses:


Vous pouvez utiliser

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

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

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

est légèrement trompeuse car ces deux valeurs ne sont pas des valeurs de caractères correctes. Vous avez effectivement créé un vecteur de caractère nommé. Pour définir dynamiquement les valeurs à gauche du signe égal est différent de ceux à droite. Vous pouvez utiliser setNames() définir dynamiquement les noms du vecteur.


24
2018-02-08 20:39



Je sais que je suis en retard à la fête, mais que diriez-vous 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
2017-07-24 14:21



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

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

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

D'abord, je me suis adapté myfn utiliser map() pour le cas posté par Peter. Le commentaire de 42 et la réponse de Felipe Gerard ont clairement montré que le by l'argument 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 trouvé que je n'avais pas besoin quo_name() / !! dans la construction de la fonction.

Ensuite, j'ai adapté la fonction pour prendre une liste de by paramètres. Pour chaque by_i dans by_grps, nous pourrions étendre x et y ajouter des valeurs nommées sur lesquelles se joindre.

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
2018-06-21 22:53