如何为连续日期的每个夜间时段创建唯一 ID?

How do I create a unique ID for each night-time period across consecutive dates?

我连续几天连续收集数据。每个观察已经包含变量 datetime(一个 POSIXct 对象)和 par(一个数字对象)。我想创建一个名为 ID 的新变量,它将是与单个夜间时段关联的唯一编号。我将夜间定义为 par = 0 的所有观察值。下面连续 3 天给出了我希望我的数据看起来像的示例。

注意:我的实际数据是以 15 分钟为间隔获取的,但为了便于查看,我截断了此示例。

              datetime    par    ID
1  2015-04-23 00:00:00    0.0     1
2  2015-04-23 08:00:00    0.0     1
3  2015-04-23 12:00:00  817.7     0
4  2015-04-23 19:00:00    0.0     2
5  2015-04-24 00:00:00    0.0     2
6  2015-04-24 08:00:00    0.0     2
7  2015-04-24 12:00:00  269.9     0
8  2015-04-24 19:00:00    0.0     3
9  2015-04-25 00:00:00    0.0     3
10 2015-04-25 08:00:00    0.0     3
11 2015-04-25 12:00:00 1701.8     0
12 2015-04-25 19:00:00    0.0     4
13 2015-04-25 23:00:00    0.0     4

我希望非夜间 (par !=0) 观察的 ID = 0。第一个夜间时段发生在 2015 年 4 月 23 日,所以我希望它的 ID = 1。然后我想添加1 到每个随后的夜间时段 ID。

有什么想法吗?尽管付出了很多努力,但我仍无法实现上述结果。提前谢谢你。

几种方法,都采用运行-length-encoding

data.table

这利用了时间段按日期分组的事实,因此我们可以使用 运行-length-encoding 对同一日期的值进行分组。然后任何带有 par > 0 的东西都可以设置为 0。

library(data.table)

setDT(df)
## explicitly ordering the data.table
df[order(datetime), ID := rleid(as.Date(datetime))][ par > 0, ID := 0]
df
#               datetime    par ID
# 1:  2015-04-23 00:00:00    0.0  1
# 2:  2015-04-23 08:00:00    0.0  1
# 3:  2015-04-23 12:00:00  817.7  0
# 4:  2015-04-23 19:00:00    0.0  2
# 5:  2015-04-24 00:00:00    0.0  2
# 6:  2015-04-24 08:00:00    0.0  2
# 7:  2015-04-24 12:00:00  269.9  0
# 8:  2015-04-24 19:00:00    0.0  3
# 9:  2015-04-25 00:00:00    0.0  3
# 10: 2015-04-25 08:00:00    0.0  3
# 11: 2015-04-25 12:00:00 1701.8  0
# 12: 2015-04-25 19:00:00    0.0  4
# 13: 2015-04-25 23:00:00    0.0  4

(这是我的首选解决方案,因为它可以处理任何日期,正确排序数据,而且效率很高,因为,你知道,data.table...)


基地

如果'night time period'总是被'daytime'句点隔开,那么你也可以这样做

## create an ID column set the 'daytime' periods to 0
df[ df$par != 0, "ID"] <- 0

## get the run-length-encoding of the ID column
r <- rle(is.na(df$ID))

## the length of the rle where the value is TRUE gives us teh number of NAs in each 'group', so we can replicate the rle value by that amount
rep(1:length(r$lengths[r$values]), r$length[r$values])
# [1] 1 1 2 2 2 3 3 3 4 4

## so you can fill the rest of hte column with these values

df[ df$par == 0, "ID"] <- rep(1:length(r$lengths[r$values]), r$length[r$values])
df
#               datetime    par ID
# 1  2015-04-23 00:00:00    0.0  1
# 2  2015-04-23 08:00:00    0.0  1
# 3  2015-04-23 12:00:00  817.7  0
# 4  2015-04-23 19:00:00    0.0  2
# 5  2015-04-24 00:00:00    0.0  2
# 6  2015-04-24 08:00:00    0.0  2
# 7  2015-04-24 12:00:00  269.9  0
# 8  2015-04-24 19:00:00    0.0  3
# 9  2015-04-25 00:00:00    0.0  3
# 10 2015-04-25 08:00:00    0.0  3
# 11 2015-04-25 12:00:00 1701.8  0
# 12 2015-04-25 19:00:00    0.0  4
# 13 2015-04-25 23:00:00    0.0  4

cumsum:

df$ID2 <- 0
sel <- df$par == 0
df$ID2[sel] <- cumsum(!sel)[sel] + 1

#      par ID ID2
#1     0.0  1   1
#2     0.0  1   1
#3   817.7  0   0
#4     0.0  2   2
#5     0.0  2   2
#6     0.0  2   2
#7   269.9  0   0
#8     0.0  3   3
#9     0.0  3   3
#10    0.0  3   3
#11 1701.8  0   0
#12    0.0  4   4
#13    0.0  4   4

我会首先将您的 POSIXct 日期转换为 POSIXlt 日期,因为这些日期具有可以更轻松地确定诸如天数之类的属性。

df[["datetime"]] <- as.POSIXlt(df[["datetime"]])

然后我们可以使用字段 ydayhour 来获取一个数值,其中 24 小时内中午之后的任何值都具有相同的值。这仅在您所有时间都在同一年时才有效。否则,您将不得不使用 year 属性来调整天数。

indicator <- df[["datetime"]][["yday"]] + (df[["datetime"]][["hour"]] > 12)

使用因子水平,我们可以得到从 1 开始的订单号。

df[["ID"]] <- as.numeric(as.factor(indicator))

然后我们可以将所有day个周期设置为0

df[["ID"]][df[["par"]] != 0] <- 0

如果需要,可以分两行完成。

df[["ID"]] <- as.numeric(as.factor(df[["datetime"]][["yday"]] + 
                                (df[["datetime"]][["hour"]] > 12)))
df[["ID"]][df[["par"]] != 0] <- 0

无论您的数据采用何种顺序,此解决方案都适用。