在R中计算非连续行为时长(按日统计)
解决非连续筑巢行为的每日时长统计问题
问题核心
现有代码直接用max(timestamp)-min(timestamp)计算筑巢时长,会把整个观测时段全部计入,但实际筑巢行为是非连续的,必须先识别每一段连续的筑巢区间,再累加各区间的时长才能得到准确结果。
解决方案
我们需要先标记筑巢状态,再识别连续的筑巢区间,最后按日期(及个体)累加各区间的时长。以下是基于tidyverse的实现方案:
步骤1:数据预处理与状态标记
先确保时间格式正确,同时给每条记录标记是否属于筑巢行为:
library(tidyverse) data_tibble <- data_tibble %>% mutate(timestamp = as.POSIXct(timestamp)) %>% # 标记当前记录是否为筑巢行为(距离巢穴≤30米) mutate(is_nesting = Dist <= 30)
步骤2:识别连续筑巢区间并计算总时长
使用rleid(来自data.table)给连续的筑巢/非筑巢记录分配唯一ID,以此区分不同的行为区间,再累加每个筑巢区间的时长:
library(data.table) nest_duration_perday <- data_tibble %>% # 按个体+日期分组,确保不同个体、不同日期的统计独立 group_by(bird_ID, date) %>% # 生成连续行为区间的ID:连续的相同is_nesting状态会得到同一个ID mutate(interval_id = rleid(is_nesting)) %>% # 只保留筑巢行为的区间 filter(is_nesting) %>% # 按区间分组,计算单个筑巢区间的时长 group_by(bird_ID, date, interval_id) %>% summarise(interval_duration = max(timestamp) - min(timestamp), .groups = "drop_last") %>% # 按日期+个体累加所有筑巢区间的总时长 summarise(total_nesting_duration = sum(interval_duration))
纯dplyr替代方案(无需依赖data.table)
如果不想引入data.table,可以用lag函数手动判断状态变化,生成区间ID:
nest_duration_perday <- data_tibble %>% mutate(timestamp = as.POSIXct(timestamp), is_nesting = Dist <= 30) %>% group_by(bird_ID, date) %>% # 判断当前记录与上一条的筑巢状态是否变化,生成切换标记 mutate(status_change = is_nesting != lag(is_nesting, default = !is_nesting[1]), # 累加切换标记生成区间ID interval_id = cumsum(status_change)) %>% filter(is_nesting) %>% group_by(bird_ID, date, interval_id) %>% summarise(interval_duration = max(timestamp) - min(timestamp), .groups = "drop_last") %>% summarise(total_nesting_duration = sum(interval_duration))
示例数据的输出结果
运行上述代码后,针对你的示例数据,会得到符合实际的筑巢总时长:
#> # A tibble: 1 × 3 #> # Groups: bird_ID [1] #> bird_ID date total_nesting_duration #> <chr> <chr> <drtn> #> 1 048T 2022-06-04 2.500278 hours
(注:示例数据中连续筑巢的区间是2022-06-04 10:49:36到2022-06-04 13:19:39,时长约2.5小时,完全匹配数据中的实际筑巢时段)
补充说明
- 代码中加入
bird_ID分组,是因为不同个体的巢穴位置独立,需要分开统计;如果你的数据集仅涉及单个个体,可以去掉该分组。 - 最终结果按
bird_ID和date汇总,确保每日每个个体的筑巢时长统计准确。
内容的提问来源于stack exchange,提问作者helianthus
相关产品推荐
相关产品推荐

