5年内不规则日期的移动平均值

4

我有大量文件(约1200个),每个文件都包含有关地下水位高度的大型时间序列数据。每个文件的起始日期和序列长度都不同。日期之间可能存在大量的数据空缺,例如(这是该文件的一小部分):

Date        Height (cm)
14-1-1980   7659
28-1-1980   7632
14-2-1980   7661
14-3-1980   7638
28-3-1980   7642
14-4-1980   7652
25-4-1980   7646
14-5-1980   7635
29-5-1980   7622
13-6-1980   7606
27-6-1980   7598
14-7-1980   7654
28-7-1980   7654
14-8-1980   7627
28-8-1980   7600
12-9-1980   7617
14-10-1980  7596
28-10-1980  7601
14-11-1980  7592
28-11-1980  7614
11-12-1980  7650
29-12-1980  7670
14-1-1981   7698
28-1-1981   7700
13-2-1981   7694
17-3-1981   7740
30-3-1981   7683
14-4-1981   7692
14-5-1981   7682
15-6-1981   7696
17-7-1981   7706
28-7-1981   7699
28-8-1981   7686
30-9-1981   7678
17-11-1981  7723
11-12-1981  7803
18-2-1982   7757
16-3-1982   7773
13-5-1982   7753
11-6-1982   7740
14-7-1982   7731
15-8-1982   7739
14-9-1982   7722
14-10-1982  7794
15-11-1982  7764
14-12-1982  7790
14-1-1983   7810
28-3-1983   7836
28-4-1983   7815
31-5-1983   7857
29-6-1983   7801
28-7-1983   7774
24-8-1983   7758
28-9-1983   7748
26-10-1983  7727
29-11-1983  7782
27-1-1984   7801
28-3-1984   7764
27-4-1984   7752
28-5-1984   7795
27-7-1984   7748
27-8-1984   7729
28-9-1984   7752
26-10-1984  7789
28-11-1984  7797
18-12-1984  7781
28-1-1985   7833
21-2-1985   7778
22-4-1985   7794
28-5-1985   7768
28-6-1985   7836
26-8-1985   7765
19-9-1985   7760
31-10-1985  7756
26-11-1985  7760
20-12-1985  7781
17-1-1986   7813
28-1-1986   7852
26-2-1986   7797
25-3-1986   7838
22-4-1986   7807
27-5-1986   7785
24-6-1986   7787
26-8-1986   7744
23-9-1986   7742
22-10-1986  7752
1-12-1986   7749
17-12-1986  7758

我想计算5年内的平均身高。例如,以1980年1月14日为例,加上5年后是1985年1月14日,再加5年后是1990年1月14日......每次计算平均值时数据点数量都是不同的。由于5年后的日期很可能不在数据集中,因此我认为需要告诉R如何在特定时间段内进行平均值计算。
我在互联网上搜索了一下,但没有找到符合我的需求的内容。很多有用的软件包(如uts、zoo、lubridate)和函数aggregate都被忽略了。与其接近解决方案,我对哪种方法最适合我的问题越来越困惑。
非常感谢您的帮助!

首先,您可以将它们全部读取并合并为一个单独的数据框。 - vagabond
也许可以看一下zoo包中的rollapply函数。 - Paul Hiemstra
4个回答

5
如@vagabond所指出的那样,您需要将1200个文件合并成一个数据框(使用plyr包可以轻松实现: data.all <- adply(dir([DATA FOLDER]), 1, read.csv))。
一旦您获得了数据,第一步是将Date列转换为适当的POSIXct日期数据。目前,数据似乎是字符串,我们希望它们具有底层的数字表示(这是POSIXct的作用):
library(lubridate)
df$date.new <- as.Date(dmy(df$Date))

       Date Height   date.new
1 14-1-1980   7659 1980-01-14
2 28-1-1980   7632 1980-01-28
3 14-2-1980   7661 1980-02-14
4 14-3-1980   7638 1980-03-14
5 28-3-1980   7642 1980-03-28
6 14-4-1980   7652 1980-04-14

请注意,date.new列看起来像是一个字符串,但实际上它是日期数据,并且可以使用数值运算(加法、比较等)进行处理。
接下来,我们可能会构建一组日期区间,以便计算平均值。你的例子提到了5年,但是根据你提供的数据,这不是一个很具有说明性的例子。因此,在此我正在创建从1980年1月14日到1985年1月14日之间的每一天开始的1年期间。
date.start <- as.Date(as.Date('1980-01-14') : as.Date('1985-01-14'), origin = '1970-01-01')
date.end <- date.start + years(1)
dates <- data.frame(start = date.start, end = date.end)

       start        end
1 1980-01-14 1981-01-14
2 1980-01-15 1981-01-15
3 1980-01-16 1981-01-16
4 1980-01-17 1981-01-17
5 1980-01-18 1981-01-18
6 1980-01-19 1981-01-19

然后我们可以使用dplyr软件包遍历该数据框的每一行并计算Height的摘要平均值:

library(dplyr)
df.mean <- dates %>% 
    group_by(start, end) %>% 
    summarize(height.mean = mean(df$Height[df$date.new >= start & df$date.new < end]))

       start        end height.mean
      <date>     <date>       <dbl>
1 1980-01-14 1981-01-14    7630.273
2 1980-01-15 1981-01-15    7632.045
3 1980-01-16 1981-01-16    7632.045
4 1980-01-17 1981-01-17    7632.045
5 1980-01-18 1981-01-18    7632.045
6 1980-01-19 1981-01-19    7632.045

据我理解,@BartM正在寻找日期和该日期5年后之间的数据点的平均值。您还为每个缺失的日期创建新的时间段。但这不是BartM要求的,以我看来。 - h3rm4n
这个问题在那一点上有点模糊,但是如果你只想基于现有数据中的日期来确定周期,那么 dates.start <- df$date.new 就可以了。 - jdobres

1

foverlaps函数在我看来是这种情况的完美选择:

library(data.table)
library(lubridate)

# convert to a data.table with setDT()
# convert the 'Date'-column to date-format
# create a begin & end date for the required period
setDT(dat)[, Date := as.Date(Date, '%d-%m-%Y')                      
           ][, `:=` (begindate = Date, enddate = Date + years(1))]

# set the keys (necessary for the foverlaps function)
setkey(dat, begindate, enddate)

res <- foverlaps(dat, dat, by.x = c(1,3))[, .(moving.average = mean(i.Height)), Date]

结果为:
> head(res,15)
          Date moving.average
 1: 1980-01-14       7633.217
 2: 1980-01-28       7635.000
 3: 1980-02-14       7637.696
 4: 1980-03-14       7636.636
 5: 1980-03-28       7641.273
 6: 1980-04-14       7645.261
 7: 1980-04-25       7644.955
 8: 1980-05-14       7646.591
 9: 1980-05-29       7647.143
10: 1980-06-13       7648.400
11: 1980-06-27       7652.900
12: 1980-07-14       7655.789
13: 1980-07-28       7660.550
14: 1980-08-14       7660.895
15: 1980-08-28       7664.000

现在,您可以得到每个日期的平均值,这些值位于该日期和该日期之后一年的范围内。

-1

嘿,我看到你的问题后刚刚尝试了一下!!!在一个样本数据框上运行。在理解代码之后,请在您的数据框上尝试并告诉我!

顺便说一句,我使用了仅为期2个月(2 * 30 = 约2个月)的间隔,而不是5年的间隔!

df = data.frame(Date = c("14-1-1980", "28-1-1980", "14-2-1980", "14-3-1980", "28-3-1980",
                     "14-4-1980", "25-4-1980", "14-5-1980", "29-5-1980", "13-6-1980:",
                     "27-6-1980", "14-7-1980", "28-7-1980", "14-8-1980"), height = 1:14)

# as.Date(df$Date, "%d-%m-%Y")

df1 = data.frame(orig = NULL, dest = NULL, avg_ht = NULL)
orig = as.Date(df$Date, "%d-%m-%Y")[1]
dest = as.Date(df$Date, "%d-%m-%Y")[1] + 2*30 #approx 2 months
dest_final = as.Date(df$Date, "%d-%m-%Y")[14]

while (dest < dest_final){
  m = mean(df$height[which(as.Date(df$Date, "%d-%m-%Y")>=orig &
                           as.Date(df$Date, "%d-%m-%Y")<dest )])
  df1 = rbind(df1,data.frame(orig=orig,dest=dest,avg_ht=m))
  orig = dest
  dest = dest + 2*30
  print(paste("orig:",orig, " + ","dest:",dest))
}

> df1
        orig       dest avg_ht
1 1980-01-14 1980-03-14    2.0
2 1980-03-14 1980-05-13    5.5
3 1980-05-13 1980-07-12    9.5

希望这对你也有用


由于我正在使用while()循环,所以代码会变得很慢。但是我希望这能为您的探索提供一个实际的开始!!请向我报告结果! - joel.wilson
你的例子运行得很好,当我在所有文件上运行它时,我会看看它的效果如何。非常感谢。 - BartM

-1

这是我最好的尝试,但请记住,我是根据年份而不是完整日期进行工作的,即基于您提供的示例,我正在对1980年初至1984年底进行平均。

dat<-read.csv("paixnidi.csv")
install.packages("stringr")
library(stringr)
dates<-dat[,1]
#extract the year of each measurement
years<-as.integer(str_sub(dat[,1], start= -4))
spread_y<-years[length(years)]-years[1]

ind<-list()
#find how many 5-year intervals there are
groups<-ceiling(spread_y/4)
meangroups<-matrix(0,ncol=2,nrow=groups)
k<-0
for (i in 1:groups){
  #extract the indices of the dates vector whithin the 5-year period
  ind[[i]]<-which(years>=(years[1]+k)&years<=(years[1]+k+4),arr.ind=TRUE)
  meangroups[i,2]<-mean(dat[ind[[i]],2])
  meangroups[i,1]<-(years[1]+k)
  k<-k+5
}

colnames(meangroups)<-c("Year:Year+4","Mean Height (cm)")

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