【问题标题】:Assigning plot to a variable in a loop将绘图分配给循环中的变量
【发布时间】:2019-12-13 10:04:05
【问题描述】:

我正在尝试创建 2 个线图。

但我注意到,使用for 循环会生成两个带有y=mev2 的图(而不是一个基于y=mev1 的图和另一个基于y=mev2 的图)。

下面的代码显示了这里的观察结果。

mev1 <- c(1,3,7)
mev2 <- c(9,8,2)
Period <- c(1960, 1970, 1980)
df <- data.frame(Period, mev1, mev2)

library(ggplot2)
# Method 1: Creating plot1 and plot2 without using "for" loop (hard-code)
plot1 <- ggplot(data = df, aes(x=Period, y=unlist(as.list(df[2])))) + geom_line()
plot2 <- ggplot(data = df, aes(x=Period, y=unlist(as.list(df[3])))) + geom_line()

# Method 2: Creating plot1 and plot2 using "for" loop
for (i in 1:2) {
   y_var <- unlist(as.list(df[i+1]))
   assign(paste("plot", i, sep = ""), ggplot(data = df, aes(x=Period, y=y_var)) + geom_line())
}

似乎这是由于ggplot() 的一些我不知道的工作方式。

问题:

  • 如果要使用方法二,应该如何修改逻辑?
  • 人们说使用assign() 不是“R 风格”,所以我想知道有什么替代方法可以做到这一点?比如说,使用list?

【问题讨论】:

  • 实现方法 - 将宽转换为长并绘制 ggplot(reshape2::melt(df, "Period"), aes(Period, value, color = variable)) + geom_line()

标签: r ggplot2


【解决方案1】:

未添加tidyverse 命令的一个可能答案是:

library(ggplot2)

y_var <- colnames(df)
for (i in 1:2) {
  assign(paste("plot", i, sep = ""),
         ggplot(data = df, aes_string(x=y_var[1], y=y_var[1 + i])) +
           geom_line())
}

plot1
plot2

您可以使用aes_string。希望对你有帮助。

编辑 1

如果你想把你的地块放在一个列表中,你可以使用这个:

初始化你的列表:

n <- 2 # number of plots
list_plot <- vector(mode = "list", length = n)
names(list_plot) <- paste("plot", 1:n)

填写:

for (i in 1:2) {
  list_plot[[i]] <- ggplot(data = df, aes_string(x=y_var[1], y=y_var[1 + i])) +
           geom_line()
}

显示:

list_plot[[1]]
list_plot[[2]]

【讨论】:

  • 您似乎正在使用外部包中的功能,最好将这些包放在您的答案中。
【解决方案2】:

对于不同“地块”中的线条,可以用facet_wrap()简化:

library(tidyverse)
df %>% 
gather(variable, value, -c(Period)) %>% # wide to long format
ggplot(aes(Period, value)) + geom_line() + facet_wrap(vars(variable))

如果需要,你也可以把它放在一个循环中,并将结果存储在一个列表中:

# empty list
listed <- list()
# fill the list with the plots
for (i in c(2:3)){
     listed[[i-1]] <- df[,-i]  %>%
                      gather(variable, value, -c(Period)) %>% 
                      ggplot(aes(Period, value)) + geom_line()
                 }

# to get the plots
listed[[1]]
listed[[2]]

【讨论】:

  • 在您的循环方法中,您的 y 轴标签只是“值”而不是“mev1”和“mev2”。并且列表元素没有命名。出于这个原因,我会采用 Rémi Coulauds 的方法。
【解决方案3】:

为什么要 2 个独立的地块? ggplots 的方法是获取长格式数据,然后进行绘图。

library(tidyverse)

df %>%
  pivot_longer(cols = -Period) %>%
  ggplot() + aes(Period, value, color = name) + geom_line()

【讨论】:

    【解决方案4】:

    这是使用 functionlapply 的替代方法。我知道您问过如何使用循环来解决这个问题。不过,我认为考虑这种方法可能很有用。

    library(ggplot2)
    mev1 <- c(1,3,7)
    mev2 <- c(9,8,2)
    Period <- c(1960, 1970, 1980)
    df <- data.frame(Period, mev1, mev2)
    
    myplot <- function(yvar){
      plot <- ggplot(df, aes(Period, !!sym(yvar))) + geom_line()
      return(plot)
    }
    
    colnames <- c("mev1","mev2")
    list <- lapply(colnames, myplot)
    names(list) <- paste0("plot_", colnames)
    # Alternativing naming: names(list) <- paste0("plot", 1:2)   
    

    使用这种方法,您可以轻松地将绘图功能应用于您喜欢的任何列。您可以按名称指定列,这可能比按位置指定更好。绘图保存在一个列表中,然后使用names 属性对其进行命名。在我的示例中,我将图命名为 plot_mev1plot_mev2。但是你可以很容易地适应一些其他的命名。例如。写names(list) &lt;- paste0("plot", 1:2)得到plot1plot2

    请注意,我在 ggplot 调用中使用了!!sym()。这本质上是 aes_string 的替代品,后者在 Rémi Coulaud 的回答中使用。通过这种方式,即使在函数上下文或循环上下文中,ggplot 也可以理解“mev1”是数据集的列,而不仅仅是文本字符串

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-07
      • 1970-01-01
      • 1970-01-01
      • 2023-04-05
      • 1970-01-01
      相关资源
      最近更新 更多