web-dev-qa-db-fra.com

Adressage de x et y dans aes par un nombre variable

Je dois dessiner un diagramme de dispersion avec les variables d'adressage par leur numéro de colonne plutôt que par leur nom, c'est-à-dire plutôt que par ggplot(dat, aes(x=Var1, y=Var2)), il me faut quelque chose comme ggplot(dat, aes(x=dat[,1], y=dat[,2])). (Je dis «quelque chose» parce que ce dernier ne fonctionne pas).

Voici mon code:

showplot1<-function(indata, inx, iny){
  dat<-indata
  print(nrow(dat)); # this is just to show that object 'dat' is defined
  p <- ggplot(dat, aes(x=dat[,inx], y=dat[,iny]))
  p + geom_point(size=4, alpha = 0.5)
}

testdata<-data.frame(v1=rnorm(100), v2=rnorm(100), v3=rnorm(100), v4=rnorm(100), v5=rnorm(100))
showplot1(indata=testdata, inx=2, iny=3)
# Error in eval(expr, envir, enclos) : object 'dat' not found
26
Vasily A

Une variante de la réponse de @ Shadow utilisant les nouvelles fonctionnalités de ggplot2 V3.0.0:

showplot <- function(indata, inx, iny){
  nms <- names(indata)
  x <- nms[inx]
  y <- nms[iny]
  p <- ggplot(indata, aes(x = !!ensym(x), y = !!ensym(y)))
  p + geom_point(size=4, alpha = 0.5)
}   

testdata <- data.frame(v1=rnorm(100), v2=rnorm(100), v3=rnorm(100), v4=rnorm(100), v5=rnorm(100))
names(testdata) <- c("a-b", "c-d", "e-f", "g-h", "i-j")
showplot(indata=testdata, inx=2, iny=3)

ensym crée un symbole à partir de la chaîne contenue dans une variable (nous devons donc d'abord créer ces variables au début de la fonction), puis !! le cite, ce qui signifie que cela fonctionnera comme si vous aviez alimenté les noms bruts de la fonction.

!! fonctionne uniquement dans le contexte de fonctions conçues pour le prendre en charge, généralement des fonctions ordonnées, sinon cela signifie simplement "ne pas" (équivalent à as.logical).

2
Moody_Mudskipper

Votre problème est que aes ne connaît pas l'environnement de votre fonction et qu'elle ne fait que regarder dans global environment. Ainsi, la variable dat déclarée dans la fonction est non visible à ggplot2 'aes fonction à moins que vous ne la transmettiez explicitement as:

showplot1<-function(indata, inx, iny) {
    dat <- indata
    p <- ggplot(dat, aes(x=dat[,inx], y=dat[,iny]), environment = environment())
    p <- p + geom_point(size=4, alpha = 0.5)
    print(p)
}

Notez l'argument environment = environment() dans la commande ggplot(). Cela devrait fonctionner maintenant.

23
Arun

Essayer:

showplot1 <- function(indata, inx, iny) {
    x <- names(indata)[inx] 
    y <- names(indata)[iny] 
    p <- ggplot(indata, aes_string(x = x, y = y))
    p + geom_point(size=4, alpha = 0.5)
}

Edité pour montrer ce qui se passe - aes_string utilise des arguments cités, les noms les obtiennent en utilisant vos numéros. 

13
alexwhan

Je suggère fortement d'utiliser aes_q au lieu de passer les vecteurs à aes (la réponse de @ Arun). Cela peut sembler un peu plus compliqué, mais il est plus flexible, par exemple lorsque mise à jour des données.

showplot1 <- function(indata, inx, iny){
  p <- ggplot(indata, 
              aes_q(x = as.name(names(indata)[inx]), 
                    y = as.name(names(indata)[iny])))
  p + geom_point(size=4, alpha = 0.5)
}

Et voici la raison pour laquelle c'est préférable:

# test data (using non-standard names)
testdata<-data.frame(v1=rnorm(100), v2=rnorm(100), v3=rnorm(100), v4=rnorm(100), v5=rnorm(100))
names(testdata) <- c("a-b", "c-d", "e-f", "g-h", "i-j")
testdata2 <- data.frame(v1=rnorm(100), v2=rnorm(100), v3=rnorm(100), v4=rnorm(100), v5=rnorm(100))
names(testdata2) <- c("a-b", "c-d", "e-f", "g-h", "i-j")

# works
showplot1(indata=testdata, inx=2, iny=3)
# this update works in the aes_q version
showplot1(indata=testdata, inx=2, iny=3) %+% testdata2

Remarque: À partir de ggplot2 v2.0.0 aes_q() a été remplacé par aes_() pour être cohérent avec les versions SE des fonctions NSE dans d'autres packages.

13
shadow

Pour être complet, je pense qu'il est plus prudent d'utiliser des noms de colonne plutôt que des index car les positions de colonne dans un cadre de données peuvent être modifiées, ce qui entraîne des résultats inattendus. 

La fonction plot_duo ci-dessous (tirée de cette réponse ) peut utiliser l'entrée sous forme de chaînes ou de noms de colonnes nues 

library(rlang)
library(purrr)
library(dplyr)
library(ggplot2)

theme_set(theme_classic(base_size = 14))
set.seed(123456)
testdata <- data.frame(v1 = rnorm(100), v2 = rnorm(100), v3 = rnorm(100), 
                       v4 = rnorm(100), v5 = rnorm(100))

plot_duo <- function(df, plot_var_x, plot_var_y) {

  # check if input is character or bare column name to 
  # use sym() or enquo() accordingly
  if (is.character(plot_var_x)) {
    print('character column names supplied, use ensym()')
    plot_var_x <- ensym(plot_var_x)
  } else {
    print('bare column names supplied, use enquo()')
    plot_var_x <- enquo(plot_var_x)
  }

  if (is.character(plot_var_y)) {
    plot_var_y <- ensym(plot_var_y)
  } else {
    plot_var_y <- enquo(plot_var_y)
  }

  # unquote the variables using !! (bang bang) so ggplot can evaluate them
  pts_plt <- ggplot(df, aes(x = !! plot_var_x, y = !! plot_var_y)) + 
    geom_point(size = 4, alpha = 0.5)

  return(pts_plt)
}

Appliquer la fonction plot_duo sur les colonnes à l'aide de purrr::map()

### use character column names
plot_vars1 <- names(testdata)
plt1 <- plot_vars1 %>% purrr::map(., ~ plot_duo(testdata, .x, "v1"))
#> [1] "character column names supplied, use ensym()"
#> [1] "character column names supplied, use ensym()"
#> [1] "character column names supplied, use ensym()"
#> [1] "character column names supplied, use ensym()"
#> [1] "character column names supplied, use ensym()"

str(plt1, max.level = 1)
#> List of 5
#>  $ :List of 9
#>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"
#>  $ :List of 9
#>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"
#>  $ :List of 9
#>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"
#>  $ :List of 9
#>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"
#>  $ :List of 9
#>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"

# test plot
plt1[[3]]

### use bare column names
# Ref: https://stackoverflow.com/a/49834499/
plot_vars2 <- alist(v2, v3, v4)
plt2 <- plot_vars2 %>% purrr::map(., ~ plot_duo(testdata, .x, alist(v1)))
#> [1] "bare column names supplied, use enquo()"
#> [1] "bare column names supplied, use enquo()"
#> [1] "bare column names supplied, use enquo()"

str(plt2, max.level = 1)
#> List of 3
#>  $ :List of 9
#>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"
#>  $ :List of 9
#>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"
#>  $ :List of 9
#>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"

plt1[[2]]

Créé le 2019-02-18 par le paquet reprex (v0.2.1.9000)

1
Tung

solution provisoire que j'ai trouvée pour le moment:

showplot1<-function(indata, inx, iny){
  dat<-data.frame(myX=indata[,inx], myY=indata[,iny])
  print(nrow(dat)); # this is just to show that object 'dat' is defined
  p <- ggplot(dat, aes(x=myX, y=myY))
  p + geom_point(size=4, alpha = 0.5)
}

Mais je n'aime pas vraiment ça parce que dans mon vrai code, j'ai besoin d'autres colonnes de indata et ici je devrai toutes les définir explicitement dans dat<-...

0
Vasily A