Hi all, I am having enormous problems with a loop that iterates over different levels in the factor wine.data$Time (levels T06, T09, and T12) and creates a PCA and graph for individuals at that time only. These graphs need to be accessible outside the loop, so I can combine them using ggpubr::ggarrange to produce a figure for a paper. The difficulty I am having is with the labels produced by ggrepel::geom_label_repel. Since the loop uses the variable i to set the level of wine.data$Time, when used outside the loop the labels produced by geom_label_repel are always for T12 (i.e., the last level run by the loop). Oddly, this only affects the labels: the title, set using ggtitle(paste0("PCA of wine.data observations at Time ", print(i))), shows the correct time, and the datapoints are for the correct time. Is there some way to get geom_label_repel to read current_rownames and store the values in the ggplot object, rather than to reload them every time I display or save the ggplot graph? Thanks! gavin, Code for reference. I can provide a sample dataset for reproducibility if needed. for(i in levels(wine.data$Time)){ print(i) wine.data.filt <- filter(wine.data,Time == i) current_rownames <- rownames(wine.data.filt) wine.data.filt.pca <- dudi.pca(wine.data.filt[3:11], nf=6, scannf=F) wine.data_quanti_sup <- wine.data.filt[,12, drop = FALSE] head(wine.data_quanti_sup) # Colour by Treatment assign(paste0("individ_", i), fviz_pca_ind(wine.data.filt.pca, geom.ind = "point", mean.point=F, col.ind = wine.data.filt$Treatment, ellipse.type = "confidence", legend.title = "Groups" )+ggtitle(paste0("PCA of wine.data observations at Time ", print(i))) + scale_colour_manual( values = c( "Control" = "#00A087FF", "Low" = "#3C5488FF", "High" = "#E64B35FF") ) + geom_label_repel(position ggpp::position_nudge_center(0.2, 0.1, 0, 0), box.padding = 0.5, max.overlaps = Inf, aes(label=current_rownames, colour=wine.data.filt$Treatment ) ) + scale_fill_manual( values = c( "Control" = "#00A087FF", "Low" = "#3C5488FF", "High" = "#E64B35FF") )) }
? Tue, 7 May 2024 16:57:14 +0200 gavin duley <gduley at gmail.com> ?????:> aes(label=current_rownames, > colour=wine.data.filt$Treatment > )As you've noticed, aes() remembers variables by their name and environment, not by value: str(ggplot2::aes(label = foo)) # List of 1 # $ label: language ~foo # <-- variable name recorded here # and here is the environment # ..- attr(*, ".Environment")=<environment: R_GlobalEnv> # - attr(*, "class")= chr "uneval" One way to get around the problem is to ensure that the variables live in different environments. Instead of making it a for loop, write a function that would accept `i` and return a plot instead of assigning it by name: makeplot <- function(i) { print(i) wine.data.filt <- filter(wine.data,Time == i) current_rownames <- rownames(wine.data.filt) wine.data.filt.pca <- dudi.pca(wine.data.filt[3:11], nf=6, scannf=F) wine.data_quanti_sup <- wine.data.filt[,12, drop = FALSE] return(fviz_pca_ind(wine.data.filt.pca, # <the rest of the plot expression is omitted> ) } individs <- lapply(levels(wine.data$Time), makeplot) individs[[1]] (In many languages, trying to use a variable as a variable name, while possible, usually means you need to consider some kind of nested data structure: https://perldoc.perl.org/perlfaq7#How-can-I-use-a-variable-as-a-variable-name? In R, this structure is a list.) Why does this work? Calling a function creates a new environment every time. The plots will all refer to the variable named current_rownames, but the environments will be different: attr((function() ggplot2::aes(label = foo))()$label, '.Environment') # <environment: 0x5650ffc2bfd8> attr((function() ggplot2::aes(label = foo))()$label, '.Environment') # <environment: 0x5650ffc5c128> attr((function() ggplot2::aes(label = foo))()$label, '.Environment') # <environment: 0x5650ffc8a3d8> Alternatively, supply a data= argument to geom_label_repel() and make your mapping = aes(...) reference variables from the data (which will be remembered), ignoring the environment (which is only referenced). Something like the following should work, untested: geom_label_repel( mapping = aes(label = current_rownames, colour = Treatment), data = data.frame( current_rownames = current_rownames, Treatment = wine.data.filt$Treatment ), # more arguments here ) -- Best regards, Ivan