带有 ggplots 的 for 循环生成具有相同值但不同标题的图形
for loop with ggplots produces graphs with identical values but different headings
我已经阅读了很多关于使用 ggplot 循环生成大量图表的文章,但找不到任何可以解释我的问题的文章...
我有一个数据框,我正在尝试遍历 92 列,为每一列创建一个新图表。我想将每个图保存为单独的 object。当我 运行 我的循环(下面的代码)并打印图表时,所有图表都是正确的。但是,当我用 assign() 更改 print() 命令时,图形不正确。标题正在按应有的方式更改,但是 graph-values 都是相同的(它们都是最终图表的所有值)。我发现这一点是因为当我使用 plot_grid() 生成 10 个图的图形时,图形标题和轴标签都是正确的,但值是相同的!
我的数据集很大,所以我提供了一个小数据集来说明下面。
示例数据名:
library(ggplot)
library(cowplot)
df <- as.data.frame(cbind(group=c(rep("A", 4), rep("B", 4)), a=sample(1:100, 8), b=sample(100:200, 8), c=sample(300:400, 8))) #make data frame
cols <- 2:4 #define columns for plots
for(i in 1:length(cols)){
df[,cols[i]] <- as.numeric(as.character(df[,cols[i]]))
} #convert columns to numeric
地块:
for (i in 1:length(cols)){
g <- ggplot(df, aes(x=group, y=df[,cols[i]])) +
geom_boxplot() +
ggtitle(colnames(df)[cols[i]])
print(g)
assign(colnames(df)[cols[i]], g) #generate an object for each plot
}
plot_grid(a, b, c)
我在想当ggplots作图时,它只渲染来自i的最终值的数据?或者类似的东西?有解决办法吗?
我希望这样做,因为我想制作很多图表,然后我想混合搭配图表。
谢谢!
我已经清理了您生成示例数据框的方式。
library(ggplot2)
library(cowplot)
df <- data.frame(group=c(rep("A", 4), rep("B", 4)),
a=sample(1:100, 8),
b=sample(100:200, 8),
c=sample(300:400, 8)) #make data frame
只需使用 data.frame() 就足够了。这使您的代码更清晰,并避免需要在 'for loop' 中进行所有 post 处理,以将您的数据框转换为数字并删除生成的因子 - 请注意 as.data.frame()如果您没有 'stringsAsFactors = FALSE' 并且 cbind() 倾向于默认因子,并且可以通过使用 cbind.data.frame() 而不是 cbind().[=14 来避免数字到字符的转换=]
我还重构了生成图的 'for loop'。您生成一个名为 'cols' (cols <- 2:4 ) 的整数列表,然后您重复该列表以从每一列数据生成您的图表。这是不必要的,我们可以在 for 语句条件中创建一个范围 - 'for (i in 2:ncol(df))' - 这只是重复从 2 到 4(数据框中的列数) - 从 2 开始需要避免第 1 列包含元数据。这是可取的,因为:
i) 当检查你的代码时,使用的条件是立即显而易见的,而不需要搜索你的代码的其余部分
ii) R 有许多 functions/parameters 与您的变量 'cols' 相似,最好避免混淆。
清理代码后,我们现在可以尝试找出错误的原因:
library(ggplot2)
library(cowplot)
df <- data.frame(group=c(rep("A", 4), rep("B", 4)),
a=sample(1:100, 8),
b=sample(100:200, 8),
c=sample(300:400, 8)) #make data frame
for (i in 2:ncol(df)){
g <- ggplot(df, aes(x=group, y=df[,i])) +
geom_boxplot() +
ggtitle(colnames(df)[i])
print(g)
assign(colnames(df)[i], g) #generate an object for each plot
}
您的代码无法正常工作的原因并不是很明显。 Imo 的建议是有价值的。将您的绘图保存到列表中可以防止您的环境因对象而变得混乱,但它不会解决此错误。原因是不直观的,需要深入了解 assign() 函数的计算方式。请参阅提供的答案 by Konrad Rudolph。以下应该可以工作并保留原始代码的样式。正如康拉德在他的回答中建议的那样,"R" 可能更喜欢使用 lapply。请注意,我们已经给出了 for loop 局部作用域,现在我们在局部重新定义了 i 。以前,循环中生成的 i 的最后一个值用于生成通过 assign() 函数创建的每个对象。请注意使用 <<- 将 g 分配给全局环境。
for (i in 2:ncol(df))
local({
i <- i
g <<- ggplot(df, aes(x=group, y=df[,i])) +
geom_boxplot() +
ggtitle(colnames(df)[i])
print(i)
print(g)
assign(colnames(df)[i], g, pos =1) #generate an object for each plot
})
plot_grid(a, b, c)
你欠我一杯酒
有两种标准方法可以处理这个问题:
1- 使用长格式 data.frame
2-使用aes_string
引用宽格式的变量名data.frame
以下是可能策略的示例。
library(ggplot2)
library(gridExtra)
# data from other answer
df <- data.frame(group=c(rep("A", 4), rep("B", 4)),
a=sample(1:100, 8),
b=sample(100:200, 8),
c=sample(300:400, 8))
## first method: long format
m <- reshape2::melt(df, id = "group")
p <- ggplot(m, aes(x=group, y=value)) +
geom_boxplot()
pl <- plyr::dlply(m, "variable", function(.d) p %+% .d + ggtitle(unique(.d$variable)))
grid.arrange(grobs=pl)
## second method: keep wide format
one_plot <- function(col = "a") ggplot(df, aes_string(x="group", y=col)) + geom_boxplot() + ggtitle(col)
pl <- plyr::llply(colnames(df)[-1], one_plot)
grid.arrange(grobs=pl)
## third method: more explicit looping
pl <- vector("list", length = ncol(df)-1)
for(ii in seq_along(pl)){
.col <- colnames(df)[-1][ii]
.p <- ggplot(df, aes_string(x="group", y=.col)) + geom_boxplot() + ggtitle(.col)
pl[[ii]] <- .p
}
grid.arrange(grobs=pl)
有时,当在 function/for 循环中包装 ggplot 调用时,会面临局部变量的问题(这里不是这种情况,如果使用 aes_string
)。在这种情况下,可以 define a local environment。
请注意,使用 aes(y=df[,i])
这样的结构可能看起来有效,但可能会产生非常错误的结果。 ,data.frame 将针对每个面板分成不同的组,如果将数值直接传递给 aes()
而不是变量名称,则此子集可能无法对正确的数据进行分组。
我已经阅读了很多关于使用 ggplot 循环生成大量图表的文章,但找不到任何可以解释我的问题的文章...
我有一个数据框,我正在尝试遍历 92 列,为每一列创建一个新图表。我想将每个图保存为单独的 object。当我 运行 我的循环(下面的代码)并打印图表时,所有图表都是正确的。但是,当我用 assign() 更改 print() 命令时,图形不正确。标题正在按应有的方式更改,但是 graph-values 都是相同的(它们都是最终图表的所有值)。我发现这一点是因为当我使用 plot_grid() 生成 10 个图的图形时,图形标题和轴标签都是正确的,但值是相同的!
我的数据集很大,所以我提供了一个小数据集来说明下面。
示例数据名:
library(ggplot)
library(cowplot)
df <- as.data.frame(cbind(group=c(rep("A", 4), rep("B", 4)), a=sample(1:100, 8), b=sample(100:200, 8), c=sample(300:400, 8))) #make data frame
cols <- 2:4 #define columns for plots
for(i in 1:length(cols)){
df[,cols[i]] <- as.numeric(as.character(df[,cols[i]]))
} #convert columns to numeric
地块:
for (i in 1:length(cols)){
g <- ggplot(df, aes(x=group, y=df[,cols[i]])) +
geom_boxplot() +
ggtitle(colnames(df)[cols[i]])
print(g)
assign(colnames(df)[cols[i]], g) #generate an object for each plot
}
plot_grid(a, b, c)
我在想当ggplots作图时,它只渲染来自i的最终值的数据?或者类似的东西?有解决办法吗?
我希望这样做,因为我想制作很多图表,然后我想混合搭配图表。
谢谢!
我已经清理了您生成示例数据框的方式。
library(ggplot2)
library(cowplot)
df <- data.frame(group=c(rep("A", 4), rep("B", 4)),
a=sample(1:100, 8),
b=sample(100:200, 8),
c=sample(300:400, 8)) #make data frame
只需使用 data.frame() 就足够了。这使您的代码更清晰,并避免需要在 'for loop' 中进行所有 post 处理,以将您的数据框转换为数字并删除生成的因子 - 请注意 as.data.frame()如果您没有 'stringsAsFactors = FALSE' 并且 cbind() 倾向于默认因子,并且可以通过使用 cbind.data.frame() 而不是 cbind().[=14 来避免数字到字符的转换=]
我还重构了生成图的 'for loop'。您生成一个名为 'cols' (cols <- 2:4 ) 的整数列表,然后您重复该列表以从每一列数据生成您的图表。这是不必要的,我们可以在 for 语句条件中创建一个范围 - 'for (i in 2:ncol(df))' - 这只是重复从 2 到 4(数据框中的列数) - 从 2 开始需要避免第 1 列包含元数据。这是可取的,因为:
i) 当检查你的代码时,使用的条件是立即显而易见的,而不需要搜索你的代码的其余部分
ii) R 有许多 functions/parameters 与您的变量 'cols' 相似,最好避免混淆。
清理代码后,我们现在可以尝试找出错误的原因:
library(ggplot2)
library(cowplot)
df <- data.frame(group=c(rep("A", 4), rep("B", 4)),
a=sample(1:100, 8),
b=sample(100:200, 8),
c=sample(300:400, 8)) #make data frame
for (i in 2:ncol(df)){
g <- ggplot(df, aes(x=group, y=df[,i])) +
geom_boxplot() +
ggtitle(colnames(df)[i])
print(g)
assign(colnames(df)[i], g) #generate an object for each plot
}
您的代码无法正常工作的原因并不是很明显。 Imo 的建议是有价值的。将您的绘图保存到列表中可以防止您的环境因对象而变得混乱,但它不会解决此错误。原因是不直观的,需要深入了解 assign() 函数的计算方式。请参阅提供的答案
for (i in 2:ncol(df))
local({
i <- i
g <<- ggplot(df, aes(x=group, y=df[,i])) +
geom_boxplot() +
ggtitle(colnames(df)[i])
print(i)
print(g)
assign(colnames(df)[i], g, pos =1) #generate an object for each plot
})
plot_grid(a, b, c)
你欠我一杯酒
有两种标准方法可以处理这个问题:
1- 使用长格式 data.frame
2-使用aes_string
引用宽格式的变量名data.frame
以下是可能策略的示例。
library(ggplot2)
library(gridExtra)
# data from other answer
df <- data.frame(group=c(rep("A", 4), rep("B", 4)),
a=sample(1:100, 8),
b=sample(100:200, 8),
c=sample(300:400, 8))
## first method: long format
m <- reshape2::melt(df, id = "group")
p <- ggplot(m, aes(x=group, y=value)) +
geom_boxplot()
pl <- plyr::dlply(m, "variable", function(.d) p %+% .d + ggtitle(unique(.d$variable)))
grid.arrange(grobs=pl)
## second method: keep wide format
one_plot <- function(col = "a") ggplot(df, aes_string(x="group", y=col)) + geom_boxplot() + ggtitle(col)
pl <- plyr::llply(colnames(df)[-1], one_plot)
grid.arrange(grobs=pl)
## third method: more explicit looping
pl <- vector("list", length = ncol(df)-1)
for(ii in seq_along(pl)){
.col <- colnames(df)[-1][ii]
.p <- ggplot(df, aes_string(x="group", y=.col)) + geom_boxplot() + ggtitle(.col)
pl[[ii]] <- .p
}
grid.arrange(grobs=pl)
有时,当在 function/for 循环中包装 ggplot 调用时,会面临局部变量的问题(这里不是这种情况,如果使用 aes_string
)。在这种情况下,可以 define a local environment。
请注意,使用 aes(y=df[,i])
这样的结构可能看起来有效,但可能会产生非常错误的结果。 aes()
而不是变量名称,则此子集可能无法对正确的数据进行分组。