使用ggplot2将两个变量绘制成线条并放在同一张图上

366

这是一个非常新手的问题,但是假设我有这样的数据:

test_data <-
  data.frame(
    var0 = 100 + c(0, cumsum(runif(49, -20, 20))),
    var1 = 150 + c(0, cumsum(runif(49, -10, 10))),
    date = seq(as.Date("2002-01-01"), by="1 month", length.out=100)
  )
我该如何使用ggplot2在同一张图上绘制时间序列var0var1,并将日期date放在x轴上? 如果您使var0var1具有不同的颜色并包含一个图例,则会获得额外的奖励分数!我相信这很简单,但我找不到相关的示例。
5个回答

440

对于少量变量,您可以手动构建图表:

ggplot(test_data, aes(date)) + 
  geom_line(aes(y = var0, colour = "var0")) + 
  geom_line(aes(y = var1, colour = "var1"))

4
很好的例子,但是如何自定义我的颜色(例如黑色和橙色)?因为似乎你正在使用colour=作为变量名。 - Darwin PC
2
即使按照Hadley指定的方式使用colour='var_names'也可以正常工作。但是,如果想要选择特定的颜色而不是函数自动选择的颜色,@DaveX可能会更具体。 - LeMarque
11
我该如何添加图例? - user1700890
1
@user1700890,传说似乎是自动添加的。 - ForceBru
如果颜色变量是数字,可能需要先对其进行 as.character() 处理。 - dss

408

一般的做法是将数据转换为长格式(使用reshape包中的melt()函数或者tidyr包中的gather()/pivot_longer()函数):

library("ggplot2")
library("tidyr")
library("reshape2")

## convert to long format with tidyr::pivot_longer
test_data_long_tidyr <- pivot_longer(test_data, cols = starts_with("var"))

ggplot(data=test_data_long_tidyr,
       aes(x=date, y=value, colour=name)) +
  geom_line() ## output not shown, it's equivalent to the below graph (with a tiny difference in the legend title)

## convert to long format with reshape2::melt
test_data_long <- melt(test_data, id="date")  

ggplot(data=test_data_long,
       aes(x=date, y=value, colour=variable)) +
  geom_line()

同时请参见有关从宽格式转换为长格式的数据重塑的此问题


9
您可以使用 tidyr 包中的 gather() 函数来融合数据:gather(test_data, variable, value, -date) - janosdivenyi
2
reshape2和tidyr::gather已经被取代(请参见https://github.com/hadley/reshape和`?tidyr::gather`)。Hadley建议使用"pivot_longer" - 我已经添加了一个使用后者的示例。 - tjebo

44
你需要把数据转换为"tall"格式,以便在ggplot2中使用。 "wide"格式意味着每行只有一个观察值,每个变量是不同的列(就像现在所拥有的那样)。你需要将其转换为"tall"格式,其中一列告诉你变量的名称,另一列告诉你变量的值。从宽格式到长格式的过程通常称为"melting"。你可以使用tidyr::gather来进行数据框的"melting"操作:
library(ggplot2)
library(tidyr)

test_data <-
  data.frame(
    var0 = 100 + c(0, cumsum(runif(49, -20, 20))),
    var1 = 150 + c(0, cumsum(runif(49, -10, 10))),
    date = seq(as.Date("2002-01-01"), by="1 month", length.out=100)
  )
test_data %>%
    gather(key,value, var0, var1) %>%
    ggplot(aes(x=date, y=value, colour=key)) +
    geom_line()

多个序列的ggplot2

仅供明确起见,在通过gather管道传递数据后,ggplot所使用的数据看起来像这样:

date        key     value
2002-01-01  var0    100.00000
2002-02-01  var0    115.16388 
...
2007-11-01  var1    114.86302
2007-12-01  var1    119.30996

18

我对 R 也很新,但在尝试理解 ggplot 的工作原理时,我认为我找到了另一种方法。我只是分享一些不同的观点,可能不是完美的解决方案,但可以增加些许不同的见解。

我知道 ggplot 更适合与数据框一起使用,但有时直接绘制两个向量而不使用数据框也可能很有用。

加载数据。原始日期向量长度为100,而 var0 和 var1 的长度为50,因此我只绘制可用的数据(前50个日期)。

var0 <- 100 + c(0, cumsum(runif(49, -20, 20)))
var1 <- 150 + c(0, cumsum(runif(49, -10, 10)))
date <- seq(as.Date("2002-01-01"), by="1 month", length.out=50)    

绘图

ggplot() + geom_line(aes(x=date,y=var0),color='red') + 
           geom_line(aes(x=date,y=var1),color='blue') + 
           ylab('Values')+xlab('date')

输入图像描述

然而,使用这种格式我无法添加正确的图例。有人知道怎么做吗?


5
这里添加了一个图例。ggplot() + geom_line(aes(x=date,y=var0, group=1, colour = 'red')) + geom_line(aes(x=date,y=var1, group = 2, colour = 'blue')) + ylab('数值')+xlab('日期') + labs(colour = "变量", title = "标题") - flurbius
1
除了你的图表没有图例之外,你的答案与被接受的答案有什么区别? - camille
1
@camille 如所示,唯一的区别是这种方式不使用数据框作为输入,而是直接使用向量。 - susopeiz

13

使用您的数据:

test_data <- data.frame(
var0 = 100 + c(0, cumsum(runif(49, -20, 20))),
var1 = 150 + c(0, cumsum(runif(49, -10, 10))),
Dates = seq.Date(as.Date("2002-01-01"), by="1 month", length.out=100))

我创建了一个堆叠版本,这是ggplot()希望使用的东西:

stacked <- with(test_data,
                data.frame(value = c(var0, var1),
                           variable = factor(rep(c("Var0","Var1"),
                                                 each = NROW(test_data))),
                           Dates = rep(Dates, 2)))
在这种情况下,生成stacked非常容易,因为我们只需进行几次操作即可。但如果你需要处理一个更加复杂的真实数据集,reshape(),以及reshapereshape2可能会有用。
一旦数据呈现出堆叠形式,只需要简单地调用ggplot(),就可以生成所需的图表及其所有其他内容(这就是为什么高级绘图包,如latticeggplot2如此有用的原因之一):
require(ggplot2)
p <- ggplot(stacked, aes(Dates, value, colour = variable))
p + geom_line()

我会让你负责整理坐标轴标签、图例标题等。

希望对你有所帮助


1
我认为你的代码中有一个括号放错了位置。我认为这是你想要的:stacked <- with(test_data, data.frame(value = c(var0, var1), variable = factor(rep(c("Var0", "Var1"))), each = NROW(test_data), Dates = rep(date, 2)))。另外,“each”列的目的是什么?这不仅是一种更加复杂和低效的方式来融合数据,而且还不如rcs所示的融合数据的方法。我可以想象除非我漏掉了什么,否则melt几乎肯定是这项工作的正确工具? - Chase
1
@chase,抱歉,这是Emacs ESS出现缩进错误。每个都是rep()的一个参数,所以在stacked中我们只得到了3列。我会编辑代码使缩进更清晰。 - Gavin Simpson
1
@chase; 你对melt()的评论很有道理,我注意到reshape[2]包在这里会很有用。我不太熟悉reshape2,对于这样一个简单的操作,手动完成比调用melt()更复杂,因为我不需要阅读如何使用melt()。当我开始回复时,rcs已经回答了我的问题;就像他们说的那样,“千条万绪皆是成路”;-) - Gavin Simpson

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