在R中使用循环绘制多个图形

18

我仍在努力理解如何在R中使用循环绘图。我想根据下面数据框中x_1列中的不同名称,绘制z_1与z_2列(任何可视化数据的图表都可以)。

x_1 <- c("A1", "A1","A1", "B10", "B10", "B10","B10", "C100", "C100", "C100")


z_1 <- rnorm(10, 70) 

z_2 <- rnorm(10, 1.7)

A <- data.frame(x_1, z_1, z_2)

因此,我希望最终得到三个不同的图形:一个为A1类别,一个为B10类别,另一个为C100类别。我可以使用三个不同的代码来实现这一点,但我想能够使用循环或任何其他单个代码在同一页上执行所有三个图形。实际上,我有一个大数据集(4,000行),并希望在一页上绘制几个ID(比如一页上绘制5个)。

希望这样说得清楚。感谢您的帮助。

以下是我尝试分别绘制它们的方法:

对于A1:

data_A1 <- A[which(A$x_1 == "A1"), ]
plot(data_A1$z_2, data_A1$z_1)

我也尝试过类似的方法,但是出现了错误信息。

for ( i in A$x_1[[i]]){

plot(A[which(A$x_1==A$x_1[[i]]), ], aspect = 1)
}

3
如果您展示出您迄今为止所做出的实际尝试,并解释它们为什么不起作用,我可能会被引诱去提供帮助。 - joran
@joran,我会编辑我的帖子以包含我至今的尝试。谢谢。 - John_dydx
1
虽然是小事,但你犯了一个常见的错误,就是过度使用“which”。尝试运行x <- runif(10),然后比较x[x < 0.5]x[which(x < 0.5)]。就像你上面的代码一样,你可以省略“which”来得到相同的结果。 - Gregor Thomas
@shujaa,谢谢你的提示,非常感谢。 - John_dydx
@ Shujaa,你能帮我重新编写我的原始代码让它正常运行吗? - John_dydx
@John 我会添加一个答案,分析你的代码哪里出了问题,但我们最终基本上会得出Mark的答案。 - Gregor Thomas
5个回答

23

只是为了理解为什么你的原始代码没有起作用:

设置数据没问题

x_1 <- c("A1", "A1", "A1", "B10", "B10", "B10","B10", "C100", "C100", "C100")
z_1 <- rnorm(10, 70) 
z_2 <- rnorm(10, 1.7)
A <- data.frame(x_1, z_1, z_2)
个别情节正常,但如我在评论中所说,which是不必要的。
data_A1 <- A[which(A$x_1 == "A1"), ] # your way
plot(data_A1$z_2, data_A1$z_1)

data_A1 <- A[A$x_1 == "A1", ]    # deleting which() makes it cleaner
with(data_A1, plot(z_2, z_1))    # you can also use with() to save typing

现在讲解for循环。让我们回顾一下R语言中的一个简单for循环(与?“for”中的示例非常接近):

for (i in 1:5) {
   print(1:i)
}
很简单,1:5表示c(1, 2, 3, 4, 5),因此第一个i1,然后是2,以此类推。你的for循环在第一行有问题。
for (i in A$x_1[[i]]) { ## already a problem

首先,iA$x_1[[i]] 吗?那不行,i 还没有定义。而且,A$x_1 是一个向量,而不是一个列表,所以你不应该使用 [[ 来对其进行子集筛选。但我们现在并不需要子集,我们想要的是应该取哪些值的向量。在这种情况下,我们需要的是 for (i in c("A1", "B10", "C100")),但我们还希望通过编程的方式来实现它,而不是手动打出所有可能性。有几种常见的方法可以做到这一点:

unique(A$x_1) # as in Mark's solution
levels(A$x_1) # works because A$x_1 is a factor
我们可以在 in 后面放置其中任意一个表达式。我在绘图调用中将你的 [[ 改成了 [。因为[[ 仅适用于列表。我还删除了不必要的which()
for (i in unique(A$x_1)) {   # this line is good
    plot(A[A$x_1==A$x_1[i], ], aspect = 1)  # still a problem
}

让我们回顾一下变量i取的值:"A1""B10""C100"。那么, A$x_1 == A$x_1["A1"] 的结果是什么?没有有用信息。

for (i in unique(A$x_1)) {  
    plot(A[A$x_1 == i, ], aspect = 1)  # getting there
}

以上代码绘制了一些内容,看起来很整洁,但不是你想要的。有一堆警告,都告诉我们 aspect 不是一个有效的参数,所以我们将其删除。观察图表,你会发现它在绘制 3 个变量,因为我们没有告诉它应该放在 x 和 y 轴上。

for (i in unique(A$x_1)) {   
    plot(A[A$x_1==i, "z_2"], A[A$x_1==i, "z_1"])  # z_2 on x, z_1 on y 
}   # Works!!!

注意,这几乎与 Mark 的答案完全相同。您不必在 for 循环中使用 ij,他使用了 cat。最好使用一个更具描述性的名称。

现在让我们稍微改进一下:

for (i in unique(A$x_1)) {   
    plot(A[A$x_1==i, "z_2"], A[A$x_1==i, "z_1"],
         xlim = range(A$z_2), ylim = range(A$z_1), # base the axes on full data range
         main = paste("Plot of", i))  # Give each a title
}

下次:不要忘记,您可以运行一些小代码片段来查看它们的内容。如果您有一行代码像 for (i in A$x_1[[i]]) ,您不确定它是否正确,请在控制台输入A$x_1[[i]],希望这将帮助您发现您未定义i,因此您需要将其更改为

for (i in A$x_1)

然后你运行A$x_1并意识到它的长度是10。你想要3个图表,而不是10个,因此你需要i取3个不同的值等。


4
太棒了!这可能是我从这个论坛收到的最好的答案。现在我更好地理解了循环。谢谢你,shujaa。 - John_dydx

19
一个简单的循环方法是:

for (cat in unique(x_1)){
  d <- subset(A, x_1 == cat)
  plot(d$z_1, d$z_2)
}

unique(x_1) 会获取所有 x_1 的唯一值。然后,对于每个唯一的值,获取相应的子集并将其用于绘图。


@ Mark Heckmann,非常感谢你提供的代码,它运行得非常好。我正在尝试编辑它,使用布局函数将所有三个图形绘制在同一页上,但还没有完全实现。 - John_dydx
@ Mark Heckmann,我刚刚重新查看了代码。我不太确定为什么在上面的代码中d只输出C100类别。为什么它会给出C100类别而不是A1或B10? - John_dydx
@John 我无法重现你的问题,请详细说明。对我来说它运行良好。 - Mark Heckmann
非常抱歉打扰您。我刚刚再次执行了代码以确保。我的 d <- subset(A, x_1==cat) 输出给了我以下三行:8 C100 71.08877 1.892950、9 C100 71.29257 1.144764、10 C100 71.28251 1.974991。这不是您得到的结果吗? - John_dydx
1
@John for循环的重点在于每次都会覆盖d,每个唯一的x_1值都会覆盖一次。由于C100是最后一个值,当for循环结束时,d将处于其最终状态。 - Gregor Thomas
@shujaa,非常感谢您的澄清,现在一切都变得非常清晰了。 - John_dydx

3
也许您不需要循环。尝试使用ggplot的facet_grid()函数。这里有文档,其中包含许多例子。
library(ggplot2)
library(reshape2)

melted_a <- melt(A)


ggplot(melted_a, aes(variable, value)) +
  geom_jitter() +
  facet_grid(. ~ x_1)

ggplot(melted_a, aes(variable, value)) +
  geom_jitter() +
  facet_grid(variable ~ x_1)

编辑 也许这样可以解决这个问题。但是如果你需要做很多类似结构的图,你可以编写一个函数,并使用aes_string()代替aes()。 注意:我不是编写函数的专家,所以可能有人可以编辑和改进它。(未经测试)

ggplot_fun <- function(data, x, y, rowfacet, colfacet, ...){
  p <- ggplot(data, aes_string(x, y))
  p <- p + geom_jitter()
  p <- p + facet_grid(as.formula(sprintf("%s ~ %s", rowfacet, colfacet))
}

ggplot_fun(melted_a, variable, value, variable, x_1)

灵感来自于这个问题


@ Martin Bel,非常感谢您提供的有用参考和代码,我会抽出时间仔细阅读它们。 - John_dydx

1
你还可以调整数据,例如我在这里所做的那样...
如果我想要按日期绘制图表,并使图表的xlab、ylab和标题具有特定的细节...
 for ( i in 1:length(unique(wheeldata$Date)) ){
     d <- subset( wheeldata, Date == unique ( wheeldata$Date )[i] )
     plot(d$X, d$Y, xlab = "X", ylab = "Y", main = paste0("Date: ",  unique(d$Date)) )
 }

0
ggplot_fun <- function(data, x, y, rowfacet, colfacet, ...){
p <- ggplot(data, aes_string(x, y))
p <- p + geom_jitter()
p <- p + facet_grid(as.formula(sprintf("%s ~ %s", rowfacet, colfacet))
}

ggplot_fun(melted_a, variable, value, variable, x_1)

我已经在循环中尝试了上面的代码,但它没有起作用。我刚刚发现需要使用print函数才能使其正常工作。以防有人使用它并认为它无法正常工作。


您介意编辑代码,以便包含您提到的打印功能吗?如果有不熟悉 R 的人看到这个答案,他们可能不知道该怎么做。 - benimwolfspelz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接