【问题标题】:How can I create ribbon area for a time series data with ggplot2?如何使用 ggplot2 为时间序列数据创建功能区区域?
【发布时间】:2020-06-16 07:08:26
【问题描述】:

我正在尝试可视化时间序列数据。我有一组 5 个记录器,它们指示雪运动距离和一个可能对雪运动有影响的 环境变量。这就是为什么将它们绘制在一起以查看雪运动(由记录仪检测到)是否受到这些环境因素的影响是有意义的。我有一个文件,其中包含气象站数据每个记录器的 5 个文件每 5 分钟测量一次天气数据,而记录器只要有运动就测量一次!到目前为止,我已经设法将它们一起可视化,但是,我的教授希望我通过呈现灰色区域(而不是 5 行)来将记录器可视化为一个组,该区域始终显示最小值和最大值记录器在某个时间点。我正在使用 ggplot2。我试图通过使用功能区 geom_ribbon 来制作这样的区域,但我的数据集并不是那么直接。这条线正在交叉,通常是具有最小值和最大值切换的记录器。我不知道将它们加入单个数据集中是否会有所帮助,但这也是不可能的,因为它们的长度不同。此外,它不像所有 5 个记录器都同时进行测量。他们仅在有运动时记录。这是我的代码和它创建的图表。不幸的是,我不确定如何重现数据。我很高兴能以某种方式与您分享。

#install.packages("patchwork")
library(ggplot2)
library(scales)
library(patchwork)

Sys.setlocale(category = "LC_ALL", locale = "english")

startTime <- as.Date("2017-10-01")
endTime <- as.Date("2018-06-30")
start_end <- c(startTime,endTime)

################################################## FALL LINE 1 #########################################################

logger1 <- read.csv("F1_17_18_167.csv",header=TRUE, sep=";")
logger1$date <- as.Date(logger1$Date, "%d.%m.%Y")

logger2 <- read.csv("F1_17_18_186.csv",header=TRUE, sep=";")
logger2$date <- as.Date(logger2$Date, "%d.%m.%Y")

logger3 <- read.csv("F1_17_18_031.csv",header=TRUE, sep=";")
logger3$date <- as.Date(logger3$Date, "%d.%m.%Y")

logger4 <- read.csv("F1_17_18_091.csv",header=TRUE, sep=";")
logger4$date <- as.Date(logger4$Date, "%d.%m.%Y")

logger5 <- read.csv("F1_17_18_294.csv",header=TRUE, sep=";")
logger5$date <- as.Date(logger5$Date, "%d.%m.%Y")

station <- read.csv("aggregates.csv",header=TRUE, sep=",")
station$date <- as.Date(station$Group.1, "%Y-%m-%d")


ggplot()+
  geom_line(data = station, aes(x = date, y = Mean_snowheight ,color = "Mean Snowheight"),na.rm = TRUE, size = 1)+
  scale_x_date(limits=start_end,breaks=date_breaks("1 month"),labels=date_format("%b %y"))+
  scale_y_continuous(limits= c (0,115))

ggplot()+
  geom_line(data = logger1, aes(x = date, y = AccuDist, color = "167 (mid-bottom)"),na.rm= TRUE, size = 1)+
  geom_line(data = logger2, aes(x = date, y = AccuDist, color = "186 (top-middle)"),na.rm= TRUE, size = 1)+
  geom_line(data = logger3, aes(x = date, y = AccuDist, color = "31 (top)"),na.rm= TRUE, size = 1)+
  geom_line(data = logger4, aes(x = date, y = AccuDist, color = "91 (bottom)"),na.rm= TRUE, size = 1)+
  geom_line(data = logger5, aes(x = date, y = AccuDist, color = "294 (middle)"),na.rm= TRUE, size = 1)+
  geom_line(data = station, aes(x = date, y = Mean_snowheight*11.49 ,color = "Mean snowheight"),na.rm = TRUE, size = 1) +
  ggtitle("Fall line 1") +
  labs(color = "")+
  xlab("Season 17/18")+
  ylab("Accumulated Distance [mm]")+
  scale_x_date(limits=start_end,breaks=date_breaks("1 month"),labels=date_format("%b %y"))+
  scale_y_continuous(sec.axis = sec_axis(~./11.49,name = "Mean snowheight [cm]"),limits = c(0,1500))+
  scale_color_manual("", guide = "legend",
                     values = c("167 (mid-bottom)"= "darkorange2",
                                "186 (top-middle)" = "darkgreen",
                                "31 (top)" = "red",
                                "91 (bottom)" = "blue",
                                "294 (middle)" = "purple",
                                "Mean snowheight" = "black"))+
  theme(legend.position="bottom",
        #legend.title = element_blank(),
        axis.text.x = element_text(angle = 50, size = 10 , vjust = 0.5),
        axis.text.y = element_text(size = 10, vjust = 0.5), 
        panel.background = element_rect(fill = "gray100"),
        plot.background = element_rect(fill = "gray100"),
        panel.grid.major = element_line(colour = "lightblue"),
        plot.margin = unit(c(1, 1, 1, 1), "cm"),
        plot.title = element_text(hjust = 0.5, size = 22))

您可以看到这段代码生成的图表:

如果您第二次忽略环境因素(黑线),则剩下的是每个伐木工在冬季期间的累积雪运动距离(彩色线)。我的目标是填充始终位于最低线和最高线之间的区域。

如果我需要在某处上传数据,请告诉我。这是记录器数据的样子:data table

提前致谢。

问候,

佐林

【问题讨论】:

  • 我想我可以在这里为您提供帮助,但是您可以上传您的数据集吗?为了共享一些数据,请不要共享文件(文件可能已损坏,此处的链接很危险),但请作为数据集加载并共享dput(dataset) 的输出。您能以这种方式至少共享 2 或 3 个记录器数据集吗? dput() 的文本输出可能很长(取决于您的数据集),因此您始终可以使用 pastebin 共享输出。然后我们复制并粘贴它以重新创建 df,因为它是为您准备的。
  • 您好@chemdork123,感谢您的快速回复。这是输出。我不确定我是否按照您想要的方式进行了操作。对于一些记录器,我设法复制了整个输出。对于其他记录器和站点数据,我创建了尽可能大的子集。 Logger1 Logger2 Logger3 Logger4 Logger5 Station (weather) data 提前致谢。问候,佐林
  • @chemdork123 另外我需要澄清一下,上面代码中的这一行可以忽略。我只是用来辅助辅助轴缩放的近似。 ggplot()+ geom_line(data = station, aes(x = date, y = Mean_snowheight ,color = "Mean Snowheight"),na.rm = TRUE, size = 1)+ scale_x_date(limits=start_end,breaks=date_breaks("1 month"),labels=date_format("%b %y"))+ scale_y_continuous(limits= c (0,115))
  • 我认为应该可以。甚至数据子集也能很好地工作。我将尝试整理一个可行的例子来说明我将如何应对这里的挑战。我想我只需要“记录器”数据集,但让我看看。

标签: r ggplot2


【解决方案1】:

这实际上比起初看起来更难。据我了解,您的目标是填写“最低”和“最高”线之间的线图中的区域。由于最低线和最高线可能会在整个绘图中改变位置,因此这变得更加困难,因此您不能简单地选择在一个日志和另一个日志之间进行绘图。由于您的 x 轴值是日期,因此并非所有日志都在同一日期和时间收集数据。

首先,我将忽略您添加的一些个人审美,并删除您包含的平均雪高线(来自数据框station),以便向您展示我拥有的解决方案。

数据准备

首先,我注意到您为每个单独的测井站数据集(logger1logger5)添加了一个 geom_line() 调用。虽然该方法确实有效(并且您以一种为您提供所需解决方案的方式进行操作),但将所有日志组合到一个数据集中是更好的做法,这对于我提出的解决方案是必要的无论如何工作。幸运的是,这样做非常简单:只需使用 rbind() 组合数据集。至关重要的是 - 您需要为每个列(此处称为 id)创建一个新列,以维护原始测井站的身份。然后,您可以使用新的 id 列作为您的 color= 美学,并使用一个 geom_line() 调用绘制所有 5 条线。

我遇到的一个小问题是您的数据集的列名略有不同(有些是大写,有些不是...)。它们都处于相同的顺序,因此在组合之前使它们都相同并不难……它只是增加了一个步骤。最后,我将date 列转换为日期格式。

# create the id column
logger1$id <- 'logger1'
logger2$id <- 'logger2'
logger3$id <- 'logger3'
logger4$id <- 'logger4'
logger5$id <- 'logger5'

# fixing inconsistency in column names
my_column_names <- names(logger1)

names(logger2) <- my_column_names
names(logger3) <- my_column_names
names(logger4) <- my_column_names
names(logger5) <- my_column_names

# make one big df
loggers <- rbind(logger1, logger2, logger3, logger4, logger5)

loggers$date <- as.Date(loggers$date)

您现在可以以更简单的方式重新创建绘图:

ggplot(loggers, aes(x=date, y=AccuDist)) + theme_bw() +
  geom_line(aes(color=id), size=1)

找到运行最小值和最大值

为了创建填充,我使用了geom_ribbon(),这需要美学yminymax。不过,您必须先设置它们,并且它们需要“运行最小值”和“运行最大值”,这意味着它们会随着您处理数据而改变。为此,我使用了如下所示的两个函数min_vect()max_vect()

# find the "running maximum"
max_vect <- function(ac) {
  curr_max <- 0
  return_vector <- vector(mode = 'numeric', length=length(ac))
  for(i in 1:length(ac)) {
    if(ac[i] > curr_max) {
      curr_max <- ac[i]
    }
    return_vector[i] <- curr_max
  }
  return(return_vector)
}

# find the "running minimum"
min_vect <- function(ac) {
  curr_min <- max(ac)
  return_vector <- vector(mode = 'numeric', length=length(ac))
  for(i in length(ac):1) {
    if(ac[i] < curr_min) {
      curr_min <- ac[i]
    }
    return_vector[i] <- curr_min
  }
  return(return_vector)
}

这个想法是,对于最大值,您逐步遍历(有序)向量,如果该数字高于先前的最大数字,则它将成为新的最大值。相同的策略用于运行最小值,尽管我们必须反向遍历有序向量。

为了应用函数来创建新列,数据集需要先排序才能正常工作:

# must arrange by date and time first!
loggers <- loggers %>% arrange(date, TIME)

# add your new columns
loggers$min_Accu <- min_vect(loggers$AccuDist)
loggers$max_Accu <- max_vect(loggers$AccuDist)

结局

现在,剧情。基本上是一样的,如上所述,我使用的是geom_ribbon()。作为奖励,我还使用scale_color_discrete() 来设置图例标题和标签,只是为了向您展示您可以在之后对其进行编码(这仍然比单独的geom_line() 调用更容易。

logger_list <- c('Log 1', 'Log 2', 'Log 3', 'Log 4', 'Log 5')

ggplot(loggers, aes(x=date, y=AccuDist)) +
  theme_bw() +
  geom_ribbon(aes(ymin=min_Accu, ymax=max_Accu), alpha=0.2) +
  geom_line(aes(color=id), size=1) +
  scale_color_discrete(name='Log ID Num', labels=logger_list)

【讨论】:

  • 谢谢老板。我根据自己的需要对其进行了改造,效果很好。感谢您合并我的记录器的建议。我真的应该学会用“for”和“if”循环来创建函数。
  • 很高兴它对你有用!如果您有一些常规处理发生(就像这里显然发生的事情),您总是可以将所有代码组合到一个脚本(*.R 文件)中,然后 source() 在控制台中运行它。这绝对是组合这些数据的方法。否则,是的,for 循环可以解决。如果您对如何做到这一点或如何改进您现在所做的工作有具体问题,我建议您在 SO 上发布另一个问题。
  • 祝福你。是的,R是惊人的。我真的应该做得更好。它有很大的潜力。作为一名学生,金钱是一个因素,我买不起 STATISTICA 的个人副本。我的主要目标是熟悉 R :)
  • 非常值得。如果你想要一些高质量的“基本 R”和“基本数据分析”的东西,我强烈推荐 R 中的 swirl 包。安装,加载库,然后在 Rstudio 控制台中运行 swirl(),你会得到一个非常好的控制台中有关各种核心 R 概念的教程。这基本上是我开始的方式,并且对 Tidy 数据的基础知识(结合上述记录器文件的方法)有很好的解释。对我来说,它比任何在线课程都要好。
  • 一定会去看看的!
猜你喜欢
  • 2020-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-06
相关资源
最近更新 更多