我正在分析复杂数据集中的时间模式,该数据集中包含多个环境变量以及来自各种动物物种的 Activity 数据。这些数据是通过多个实验设置收集的,每个设置的数据每分钟存储一次。该项目已经运行了几年,所以我的数据集很大。

我的一个数据集的前几行如下所示:

> head(setup_01)
DateTime                Film_number unused PIR Wheel Temperature LightOld LightDay LightNight LightUV IDnumbers    error mouse shrew vole rat frog rest extra_info odour
1 2015-03-10 12:27:10                  x   0       0       13.40  1471.34    -0.97    1331.29  700.42           no error     0     0    0   0    0    0                1
2 2015-03-10 12:28:10                  x   0       0       13.43  1471.38    -1.07    1291.11  731.32           no error     0     0    0   0    0    0                1
3 2015-03-10 12:29:10                  x   0       0       13.31  1471.24    -1.08    1368.57 1016.02           no error     0     0    0   0    0    0                1

由于我想将这些变量与整个季节中的各种自然周期(如日出和日落)相关联,因此我使用了maptools包来计算日出和日落时间
library(maptools)
gpclibPermit()

#set coordinates
crds=c(4.4900,52.1610)

# download the sunrise/sunset/etc data
setup_01$sunrise=sunriset(matrix(crds,nrow=1),dateTime=as.POSIXct(setup_01$DateTime),POSIXct.out=TRUE,direction="sunrise")
setup_01$sunset=sunriset(matrix(crds,nrow=1),dateTime=as.POSIXct(setup_01$DateTime),POSIXct.out=TRUE,direction="sunset")

#create a variable that's 0 except at sunrise, and one that's 0 except at sunset
setup_01$sunrise_act=0
setup_01$sunset_act=0
setup_01[abs(unclass(setup_01[,"DateTime"])-unclass(setup_01[,"sunrise"]$time))<30,]$sunrise_act=1
setup_01[abs(unclass(setup_01[,"DateTime"])-unclass(setup_01[,"sunset"]$time))<30,]$sunset_act=1

由于大多数动物的行为各不相同,具体取决于白天还是晚上,所以我使用日落/日出时间来计算一个新变量,该变量在晚上为0,在白天为1:
#create a variable that's 0 at night and 1 at daytime
setup_01$daytime=0
setup_01[setup_01[,"DateTime"]>setup_01[,"sunrise"]$time & setup_01[,"DateTime"]<setup_01[,"sunset"]$time,]$daytime=1

到目前为止,一切都很好... maptools甚至可以使用民用/航海/天文黄昏和黎明的开始,而不是日出和日落。

但是,这是我的问题开始的地方。我想对实验中的每一天进行编号。我不想像通常且容易做到的那样在午夜增加日计数,我想在日落时增加日计数(或者可能在将来的实验中增加一天中其他可移动的时间,例如日出,航海黄昏和黎明等)。 。由于日落并非每天同一时间发生,所以对我来说,这不是一个简单的解决问题。

我只想出了一个for -loop,这不是一个好方法。同样,鉴于我在几种设置中每分钟一次收集了超过6年的数据点,因此我可以坐下来观察构造板块的运动,而R贯穿整个循环,如下所示:
setup_01$day=0
day<-1
for(i in 1:nrow(setup_01)){
    setup_01[i,]$day<-day
    if(setup_01[i,]$sunset_act==1){
        day<-day+1
    }
}

除了丑陋和缓慢之外,这段代码还有一个大问题:它不能处理缺失的值。有时,由于设备故障,数小时或数天内根本没有记录数据。如果日落期间未记录任何数据,则以上代码不会增加日计数器。这意味着我还需要-以某种方式-并入日期/时间代码。自实验开始以来,很容易创建天数变量:
setup_01$daynumber<-as.integer(ceiling(difftime(setup_01$DateTime, setup_01$DateTime[1], units = "days")))

也许可以使用这些数字,也可以结合使用Heroka's nice rle -algorithm。

我已经使用dput从一种设置中获得了几个月的数据值(value),其中包括大量丢失的数据,以及新创建的变量(如本博文和Heroka's答案中所述)可用here

我一直在寻找更好,更好,特别是更快的东西,但一直想不出一个好办法。我摆弄了我的数据框的子集,但得出的结论是这可能是一种愚蠢的方法。我看过maptoolslubridateGeoLight。我搜索过Google,Stack Overflow和各种书籍,例如Hadley Wickham出色的AdvancedR。所有搜索都无济于事。也许我虽然缺少明显的东西。我希望这里有人可以帮助我。

最佳答案

我更喜欢基于预计算表的解决方案。这比较慢,但是我觉得更容易理解。然后,我使用dplyr安排所需的信息。

让我表明我的意思。为了举例,我创建了一个日落时间列表。当然,您将需要计算实际值。

library(dplyr)
n.obs=1000
set.seed(10)
t0 <- as.POSIXct('2015-03-08 18:00:00')
artificial.sunsets <- data.frame(num.day= seq(0,n.obs+35)) %>% mutate(sunset=cumsum(rlnorm(length(num.day))*30)+t0 + 24*3600*num.day)
artificial.sunsets包含日期和确切的日落时间,但可能还包含有关该日期的更多信息。

还有一些人工数据:
t0 <- as.POSIXct('2015-03-10 12:27:10')
test.data <- data.frame(DateTime=t0+ seq(0, n.obs*24*3600, by=3600), observation=rnorm(24*n.obs+1))

然后,可以使用以下命令找到先前的日落:
find.sunset.before <- function(x){
  cbind(x,artificial.sunsets %>% filter(sunset < x$DateTime) %>% tail(.,n=1))
}

data.with.sunset=test.data %>% rowwise() %>% do(find.sunset.before(.)) %>% ungroup()%>% mutate(rel.time = DateTime-sunset)
head(data.with.sunset)

然后,结果表将包含另外三列:1)相应的日期数字2)相应的日落时间,以及3)日落之后的时间。

当在另一张表中进行日期编号时,这应该可以防止丢失度量。您还可以轻松地修改算法以使用不同的时间,甚至可以应用多个时间。

更新

使用data.table可以更快地完成所有这些操作:
library(data.table)
dt1 <- data.table(artificial.sunsets)
dt2 <- data.table(test.data)

dt1[,DateTime:=sunset]

setkey(dt1, DateTime)
setkey(dt2, DateTime)

r <- dt1[dt2,roll=TRUE]
r[,time.diff:=DateTime-sunset]

我尝试使用system.time对其进行计时以进行1000次观察-前一个过程大约需要1m,data.table解决方案为0.011s。

09-27 23:10