datatable(data7_1,rownames = FALSE)
第七章 时间序列可视化
1 案例数据
1.1 data7_1:2000-2021年国内生产总值和指数
GDP、第一产业、第二产业、第三产业,国内生产总值,
GDP指数(%),大于100为增长。
1.2 data7_2:2020-2023年某地区空气质量评价数据
读入数据时需要将日期转化为日期型变量,质量等级转化为因子型变量
AQI为空气质量总指数,由PM2.5~臭氧浓度综合计算,各空气质量数值越大质量越差
datatable(data7_2,rownames = FALSE)
1.3 data7_3:2024年上证指数交易数据
该数据中
data
为为日期变量,但该日期变量不规则(不连续),周末和公众假期没有交易数据利用数据作K线图
datatable(data7_3,rownames = FALSE)
2 折线图和面积图
2.1 折线图
ggplot+geom_line+geom_point
如果需要对多个变量同时作折线图,需要将多个变量通过
gather
函数转化为长型数据。p1 %+% df2
将p1
的df1
数据集替换为df2
,要注意两个数据集必须有相同的变量名。
# 选择绘图变量并融合为长格式
<-data7_1 |> select(年份,GDP,第三产业) |>
df1gather(GDP,第三产业,key="指标",value="指标值")
<-data7_1 |> select(年份,第一产业:第三产业) |>
df2gather(2:4,key="指标",value="指标值") |>
mutate(指标=fct_inorder(指标))
# 设置绘图主题
<-theme(legend.position=c(0.2,0.8), # 将图例放在图内
mythemelegend.background=element_blank()) # 移除图例整体边框
<-ggplot(df1,aes(x=年份,y=指标值,color=指标))+ # 设置x轴、y轴和线的颜色
p1geom_line(size=0.8)+
geom_point(aes(shape=指标),size=2)+ # 数值点的形状和大小
+ggtitle("(a) GDP与第三产业的折线图")
mytheme
<-p1 %+% df2+ggtitle("(b) 三次产业的折线图")
p2
::grid.arrange(p1,p2,ncol=2) gridExtra
如果指标很多,转化为长型数据后采用分面处理,
facet_wrap(~指标,scale="free")
如果横轴为日期型变量,将自动选择合适刻度显示
<-data7_2 |> select(日期,PM2.5:臭氧浓度) |>
dffilter(year(日期)==2023) |>
gather(PM2.5:臭氧浓度,key=指标,value=指标值)
# 绘制折线图
<- ggplot(df,aes(x=日期,y=指标值,color=指标))+ # 设置x轴、y轴和线的颜色
p geom_line(size=0.45)+ # 绘制折线图
theme(legend.position="none", # 删除图例
axis.text.x=element_text(size=7,angle=90,hjust=1,vjust=1))+# 设置x轴标签角度
theme(panel.grid.minor.x=element_blank(), # 去掉x轴次网格线
panel.grid.minor.y=element_blank())+ # 去掉y轴次网格线
scale_x_date(expand=c(0,0),date_breaks="1 month",date_labels="%b")+# 设置x轴间隔为1个月
facet_wrap(~指标,ncol=3,scale="free") # 按指标分面,并单独设置各分面图的y轴刻度
p
通过
ggpol::geom_tshighlight
对某段时间高亮显示,通过xmin
和xmax
两个映射参数指定。日期型数据需要通过
as.Date
函数将字符转为为日期。
library(ggpol)
+geom_tshighlight(aes(xmin=as.Date("01/07/2023",format="%d/%m/%Y"),
pxmax=as.Date("30/09/2023",format="%d/%m/%Y")), # 设置x轴的最小值和最大值
color="skyblue",fill="lightblue",alpha=0.02)
2.2 面积图
ggplot+geom_area
,数据准备与折线图一致,只是将折线图和横轴的部分填充颜色facet_wrap(~指标,scale="free")
,如果时间序列较长可以选择分面处理
<-data7_2 |> select(日期,PM2.5:臭氧浓度) |>
dffilter(year(日期)==2023) |>
gather(PM2.5:臭氧浓度,key=指标,value=指标值) |>
mutate(指标=fct_inorder(指标))
ggplot(df,aes(x=日期,y=指标值,fill=指标))+
geom_area()+ # 绘制面积图
scale_x_date(expand=c(0,0),date_breaks="3 month",date_labels="%b")+
guides(fill="none")+
facet_wrap(~指标,ncol=3,scale="free")
如果时间序列较短可以选择堆叠处理
ggplot+geom_area+geom_line(position="stack")
,绝对值堆叠ggplot+geom_area+geom_line(position="fill")
,百分比堆叠scale_fill_brewer
设置填充颜色采用的调色板注意这个图不同指标的意义和量纲不一致,堆叠没有实际意义。
# 选择一月份数据
<- df |> filter(日期>="2023/1/1" & 日期<="2023/1/31")
df
# 图(a)堆叠面积图
<-ggplot(df,aes(x=日期,y=指标值,fill=指标))+ # 按指标分组
p1geom_area()+ # 绘制面积图
geom_line(position="stack",color="grey")+ # 绘制线图
scale_fill_brewer(palette="Blues")+ # 设置配色方案(蓝色)
theme_bw()+theme(legend.position="bottom")+
ggtitle("(a) 堆叠面积图")
# 图(b)百分比堆叠面积图
<-ggplot(df,aes(x=日期,y=指标值,fill=指标))+
p2geom_area(position="fill",color="grey")+ # 绘制百分百堆叠面积图
scale_fill_brewer(palette="Reds")+
scale_y_continuous(labels=scales::percent)+ # 显示百分百标签
theme_bw()+theme(legend.position="bottom")+
ylab("百分比")+ggtitle("(b) 百分比堆叠面积图")
::grid.arrange(p1,p2,ncol=2) gridExtra
3 风筝图和流线图
3.1 风筝图
风筝图就是面积图依横轴对称复制形成,比较有利观察时间序列的波动规律。
plotrix::kiteChart
,输入必须为均值,将日期变量转发为字符作为行名。normalize=TRUE
,对数据作归一化处理
library(plotrix)
<-as.matrix(data7_2[,c(4:9)]);
matrownames(mat)=format(data7_2$日期, format = "%Y/%m/%d")
# 将数据框转化成矩阵
# 图7-7(a)原始数据
par(mfrow=c(1,2),cex=0.7,font.main=1)
kiteChart(t(mat),
varscale=FALSE, # 不显示每个"风筝线"的最大值
xlab="时间",ylab="指标",main="(a) 原始数据",
mar=c(4,4,2,1)) # 设置边距
# 图7-7(b)归一化数据
kiteChart(t(mat),
timex=TRUE, # 时间放在水平的x轴
normalize=TRUE, # 将每行值缩放为最大宽度为1
shownorm=FALSE, # 不显示归一化乘数
xlab="时间",ylab="指标",main="(b) 归一化数据",
mar=c(4,4,2,1)) # 设置边距
3.2 流线图
外形为堆叠面积图依横轴对称所得,各类在其中堆叠显示,形成流水形状
ggplot(aes(group=指标,fill=指标))+geom_stream
需要将多个指标数据转化为长型数据
library(ggstream)
# 处理数据
<-data7_2 |> select(日期,PM2.5:臭氧浓度) |>
df1 filter(日期>="2023/1/1" & 日期<="2023/3/31") |>
gather(PM2.5:臭氧浓度,key=指标,value=指标值) |>
mutate(指标=fct_inorder(指标))
<-data7_2 |> select(日期,PM2.5:臭氧浓度) |>
df2 filter(日期>="2023/1/1" & 日期<="2023/12/31") |>
gather(PM2.5:臭氧浓度,key=指标,value=指标值) |>
mutate(指标=fct_inorder(指标))
# 绘制流线图
<-ggplot(df1,aes(x=日期,y=指标值,group=指标,fill=指标))+
p1geom_stream(bw = 0.3) + # 绘制流线图,设置带宽=0.2
theme(legend.position="bottom")+
guides(fill=guide_legend(nrow=2,title=NULL))+# 图例排成2行,去掉图例标题
ylab("")+ggtitle("(a) 1~3月份数据的流线图") # 去掉y轴标签
<-ggplot(df2,aes(x=日期,y=指标值,group=指标,fill=指标))+
p2geom_stream(bw = 0.3) +
theme(legend.position="bottom")+
scale_x_date(expand=c(0,1),date_breaks="1 month",date_labels="%b")+
guides(fill=guide_legend(nrow=2,title=NULL))+# 图例排成2行,去掉图例标题
ylab("")+ggtitle("(b) 全年数据的流线图")
::grid.arrange(p1,p2,ncol=2) gridExtra
4 地平线图
用于观测多个长时间序列的波动情况
latticeExtra::horizonplot
,需要输入ts
类的时间序列对象origin
为自定义原点
library(latticeExtra)
<-data7_2 |> filter(日期>="2023/1/1" & 日期<="2023/12/31") |>
dtselect(-c(日期,质量等级)) |> ts() # 选择绘图数据并生成时间序列对象
horizonplot(dt,main="latticeExtra 包绘制的地平线图",
layout=c(1,7), # 1列7行的页面布局
origin=median,
colorkey=TRUE, # 显示色键
par.settings=list(par.main.text=list(cex=1,font=1)))# 设置主标题字体大小
ggHoriPlot::geom_horizon
library(ggHoriPlot)
library(ggthemes) # 为了使用theme_few主题
# 处理数据
<-data7_2 |> select(日期,PM2.5:臭氧浓度) |>
df1 filter(日期>="2023/1/1" & 日期<="2023/3/31") |>
gather(PM2.5:臭氧浓度,key=指标,value=指标值) |>
mutate(指标=fct_inorder(指标))
<-data7_2 |> select(日期,PM2.5:臭氧浓度) |>
df2 filter(日期>="2023/1/1" & 日期<="2023/12/31") |>
gather(PM2.5:臭氧浓度,key=指标,value=指标值) |>
mutate(指标=fct_inorder(指标))
# 图(a) 1~3月份数据的地平线图
<-ggplot(df1)+aes(x=日期,y=指标值,fill=指标)+
p1geom_horizon(origin='median',horizonscale=10,show.legend=FALSE) + # 绘制地平线图,原点为最小值,地平线图的切割点为10,不显示图例
scale_x_date(expand=c(0,1),date_breaks="1 month",date_labels="%b")+# 设置x轴间隔为1个月(向后扩展1期)
facet_grid(指标~.)+ # 按指标分面
scale_fill_hcl(palette='RdYlBu',reverse=F)+ # 设置调色板(颜色不反转)
+ggtitle("(a) 1~3月份数据的地平线图")
mytheme
# 图(b) 全年数据的地平线图
<-ggplot(df2)+aes(x=日期,y=指标值,fill=指标)+
p2geom_horizon(origin='median',horizonscale=10,show.legend=FALSE)+
scale_x_date(expand=c(0,0),date_breaks="1 month",date_labels="%b")+# 设置x轴间隔为1个月
facet_grid(指标~.)+
scale_fill_hcl(palette='RdYlBu',reverse=F)+
+ggtitle("(b) 全年数据的地平线图")
mytheme
::grid.arrange(p1,p2,ncol=1) # 组合图形 gridExtra
5 双坐标图
plotrix::twoord.plot(lx,ly=,rx=,ry=)
,一般要求lx
和rx
为相同变量type=c(A,B)
设定不同作图效果
library(plotrix)
par(mfrow=c(1,2),lab=c(10,2,1),cex.main=0.8,font.main=1)
twoord.plot(data=data7_1,type="b", # 设置绘图类型
lcol="black",rcol="red2",rpch=22, # 设置左侧和右侧图的颜色和点的类型
lx="年份",ly="GDP",rx="年份",ry="GDP指数",
# 设置左侧和右侧坐标轴
lytickpos=seq(100000,1000000,by=200000), # 设置左侧坐标轴标签刻度
rytickpos=seq(100,120,by=2), # 设置右侧坐标轴标签刻度
xlab="年份",ylab="国内生产总值(亿元)",rylab="国内生产总值指数(上年=100)",# 设置左侧和右侧坐标轴标签
rylab.at=108, # 设置右侧标签位置
axislab.cex=0.7, # 设置坐标轴标签字体大小
main="(a) 双折线图", # 设置主标题
mar=c(4,4,2,4)) # 设置图形边界
# (b)条形图和折线图
twoord.plot(data=data7_1,type=c("bar","b"), # 设置绘图类型
lcol="orange2",rcol="black",rpch=1,
lx="年份",ly="GDP",rx="年份",ry="GDP指数",
lytickpos=seq(100000,1000000,by=200000),rytickpos=seq(100,120,by=2),
xlab="年份",ylab="国内生产总值(亿元)",rylab="国内生产总值指数(上年=100)",
rylab.at=108,axislab.cex=0.7,main="(b) 条形图和折线图",
mar=c(4,4,2,4))
6 日历图
openair::calendarPlot
,输入数据框中需要有date为名的日期变量。
library(openair)
# 处理数据
<- data7_2 |> select(日期,AQI) |>
df mutate(year=year(日期),date=日期) |> filter(year=="2023") # 筛选出2023年的数据
# 绘制日历图
Sys.setlocale(locale="C") # 修改计算机系统以合理显示x轴标签
[1] "C"
calendarPlot(df,pollutant="AQI",cols="heat",year=2023,month=c(1:12))
Sys.setlocale()
[1] "LC_COLLATE=Chinese (Simplified)_China.utf8;LC_CTYPE=Chinese (Simplified)_China.utf8;LC_MONETARY=Chinese (Simplified)_China.utf8;LC_NUMERIC=C;LC_TIME=Chinese (Simplified)_China.utf8"
- 通过
month
参数选择月份;
calendarPlot(selectByDate(df,month=c(1,2,3),year=2023),# 选择月份
pollutant="AQI",
key.position="bottom", # 设置图例位置
breaks=c(0,50,100,150,200,300), # 设置分组向量
labels=c("优","良","轻度污染","中度污染","重度污染"), # 设置标签向量
cols=c("green","yellow","orange","red","purple")) # 设置颜色向量
ggTimeSeries::ggplot_calendar_heatmap
library(ggTimeSeries)
<- data7_2 |> rename(date=日期) |>
dfselect(date,AQI) |>
mutate(year=year(date))
# 绘制日历图
ggplot_calendar_heatmap(dtDateValue=df,
cDateColumnName="date", # 设置日期的列名
cValueColumnName="AQI", # 设置数据的列名
vcGroupingColumnNames="year", # 设置分组的列名
dayBorderSize=0.2,dayBorderColour="grey60", # 设置每天的边界线大小和颜色
monthBorderSize=0.5,monthBorderColour="white")+ # 设置月份间的边界线大小和颜色
scale_fill_gradientn(colors=rev(brewer.pal(11,"Spectral")))+# 设置调色板
facet_wrap(~year,ncol=1) # 按年份分面
7 随机成分平滑曲线
forecast::ma(order,centre)
,centre=FALSE
则向前求移动平均,不能计算的日期数值用NA代替。
library(forecast) # 为了使用ma(移动平均)函数
# 处理数据
<- data7_2 |> filter(year(日期)==2023) |> select(日期,AQI)
d1 <-ma(d1$AQI,order=7,centre=TRUE) # 计算7日移动平均
ma7<-ma(d1$AQI,order=30,centre=TRUE) # 计算30日移动平均
ma30<-data.frame(d1,ma7,ma30) |>
dfgather(AQI:ma30,key=指标,value=指标值) |>
mutate(指标=fct_inorder(指标))
# 绘制折线图
<-ifelse(df$指标=="AQI",0.2,0.8) # 设置线宽度
sizeggplot(df,aes(x=日期,y=指标值,color=指标))+
geom_line(size=size)+ # 绘制折线图
scale_x_date(expand=c(0,0),date_breaks="1 month",date_labels="%b")
# 处理数据
<- data7_2 |> filter(year(日期)==2023) |> select(日期,PM2.5:臭氧浓度)
d1 <- d1[,-1] |> ma(order=30,centre=TRUE)
ma colnames(ma)<-c('maPM2.5','maPM10','ma二氧化硫','ma一氧化碳','ma二氧化氮','ma臭氧浓度') # 重新命名列名称
<-d1 |> gather(2:7,key="指标",value="指标值")
df1<-ma |> as.data.frame() |> gather(1:6,key="ma指标",value="ma指标值")
df2<-cbind(df1,df2)
df
ggplot(df,aes(x=日期,y=指标值,color=指标))+geom_line(size=0.2)+# 绘制折线图
geom_line(aes(y=ma指标值,color=ma指标),size=0.8)+# 绘制折线图
scale_x_date(expand=c(0,0),date_breaks="2 month",date_labels="%b")+
guides(color="none")+
facet_wrap(~指标,ncol=3,scale="free") # 按指标分面,单独设置各分面图的y轴刻度
8 时间序列动态交互图
dygraphs::dygraph
,输入对象必须为xts
函数生成的时间序列对象。
library(dygraphs)
library(xts)
<- data7_2 |> filter(year(日期)==2023)
d1 <-xts(x=d1[,-c(1,3)],order.by=d1$日期) # 生成时间序列对象
dtsdygraph(data=dts$AQI,xlab="时间",ylab="AQI",main="AQI的动态交互图")
- dyRoller(rollPeriod=30)
dygraph(data=dts[,c(1,2,3)],xlab="时间",ylab="移动平均值",main="移动平均动态交互图")%>%
dyRoller(dygraph,rollPeriod=30) # 30日移动平均
plotly::plotly
library(plotly)
<- data7_2 |> filter(日期>="2023/1/1"&日期<="2023/3/31") |> select(1:3)
df # 绘制图形
<-plot_ly(data=df,x=~日期,y=~AQI,type="bar") # 绘制条形图
padd_markers(p,symbol=~质量等级) # 按因子设置符号
9 不规则时间序列图
9.1 数据准备
通过
zoo::rollmean
时间收盘价的5天、10天和20天的移动平均将日期变量转化为
id
变量
library(zoo)
<- data7_3 |> select(date:volume) |>
df mutate(ma5 = rollmean(close, k = 5, fill = NA, align = "right"),
ma10 = rollmean(close, k = 10, fill = NA, align = "right"),
ma20 = rollmean(close, k = 20, fill = NA, align = "right"),
date_index = seq_along(date)
)datatable(df)
9.2 K线图
线段图
geom_segment
+矩形图geom_rect
+折线图geom_line
scale_x_continuous
将横轴坐标刻度转化回日期型
<- df |> ggplot(aes(x = date_index)) +
p1 geom_segment(aes(x = date_index, xend = date_index, y = low, yend = high)) +
geom_rect(aes(xmin = date_index - 0.4, xmax = date_index + 0.4,
ymin = pmin(open, close), ymax = pmax(open, close),
fill = close > open)) +
geom_line(aes(y = ma5, color = "MA5"), size = 0.8) +
geom_line(aes(y = ma10, color = "MA10"), size = 0.8) +
geom_line(aes(y = ma20, color = "MA20"), size = 0.8) +
scale_fill_manual(values = c("TRUE" = "red", "FALSE" = "green")) +
scale_color_manual(values = c("MA5" = "blue", "MA10" = "orange", "MA20" = "purple")) +
scale_x_continuous(breaks = seq(1, nrow(df), by = 20),
labels = format(df$date[seq(1, nrow(df), by = 20)], "%Y-%m-%d"),
expand = c(0, 0)) +
theme_minimal() + guides(fill="none")+
theme(legend.position = "top",
axis.title.x = element_blank(),
axis.text.x = element_blank(),
plot.margin = margin(b = 0)) +
labs(x = "", y = "", title = "上证指数K线图", color = "移动平均线")
p1
9.3 交易量图
交易量通过条图
geom_bar
显示,width = 1
使直条间没有间隙
<- df |> ggplot(aes(x = date_index, y = volume)) +
p2 geom_bar(stat = "identity", aes(fill = close > open),width = 1) +
scale_fill_manual(values = c("TRUE" = "red", "FALSE" = "green")) +
scale_x_continuous(breaks = seq(1, nrow(df), by = 20),
labels = format(df$date[seq(1, nrow(df), by = 20)], "%Y-%m-%d"),
expand = c(0, 0)) +
theme_minimal() +
theme(legend.position = "none",
plot.margin = margin(t = 0)) +
labs(x = "", y = "", title = "")
p2
9.4 整合K线图和交易量图
::grid.arrange(p1,p2,ncol=1,
gridExtraheights = c(2, 1))