【问题标题】:Calculating the median of a time series, by 8 every 8 hours计算时间序列的中位数,每 8 小时乘 8
【发布时间】:2016-04-21 18:58:03
【问题描述】:

我是 R 新手,我必须计算时间序列的平均值,包含 5 年,每小时采集的臭氧数据等。

我的 df 看起来像:

structure(list(date = structure(c(1L, 1L, 1L, 1L), .Label = "01.01.2010", class = "factor"), 
day.of = c(1L, 1L, 1L, 1L), time = structure(1:4, .Label = c("00:00", 
"01:00", "02:00", "03:00"), class = "factor"), SVF_Ray = c(1L, 
1L, 1L, 1L), Gmax = c(0, 0, 0, 0), Ta = c(-1.3, -1.2, -1.2, 
-1.2), Tmrt = c(-19.3, -12.1, -12, -12.1), PET = c(-10.4, 
-8.7, -8.7, -8.7), PT = c(-11.3, -9.3, -9.3, -9.3), Ozon = c(61.35, 
62.65, 63.4, 63.85), rDatum = structure(c(14610, 14610, 14610, 
14610), class = "Date"), year = c(2010, 2010, 2010, 2010), 
month = c(1, 1, 1, 1), day = c(1, 1, 1, 1), hour = c(0, 1, 
2, 3)), .Names = c("date", "day.of", "time", "SVF_Ray", "Gmax", 
"Ta", "Tmrt", "PET", "PT", "Ozon", "rDatum", "year", "month", 
"day", "hour"), row.names = c(NA, 4L), class = "data.frame")

我想每 8 小时计算一次 Ozon 的平均值,所以每天计算一系列 4 个平均值。我的数据安排如下:

Datum_Ozon$rDatum <- as.Date(data$date, format="%d.%m.%Y")

Datum_Ozon$hour<-as.numeric(unlist(strsplit(as.character(df$time), ":"))[seq(1, 2 * length(df$time), 2)])

格式是数字

但我不知道如何实现我的目标。提前致谢!

【问题讨论】:

  • 使用 dput(DF_Ozone) 提供(部分)数据比数据图片更有用
  • 感谢您的评论,我正在想办法。
  • 如果我们不知道time是什么类型的数据,也很难给出答案。但是,假设它是某种时间对象,您可以使用ifelse 设置一些条件(即 8 小时的块)来创建一个新的分组变量。
  • 很抱歉,我不知道如何上传我的 df 的一部分,我知道这会有所帮助。
  • 谢谢你,希望我做对了

标签: r time time-series mean


【解决方案1】:

如果您的数据是规则且完整的(即,每个小时都有一条记录),那么以下基本 R 代码应该可以解决问题:

# Get the number of 8 hour intervals
intervalCnt <- nrow(df) / 8L

# add a grouping vector to your data
df$group <- rep(1:intervalCnt, each=8)

# get the median for each interval, keep year var around for later
intervalMedian <- aggregate(var~group + day + month + year, data=df, FUN=median)

请注意,此解决方案依赖于数据具有规则结构的假设,即每小时都有一条记录。如果缺少感兴趣的度量,即 NA,那么只需将 na.rm 添加到聚合函数将返回感兴趣的统计信息:

# get the median for each interval
intervalMedian <- aggregate(var~group + day + month + year, data=df, FUN=median, na.rm=T)

如果您有一天中某个小时的变量,这是检查数据规律性的简单方法:

table(df$hourOfDay)

这个函数的结果是每小时的频率计数。计数应该相等。另外要检查的是,第一次观察是在最后一次观察之后的一小时内开始的,即如果观察时间 1 == "00:00",那么最后一次观察的时间应该是 23:00。

要按年份绘制 8 小时期间的平均值,您可以再次使用聚合:

intervalMeans.year <- aggregate(var~group, data=intervalMedian,
                                FUN=mean, na.rm=T)

intervalMedian data.frame 中包含 group、day、month 和 year 变量允许许多不同的聚合。例如,稍作调整,就可以得到一个变量在 5 年内每个时间段-日-月的平均值:

intervalMedian$periodDay <- rep(1:3, length.out=intervalMedian)
intervalMeans.dayMonthPeriod <- aggregate(var~periodDay+day+month,
                                          data=intervalMedian, FUN=mean, na.rm=T)

【讨论】:

  • 正如您所提到的,这种方法的问题在于数据通常不完整。这种方法会很顺利地进行,您将无法知道您的间隔和分配不正确。
  • 确实,很遗憾我的数据不完整,NA 有很多小时
  • @boshek 这是数据结构的问题。有时它是完整的,有时它不是。当数据不规则时,此选项将不起作用。如果它是常规的,这是一个非常简单的解决方案。在实施解决方案之前,分析师需要了解他们的数据。如果,正如上面发布的 OP,数据在结构上是规则的,即使值丢失,只要将 na.rm 参数设置为 TRUE,这将起作用。
  • @lmo 我完全同意你所说的一切,只是指出,对于初学者来说,你真的需要小心,或者正如你所说的“在实施解决方案之前了解他们的数据”
  • @boshek 我同意你的警告。我添加了一些 OP 可以使用的快速检查,以大大减少一些未被注意到的违规行为的主要来源。
【解决方案2】:

这是一个使用dplyr 管道而不是plyr 方法以及ifelse() 的基本示例。这里的一切都是自包含的:

library(dplyr)

## OP data
df <- 
structure(list(date = structure(c(1L, 1L, 1L, 1L), .Label = "01.01.2010", class = "factor"), 
day.of = c(1L, 1L, 1L, 1L), time = structure(1:4, .Label = c("00:00", 
"01:00", "02:00", "03:00"), class = "factor"), SVF_Ray = c(1L, 
1L, 1L, 1L), Gmax = c(0, 0, 0, 0), Ta = c(-1.3, -1.2, -1.2, 
-1.2), Tmrt = c(-19.3, -12.1, -12, -12.1), PET = c(-10.4, 
-8.7, -8.7, -8.7), PT = c(-11.3, -9.3, -9.3, -9.3), Ozon = c(61.35, 
62.65, 63.4, 63.85), rDatum = structure(c(14610, 14610, 14610, 
14610), class = "Date"), year = c(2010, 2010, 2010, 2010), 
month = c(1, 1, 1, 1), day = c(1, 1, 1, 1), hour = c(0, 1, 
2, 3)), .Names = c("date", "day.of", "time", "SVF_Ray", "Gmax", 
"Ta", "Tmrt", "PET", "PT", "Ozon", "rDatum", "year", "month", 
"day", "hour"), row.names = c(NA, 4L), class = "data.frame")

df %>%
  mutate(DayChunk=ifelse(hour %in% c(0:7),"FirstThird",
         ifelse(hour %in% c(8:15), "SecondThird"
              ,"ThirdThird")
         )) %>%
  group_by(Date, DayChunk) %>%
  summarise(MedOzon=median(Ozon))

【讨论】:

  • 我正在努力使您的解决方案发挥作用,但遗憾的是它不会。我做错了什么?非常感谢您的支持!
  • 对我来说很好。具体说明什么不起作用。上面的例子运行良好。
  • 我相信您的 group_by 还需要将日期与 DayChunk 一起合并,否则您的摘要长度为 3 个元素,而不是数据集长度每天 3 个。
【解决方案3】:

查找函数 seq.POSIXt。有一些选项可以指定开始和停止间隔。此功能旨在创建时间序列。对于您的问题:

myseq<-seq(ISOdate(2010,01,01, 00, 00, 00, tz="GMT"), to=ISOdate(2016,01,05), by = "8 hour")

使用 ISOdate 函数设置开始和停止时间。如果您要大量使用时间,我建议研究函数 strptime 和 POSIXlt/ct 时间类。 现在定义了中断并假设您的数据框 (Datum_Ozon) 中有一个名为“datetime”的列,然后使用“cut”对数据进行分组/子集。

Datum_Ozon$datetime<-as.POSIXct(paste(as.character(Datum_Ozon$date),
     as.character(Datum_Ozon$time)), "%d.%m.%Y %H:%M", tz="GMT" )

library(dplyr)
summarize(group_by(Datum_Ozon, cut(Datum_Ozon$datetime, myseq)), mean(Ozon))

【讨论】:

  • 当我使用你的解决方案时,第二步返回:Error in 1:intervalCnt : argument of length 0 非常感谢您的支持!
  • 好的,上面做了一些修改。 myseq 是在中午而不是午夜开始的。所有时间都设置为格林威治标准时间。新添加的日期时间列现在是 POSIXct 类。现在一切都应该一致且没有错误。如果不告诉我。
  • @ Dave2e,谢谢,这对我也有用。现在我的 df 为 5.477 x2。下一步是用 DOY (1...365) 画一个 grapg。每个 DOY 应该代表时间序列中每个 DOY (1...365) 的平均值。任何的想法?另外,如何将 GMT 设置为 GMT +1?
  • 查看“时区”帮助并使用 OlsonNames() 获取可用时区代码的列表。选择您居住的地方并在上面替换它。此时绘图很简单,您有一个新的数据框,其中 datetime 作为一列,mean 作为第二列,因此 plot() 应该可以工作。 x 标签的“julian”命令为 0-365 天。
  • 我知道了如何管理时区。但是,如果我绘图,它当然会在 x 轴上绘制 5477 个条目,而我想要显示的是臭氧在 365 天的线图(基于五年测量)处的图(正态分布) )。那么我怎样才能得到它呢?
猜你喜欢
  • 2021-03-18
  • 2022-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多