问题

R语言怎么把两个时间序列画在同一张图上?

回答
好的,咱们来聊聊 R 语言里,怎么把两个时间序列图画在一张图里。这事儿吧,听着简单,但有时候确实需要点技巧,尤其是在你想让这两条线都看得清楚,而且能一目了然地看出它们之间的关系时。

我这就把经验给你捋一捋,一步步来,保证讲得明明白白。

核心思路:合并数据,然后使用绘图函数

说白了,把两个时间序列画在一张图上,最核心的思路就是:

1. 把这两个时间序列的数据放在同一个“容器”里。 这个容器最好能清晰地标识出哪个数据属于哪个时间序列,并且还得带着时间这个维度。
2. 使用 R 的绘图函数,把这个“容器”里的数据,按照时间和对应的值,画出来。

听着好像挺虚的?别急,咱们用实际的 R 代码来说话,你会发现它比想象的要容易。

准备工作:让你的数据“站好队”

首先,你需要有你的两个时间序列数据。在 R 里,时间序列数据最常见、最方便的形式是 `ts` 对象,或者一个包含时间戳和对应数值的 `data.frame` / `tibble`。

场景一:你已经有了两个 `ts` 对象

假设你已经有两个命名好的 `ts` 对象,比如 `ts_data1` 和 `ts_data2`。

```R
假设这是你的第一个时间序列数据(例如,每月的销售额)
咱们随便生成点数据模拟一下
start_date1 < as.Date("20220101")
end_date1 < as.Date("20231231")
dates1 < seq(start_date1, end_date1, by = "month")
values1 < cumsum(rnorm(length(dates1), mean = 5, sd = 2)) 模拟一些随机波动
ts_data1 < ts(values1, start = c(2022, 1), frequency = 12) 频率为12表示月度

假设这是你的第二个时间序列数据(例如,每月的广告投入)
start_date2 < as.Date("20220201") 可能开始时间略有不同
end_date2 < as.Date("20231130") 结束时间也可能不同
dates2 < seq(start_date2, end_date2, by = "month")
values2 < cumsum(rnorm(length(dates2), mean = 2, sd = 1)) 模拟另一组数据
ts_data2 < ts(values2, start = c(2022, 2), frequency = 12)
```

怎么把它们放在一张图里?

最直接的方法是创建一个新的 `data.frame`,把它们整合起来。但 `ts` 对象直接转成 `data.frame` 的时候,时间信息不太直观。这时候,我们可以利用 `ts` 对象本身的 `time()` 函数来获取时间点,然后把数值提取出来。

关键一步:统一时间轴

这里有个小陷阱:如果两个时间序列的时间范围不完全一致,或者开始/结束时间点有差异,你需要决定如何处理。 通常的做法是:

取两个时间序列的并集: 包含所有存在数据的时间点。
取两个时间序列的交集: 只包含两个时间序列都有数据的时段。
以其中一个为准,另一个进行填充: 例如,以数据较全的为基础,另一个序列在没有数据的地方填充 `NA`。

这里我们为了方便演示,假设它们的时间点大部分重叠,并且我们想看到从最早到最晚的全部区间。

```R
1. 获取所有唯一的时间点
all_times < sort(unique(c(time(ts_data1), time(ts_data2))))

2. 创建一个数据框来存储整合后的数据
注意:time() 函数对于 ts 对象,会返回一个数值型的时间索引,
例如 2022.0, 2022.0833, 2022.1667... (表示年份+月份的比例)
如果需要实际的日期,我们可能需要转换一下。
plot_data < data.frame(
Time = all_times
)

3. 将两个时间序列的值加入数据框,并在没有数据的地方填充 NA
这里要小心,time() 返回的数值索引可能和我们的 all_times 不直接匹配,
所以直接用 all_times 索引 ts 对象的值会报错。
更稳妥的方式是,先将 ts 对象转换为带日期的 data.frame
```

推荐的更稳妥做法:将 `ts` 对象转换为带日期的数据框

R 的 `stats` 包里有个 `fortify.ts` 函数(通常是 `ggplot2` 包调用),或者我们自己手动转换:

```R
载入 ggplot2 包,它提供了很多方便的功能
library(ggplot2)

将 ts 对象转换为 tibble (一种现代化的 data.frame)
并且格式化时间
ts_to_tibble < function(ts_obj, series_name) {
data.frame(
time = time(ts_obj),
value = as.numeric(ts_obj)
) %>%
dplyr::mutate(
将数值型的时间索引转换为实际日期(假设 frequency=12 表示月度)
start(ts_obj)[1] 是起始年份,start(ts_obj)[2] 是起始月份
date = seq.Date(
from = as.Date(sprintf("%d%02d01", start(ts_obj)[1], start(ts_obj)[2])), "%Y%m%d"),
by = "month",
length.out = length(ts_obj)
),
series = series_name
) %>%
dplyr::select(date, value, series) 只保留我们需要的列
}

df1 < ts_to_tibble(ts_data1, "Series 1")
df2 < ts_to_tibble(ts_data2, "Series 2")

合并两个数据框
使用 bind_rows from `dplyr`,它能很好地处理列名相同但可能来自不同源的数据
all_data_df < dplyr::bind_rows(df1, df2)

检查一下合并后的数据
head(all_data_df)
tail(all_data_df)
```

场景二:你的数据本来就是 `data.frame` 格式

如果你的数据已经是一个 `data.frame`,比如这样:

```R
假设你有两个数据框,分别代表两个序列,但都包含日期列
data_frame1 < data.frame(
Date = seq.Date(as.Date("20220101"), by = "month", length.out = 24),
Value1 = cumsum(rnorm(24, mean = 10, sd = 3))
)

data_frame2 < data.frame(
Date = seq.Date(as.Date("20220301"), by = "month", length.out = 22),
Value2 = cumsum(rnorm(22, mean = 15, sd = 4))
)
```

处理方式类似:

1. 转换格式:将每个数据框转换为一个“长格式”数据框,包含 `Date`、`Value` 和 `Series` 三列。
2. 合并:使用 `bind_rows` 合并。

```R
library(dplyr) 确保你已经加载了 dplyr

df_long1 < data_frame1 %>%
tidyr::pivot_longer(cols = Date, names_to = "series", values_to = "value") %>%
mutate(series = "Series A") 给第一个序列命名

df_long2 < data_frame2 %>%
tidyr::pivot_longer(cols = Date, names_to = "series", values_to = "value") %>%
mutate(series = "Series B") 给第二个序列命名

合并
all_data_long < bind_rows(df_long1, df_long2)

检查一下
head(all_data_long)
tail(all_data_long)
```
注意:在上面的例子中,`pivot_longer` 是一个非常强大的工具,可以将宽格式数据(多列代表不同变量)转换为长格式数据(一列存储变量名,一列存储变量值)。如果你本来就只有一个数值列,可以直接创建 `series` 列。

绘图:让你的数据“活起来”

现在我们有了整洁的 `all_data_df` (或者 `all_data_long`),就可以用 R 的强大绘图功能来画图了。最常用也最灵活的莫过于 `ggplot2`。

使用 `ggplot2` 绘制

`ggplot2` 的核心思想是“语法化”,把绘图的各个元素(数据、映射、几何对象、统计变换、标度、坐标系、主题)分开处理。

```R
确保你已经加载了 ggplot2
library(ggplot2)

使用我们前面创建的 all_data_df (从 ts 对象转换来的)
ggplot(all_data_df, aes(x = date, y = value, color = series)) +
geom_line() + 画线图
labs(
title = "对比两个时间序列",
x = "日期",
y = "数值",
color = "序列名称" Legend 的标题
) +
theme_minimal() 使用一个简洁的图表风格
```

如果你使用的是 `all_data_long` (从 data.frame 转换来的)

```R
同样是 ggplot2
ggplot(all_data_long, aes(x = Date, y = value, color = series)) +
geom_line() +
labs(
title = "对比两个时间序列",
x = "日期",
y = "数值",
color = "序列"
) +
theme_minimal()
```

关键点解读:

`ggplot(all_data_df, aes(x = date, y = value, color = series))`:
`ggplot()`: 初始化一个 ggplot 对象,指定要使用的数据集 (`all_data_df`)。
`aes(x = date, y = value, color = series)`: 这是“美学映射”。
`x = date`: 把 `date` 列映射到 x 轴。
`y = value`: 把 `value` 列映射到 y 轴。
`color = series`: 把 `series` 列映射到颜色。这样,`ggplot2` 就会自动为不同的 `series` 赋予不同的颜色,并在图例中显示出来。

`+ geom_line()`: 这是一个“几何对象层”。`geom_line()` 告诉 `ggplot2` 使用线条来表示数据。因为我们在 `aes()` 里已经指定了 `color = series`,`geom_line()` 会根据 `series` 的不同值,画出不同的颜色线条。

`+ labs(...)`: 用来添加标题、轴标签和图例标题,让图表更易读。

`+ theme_minimal()`: 这是一个“主题”。`ggplot2` 提供了很多主题(如 `theme_bw()`, `theme_classic()` 等),可以快速改变图表的整体外观,去除一些不必要的背景元素,让图更干净。

进阶技巧:调整外观与添加辅助信息

1. 调整颜色和线型

如果你想手动指定颜色,或者让不同序列使用不同的线型(实线、虚线等),可以这样做:

```R
ggplot(all_data_df, aes(x = date, y = value, color = series, linetype = series)) +
geom_line() +
scale_color_manual(values = c("Series 1" = "blue", "Series 2" = "red")) + 手动指定颜色
scale_linetype_manual(values = c("Series 1" = "solid", "Series 2" = "dashed")) + 手动指定线型
labs(
title = "对比两个时间序列 (自定义颜色和线型)",
x = "日期",
y = "数值",
color = "序列",
linetype = "序列"
) +
theme_minimal()
```

2. 双 Y 轴(谨慎使用)

有时候,两个时间序列的数值范围差异很大,一条 Y 轴可能导致其中一个序列的细节看不清。这时可以考虑使用双 Y 轴。但是! 双 Y 轴容易让图表产生误导,建议在非常必要的情况下使用,并且要清晰地标明每个 Y 轴代表什么。

在 `ggplot2` 里实现双 Y 轴,通常需要自定义标度(scale)和转换。一个常见的方法是使用 `sec_axis`:

```R
假设 Series 2 的数值范围比 Series 1 大很多
我们在 Series 1 的右侧添加一个 Y 轴,并对 Series 2 的值进行缩放
ggplot(all_data_df, aes(x = date)) +
geom_line(aes(y = value, color = series), data = subset(all_data_df, series == "Series 1")) +
geom_line(aes(y = value 10, color = series), data = subset(all_data_df, series == "Series 2")) + 假设 Series 2 的值需要乘以 10 来匹配 Series 1 的范围
scale_y_continuous(
name = "Series 1 的数值",
sec.axis = sec_axis(~ . / 10, name = "Series 2 的数值") 右侧 Y 轴的标签和转换
) +
scale_color_manual(values = c("Series 1" = "blue", "Series 2" = "red")) +
labs(
title = "双 Y 轴对比时间序列 (谨慎使用)",
x = "日期",
color = "序列"
) +
theme_minimal()
```
注意:上面的 `value 10` 和 `~ . / 10` 是示例,实际使用时需要根据你的数据来调整缩放因子。`sec_axis` 的第一个参数 `~ . / 10` 是一个函数,它接收右侧 Y 轴上的数值,并进行逆向转换,以得到左侧 Y 轴上的实际值(或者反之)。

3. 添加垂直线或阴影区域

如果你想标出某个重要的时间点,比如一个事件发生的时间,可以在图上添加垂直线。

```R
假设一个重要事件发生在 2022 年 6 月 15 日
event_date < as.Date("20220615")

ggplot(all_data_df, aes(x = date, y = value, color = series)) +
geom_line() +
geom_vline(xintercept = event_date, linetype = "dashed", color = "gray40") + 添加垂直线
annotate("text", x = event_date + 30, y = max(all_data_df$value, na.rm = TRUE), label = "关键事件",hjust = 0, color = "gray40") + 添加文本说明
labs(
title = "对比两个时间序列,标记关键事件",
x = "日期",
y = "数值",
color = "序列"
) +
theme_minimal()
```

总结一下步骤

1. 获取你的时间序列数据。 无论是 `ts` 对象还是 `data.frame`。
2. 将数据转换为适合绘图的格式。
如果原始是 `ts` 对象,转换为一个包含 `date`, `value`, `series` 列的 `data.frame` 或 `tibble`。
如果原始就是 `data.frame`,确保它有 `Date`, `Value` 列,然后如果每个序列在不同列,先用 `pivot_longer` 转换成“长格式”。
3. 合并所有序列到一个数据框。 确保时间轴是统一的。
4. 使用 `ggplot2` 进行绘图。
`ggplot(data, aes(x = time_column, y = value_column, color = series_column))`
`+ geom_line()`
根据需要添加 `labs()`, `theme_()`, `scale_()` 等。

掌握了这些,你就可以灵活地把任何两个(或更多)时间序列画在同一张图上,并根据你的需求来调整它们的外观和信息展示。希望这个详细的说明能帮到你!

网友意见

user avatar
单个时间序列的话可以直接plot,可是plot函数里面没有add参数怎么办

类似的话题

  • 回答
    好的,咱们来聊聊 R 语言里,怎么把两个时间序列图画在一张图里。这事儿吧,听着简单,但有时候确实需要点技巧,尤其是在你想让这两条线都看得清楚,而且能一目了然地看出它们之间的关系时。我这就把经验给你捋一捋,一步步来,保证讲得明明白白。 核心思路:合并数据,然后使用绘图函数说白了,把两个时间序列画在一张.............
  • 回答
    好的,我们来一步一步学习如何用 R 语言绘制一张包含英文省名、并且根据需要为不同省份填充颜色的中国地图。我会尽量讲得详细一些,让你能理解其中的原理和步骤。在开始之前,你需要先确保你的 R 环境已经安装好了必要的包。如果你还没有安装,可以通过 R 控制台执行以下命令来安装:```Rinstall.pa.............
  • 回答
    好的,我们来聊聊 R 语言中处理连续求和(也就是多重求和,用多个 $sum$ 来表示)的编码方式。在数学里,多重求和 $sum_{i=1}^{n} sum_{j=1}^{m} f(i, j)$ 表示的是对函数 $f$ 的两个变量 $i$ 和 $j$ 分别在指定范围内进行求和。在 R 语言中,我们并没.............
  • 回答
    好的,咱们来聊聊 R 语言在 R Markdown 里用 `qplot` 画散点图,并且要把它们“堆”在一起显示。这事儿其实不难,关键在于理解 R Markdown 如何处理代码块和输出。 核心思路:代码块与输出管理在 R Markdown 里,每个用 ```{r} 包裹起来的代码块,都会独立执行一.............
  • 回答
    好的,我们来聊聊 R 语言中 `rvest` 爬虫如何使用 IP 代理,并且我会尽量讲得详细些,避免那些“机器人生成”的痕迹。话说回来,咱们做爬虫的,谁还没遇见过 IP 被封的情况?尤其是爬取一些反爬机制比较强的网站,时不时就来个“403 Forbidden”或者直接 IP 拉黑,那叫一个糟心。这时.............
  • 回答
    R 语言,作为一款强大的统计计算和图形展示平台,在数据科学和统计学领域占据着举足轻重的地位。它的诞生和发展,几乎与现代数据分析的需求同步。要深入了解 R 语言,我们得从它的优点和缺点两个方面,细致地剖析。 R 语言的优势1. 卓越的统计分析能力与海量专业包:R 语言的核心竞争力在于其深厚的统计分析能.............
  • 回答
    在 R 语言中,对数据框进行按条件运算是数据分析中非常核心的一个环节,它允许我们根据数据的特定属性或状态来执行不同的计算、筛选或者转换。这使得我们能够更精细地理解和操控数据。下面我将详细讲解如何在 R 中实现这一目标,涵盖多种方法和场景。 理解数据框与条件运算首先,我们需要明确 R 中的数据框(`d.............
  • 回答
    在 R 语言中,大家更倾向于使用 `<` 进行赋值,而非 `=`,这背后其实有几层原因,涉及 R 的历史、设计哲学以及一些实际的便利性考量。虽然 `=` 也能进行赋值,但 `<` 已经成为 R 社区一种约定俗成的、更受推崇的风格。咱们细细聊聊这其中的门道。1. 历史的传承与设计的哲学: S 语言.............
  • 回答
    获取特定地址的经纬度,在 R 语言中并非直接内置的功能,但我们可以借助强大的第三方服务和 R 包来完成这项任务。这通常涉及到“地理编码”(Geocoding)的过程,即将人类可读的地址文本转换为地理坐标(经度、纬度)。下面我将一步步地详细介绍如何在 R 中实现这一目标,并尽力让这篇指南读起来像一位经.............
  • 回答
    说起来,我与 R 的结缘,更像是一场“被迫”的成长,但随着时间的推移,这份“被迫”也渐渐变成了心甘情愿的探索。最初接触 R,是因为一个项目。当时我的工作涉及一些数据分析,而团队里的小伙伴都在用 R。看着他们能用简单的代码画出漂亮的图表,处理复杂的数据,我心里那个痒痒啊。但我一直以来都不是一个特别主动.............
  • 回答
    经济学研究生做经验研究,要问 R 能不能完全取代 Stata?这真是一个绕不开的话题,也是很多同学在学习和研究过程中反复权衡的问题。我可以给你详细地说说,尽量讲透彻,让你自己能有个判断。首先,我的直接回答是:目前来说,对于大多数经济学研究生做经验研究,R 不能 完全 取代 Stata。这里面“完全”.............
  • 回答
    关于国际音标(IPA)符号在不同语言中的发音是否完全一致,这是一个非常有趣且值得深入探讨的问题。简单来说,大部分IPA符号的发音在理论上是约定俗成的,即一个符号对应一个特定的语音,但在实际应用中,由于不同语言的语音系统差异,以及发音的细微变异,同一个IPA符号在不同语言里听起来会有些许不同,但通常仍.............
  • 回答
    从狼的嚎叫到印欧语系的羁绊:拉丁语 lupus 与亚非语 labiʔ 的划清界限印欧语系这个庞大的语言家族,其成员之间存在着千丝万缕的联系,就像一张覆盖了欧亚大陆的古老地图。而要在这张地图上准确地定位一个词的来龙去脉,则需要印欧语学家们运用严谨的科学方法,如同考古学家在层层泥土中挖掘失落的文明。拉丁.............
  • 回答
    R级战列舰,或者更准确地说,是“R”级战列舰的设想,在海军事典中,它是一个充满传奇色彩却又从未真正诞生的庞然大物。它代表着德国在第一次世界大战末期,甚至是战后仍在构思的一种更为宏大、更具野心的海军力量。要理解R级战列舰的存在,我们必须深入那个时代德国海军的战略思维、技术追求以及政治经济的博弈之中。背.............
  • 回答
    R 和 Python(尤其是带有 NumPy、SciPy 和 Pandas 的 Python 生态系统)都是进行统计分析的强大工具,但它们在设计哲学、生态系统和最适合的应用场景上存在显著差异。选择哪一个“更好”很大程度上取决于你的具体需求、背景以及你更偏好的工作流程。R:统计学家的摇篮,为数据分析而.............
  • 回答
    说起在 R 里面那些让我觉得“怎么之前没早点知道啊”的命令和包,那可真是不少。有些功能,一旦用了,就感觉像是打开了新世界的大门,之前的很多手动操作、绞尽脑汁的思考瞬间都变得轻松了。今天就想跟大家聊聊几个我“相见恨晚”的 R 利器,希望能给正在学习或者已经在使用 R 的朋友们带来点启发。 1. `dp.............
  • 回答
    这个问题触及到了线性代数中一个非常优美且重要的概念,那就是向量组的张成空间以及其与行列式之间的深刻联系。简单来说,答案是肯定的。对于一组 $r$ 个线性无关的 $n$ 维向量,它们张成的平行体的体积的平方,确实等于这 $r$ 个向量构成矩阵的所有 $r$ 阶子式的平方和。这个结论通常被称为拉普拉斯展.............
  • 回答
    .......
  • 回答
    .......
  • 回答
    索尼R系列,尤其是像α7R系列,之所以在画质上备受推崇,绝非空穴来风。它在很多方面都展现出了令人惊叹的实力,让不少追求极致影像的用户为之倾倒。咱们就来掰开了揉碎了聊聊,到底它好在哪里,好得有多细致。1. 那令人咋舌的超高像素:细节的极致捕捉这是R系列最直观的卖点,也是它“R”姓的根本。想象一下,一块.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有