【问题标题】:R: Split observation values by and aggregate to time intervalsR:按时间间隔拆分观察值并聚合到时间间隔
【发布时间】:2018-06-18 14:53:06
【问题描述】:

在某些区域(名称)的各个观察点(obs)对鸟类进行了观察。 startend时间都取了,时间差(diff_corr)用修正因子重新计算,所以不是简单的difftime开始-结束-间隔。

我现在需要将这些值“拆分”为“合适的”间隔(15 分钟,例如 10:15:00、10:30:00,...),然后按区域聚合(name) 以便能够在这些干净的 15 分钟间隔内绘制出这些区域存在鸟类的图。

所以,为了更清楚一点:观察可能从 10:14 开始,一直持续到 10:25,因此它跨越了 10:00-10:15 和 10:15-10:30 的时间间隔,所以我得到的值应该被分割并相应地分配到适当的间隔中。

在更复杂的设置中,一次观察可能跨越 3 或 4 个间隔,因此也必须在此处相应地拆分值。

最后一步是聚合每个区间的所有观察部分并绘制它们。

我已经搜索了几天的解决方案,但只找到了使用 cutbreaks 重新排列间隔的非常简单的示例,但从未示例如何处理关联值,而是简单的频率计数。

示例数据:

structure(list(obs = structure(c(2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = c("b", 
"C2", "Dürnberg2"), class = "factor"), name = c("C2", "C2", 
"C2", "C2", "C2", "C2", "C2", "C2", "C2", "b", "981", "1627", 
"b", "b", "981", "1627", "b", "b", "b", "b"), start = structure(c(1495441500, 
1495441590, 1495441650, 1495441680, 1495447380, 1495447410, 1495447530, 
1495447560, 1495447580, 1496996580, 1496996580, 1496996580, 1496996760, 
1496996820, 1496996820, 1496996820, 1496997180, 1496997300, 1496997420, 
1496998260), class = c("POSIXct", "POSIXt"), tzone = ""), end = structure(c(1495441590, 
1495441650, 1495441680, 1495441800, 1495447410, 1495447530, 1495447560, 
1495447580, 1495447620, 1496996760, 1496996760, 1496996760, 1496996820, 
1496997180, 1496997180, 1496997180, 1496997300, 1496997420, 1496997540, 
1496998320), class = c("POSIXct", "POSIXt"), tzone = ""), diff_corr = c(1.46739130434783, 
0.978260869565217, 0.489130434782609, 1.95652173913043, 0.489130434782609, 
1.95652173913043, 0.489130434782609, 0.326086956521739, 0.652173913043478, 
2.96703296703297, 2.96703296703297, 2.96703296703297, 0.989010989010989, 
5.93406593406593, 5.93406593406593, 5.93406593406593, 1.97802197802198, 
1.97802197802198, 1.97802197802198, 0.989010989010989)), .Names = c("obs", 
"name", "start", "end", "diff_corr"), row.names = c("1", "9", 
"7", "8", "3", "2", "4", "5", "6", "13", "13.1", "13.2", "22", 
"11", "11.1", "11.2", "12", "23", "15", "16"), class = "data.frame")

附言我很难正确命名我的问题,因此非常感谢任何提示(不仅限于此)

一个小例子的新尝试: 按比例将值分配给间隔(然后将相等的间隔相加)

start         end         value     new values in new 15-min-intervals
10:03:00      10:14:00    11        ---> 10:00:00 =  11
10:14:00      10:16:00     2        ---> 10:00:00 = 1 ; 10:15:00 = 1
10:00:00      10:35:00    40        ---> 10:00:00 = 40/35*15 ; 10:15:00 = 40/35*15 ; 10:30:00 = 40/35*5
10:15:00      10:30:00    12        ---> 10:15:00 = 12

【问题讨论】:

  • 为了确保我理解正确,您想获得所有diff_corr 值的平均值或累积总和吗?其他列是字符串,因此它们不能被求和/平均等。
  • 您的意思是将时间间隔 (diff_corr) 列重新格式化为 15 分钟时间戳吗?
  • @Gautam 是的,我需要 diff_corr 的总和,但是对于四分之一小时的“不错”间隔,它的比例正在下降。
  • @SaleemKhan diff_corr 不是时间间隔,而是在开始和结束间隔内重新计算的观察分钟数。这些应该在一小时内分成 15 分钟间隔的比例部分,然后汇总。

标签: r time intervals


【解决方案1】:

这是一种data.table 方法,它允许您使用 SQL 类型的查询对数据进行排序/过滤并执行操作。

数据

> p
    obs name               start                 end diff_corr
 1:  C2   C2 2017-05-22 04:25:00 2017-05-22 04:26:30 1.4673913
 2:  C2   C2 2017-05-22 04:26:30 2017-05-22 04:27:30 0.9782609
 3:  C2   C2 2017-05-22 04:27:30 2017-05-22 04:28:00 0.4891304
 4:  C2   C2 2017-05-22 04:28:00 2017-05-22 04:30:00 1.9565217
 5:  C2   C2 2017-05-22 06:03:00 2017-05-22 06:03:30 0.4891304
 6:  C2   C2 2017-05-22 06:03:30 2017-05-22 06:05:30 1.9565217
 7:  C2   C2 2017-05-22 06:05:30 2017-05-22 06:06:00 0.4891304
 8:  C2   C2 2017-05-22 06:06:00 2017-05-22 06:06:20 0.3260870
 9:  C2   C2 2017-05-22 06:06:20 2017-05-22 06:07:00 0.6521739
10:   b    b 2017-06-09 04:23:00 2017-06-09 04:26:00 2.9670330
11:   b  981 2017-06-09 04:23:00 2017-06-09 04:26:00 2.9670330
12:   b 1627 2017-06-09 04:23:00 2017-06-09 04:26:00 2.9670330
13:   b    b 2017-06-09 04:26:00 2017-06-09 04:27:00 0.9890110
14:   b    b 2017-06-09 04:27:00 2017-06-09 04:33:00 5.9340659
15:   b  981 2017-06-09 04:27:00 2017-06-09 04:33:00 5.9340659
16:   b 1627 2017-06-09 04:27:00 2017-06-09 04:33:00 5.9340659
17:   b    b 2017-06-09 04:33:00 2017-06-09 04:35:00 1.9780220
18:   b    b 2017-06-09 04:35:00 2017-06-09 04:37:00 1.9780220
19:   b    b 2017-06-09 04:37:00 2017-06-09 04:39:00 1.9780220
20:   b    b 2017-06-09 04:51:00 2017-06-09 04:52:00 0.9890110

代码

library(data.table)
library(lubridate)
p <- as.data.table(p)
p[, .(new_diff = mean(diff_corr)), .(tme_start = round_date(start, unit = "15min"))]

输出

> p[, .(new_diff = mean(diff_corr)), .(tme_start = round_date(start, unit = "15min"))]
             tme_start  new_diff
1: 2017-05-22 04:30:00 1.2228261
2: 2017-05-22 06:00:00 0.7826087
3: 2017-06-09 04:30:00 3.3626374
4: 2017-06-09 04:45:00 0.9890110

Data.Table 在做什么?

由于您不熟悉data.table,这里有一个关于正在发生的事情的非常快速、基本的描述。 data.table 调用的一般形式是:

DT[select rows, perform operations, group by] 

其中DTdata.table 名称。 Select rows 是一个逻辑操作,例如假设您只想观察 C2(名称),则调用将是 DT[name == "C2",] 不需要执行任何操作,也不需要分组。如果您想要所有name == "C2"diff_corr 列的总和,则调用变为DT[name == "C2", list(sum(diff_corr))]。你可以使用.(),而不是写list()。现在,输出将只有一行一列,称为V1,这是name == "C2" 时所有diff_corr 的总和。该列没有太多信息,因此我们为其命名(可以与旧的相同):DT[name == "C2", .(diff_corr_sum = sum(diff_corr))]。假设您有另一个名为“mood”的列,它报告了进行观察的人的情绪,并且可以假设三个值(“happy”、“sad”、“sleepy”)。您可以按心情“分组”:DT[name == "C2", .(diff_corr_new = sum(diff_corr)), by = .(mood)]。输出将是对应于每种情绪的三行和一列diff_corr_new。为了更好地理解这一点,请尝试使用像 mtcars 这样的示例数据集。您的示例数据没有足够的复杂性等,无法让您探索所有这些功能。

返回答案 - 其他变体

如果您想根据startend 进行四舍五入,从问题或 cmets 中并不清楚。我用的是前者,但你可以改变它。上面的示例使用mean,但您可以执行您可能需要的任何其他操作。其他列似乎或多或少是多余的,因为它们是字符串,您不能对它们做太多事情。您可以使用它们对by 条目(代码中的最后一个字段)中的结果进行进一步排序。下面是分别使用obsname 的两个示例。您也可以将所有这些组合在一起。

> p[, .(new_diff = mean(diff_corr)), .(tme_start = round_date(start, unit = "15min"), obs)]
             tme_start obs  new_diff
1: 2017-05-22 04:30:00  C2 1.2228261
2: 2017-05-22 06:00:00  C2 0.7826087
3: 2017-06-09 04:30:00   b 3.3626374
4: 2017-06-09 04:45:00   b 0.9890110


> p[, .(new_diff = mean(diff_corr)), .(tme_start = round_date(start, unit = "15min"), name)]
             tme_start name  new_diff
1: 2017-05-22 04:30:00   C2 1.2228261
2: 2017-05-22 06:00:00   C2 0.7826087
3: 2017-06-09 04:30:00    b 2.6373626
4: 2017-06-09 04:30:00  981 4.4505495
5: 2017-06-09 04:30:00 1627 4.4505495
6: 2017-06-09 04:45:00    b 0.9890110

【讨论】:

  • 非常感谢您到目前为止的回答。不得不做更多的事情,并且很难理解 data.table 用它的 , 和 . round_date 等 1. 我需要总和,而不是平均值,但这个接缝才能正常工作。 2.如果开始-结束时间间隔超过 15 分钟,我还需要弄清楚你的解决方案是否真的划分了 diff_corr。得找例子。我已经发现的是,当结束时间恰好落在休息时,该值进入一个单独的间隔,而不是较低的间隔。如果 end
  • 查看Data.table 包以了解有关查询的更多信息。这是一个很好的资源:@​​987654321@ Round_date 是来自lubridate 包的函数,它的功能正如其名称所暗示的那样 - 圆形日期。在四舍五入不同的时间间隔(7 分钟、2 小时、12 秒等)时很有用。您可以轻松地将mean 替换为sum 以获得总和。我将在代码中添加更多解释。
  • Boahhh,我花了几天时间来学习 data.table 的基础知识,你的语法对我来说仍然是个谜……但结合一些额外的笨拙,我似乎达到了我的目标.. . 一周后我预计交付。非常感谢!
【解决方案2】:

这既慢又笨重,但也许有帮助。按名称和 15 分钟间隔计算计数和加权 diff_corr 总和:

library(dplyr)
range <- seq.POSIXt(min(df$start)-(15*60), max(df$end)+(15*60), by = "15 min")

df$totalDuration <- as.numeric(as.difftime(df$end-df$start),units=c("secs"))

out <- NULL
for (r in 1:length(range)){
  subset <- df %>% filter( (start >= (range[r]-(15*60)) & start<range[r]) |
                             (end>= (range[r]-(15*60)) & end<range[r] ) |
                             (end > range[r] & start < range[r])) %>%
    mutate(bin=range[r],
           duration = ifelse(start>=(range[r]-(15*60)) & end<range[r],totalDuration,
                        ifelse(start>=(range[r]-(15*60)),as.numeric(as.difftime(range[r]-start),units="secs"),
                          ifelse(end<range[r],
                                 as.numeric(as.difftime(end-(range[r]-(15*60))),units="secs"),
                                            as.numeric(as.difftime(range[r]-(range[r]-(15*60))),units="secs")
                        )))
           ) %>% 
    mutate (diff_corr_W = diff_corr*(duration/as.double(totalDuration, units='secs'))) %>%
    group_by(bin,name) %>% summarise(count=n(),
                                     diff_corr_sum = sum(diff_corr_W)) %>% ungroup()


  if (is.null(out)){
    out <- subset
  } else {
    out <- rbind(out,subset)
  }
}


> out
# A tibble: 9 x 4
bin  name count diff_corr_sum
*              <dttm> <chr> <int>         <dbl>
  1 2017-05-22 04:40:00    C2     4      4.891304
2 2017-05-22 06:10:00    C2     5      3.913043
3 2017-06-09 04:25:00  1627     1      1.978022
4 2017-06-09 04:25:00   981     1      1.978022
5 2017-06-09 04:25:00     b     1      1.978022
6 2017-06-09 04:40:00  1627     2      6.923077
7 2017-06-09 04:40:00   981     2      6.923077
8 2017-06-09 04:40:00     b     6     13.846154
9 2017-06-09 04:55:00     b     1      0.989011

【讨论】:

  • 对不起,我似乎没有正确解释它。计数频率的示例已经可用。但我需要 diff_corr 拆分的值,并按一刻钟的比例分配。但是thanx的例子。
  • 根据您的 cmets 在上面编辑。
  • 您的示例非常感谢。在我敢于潜入 mutate-ifelse-groupby-whatever conglomerat 之前,我会先尝试其他解决方案;)
  • 是的,我同意它最终看起来很糟糕。嵌套的 ifelse 是将持续时间分配给 15 分钟的间隔,但这样做可能有更清晰的逻辑。删除group_by 也可能更清楚,这样您就可以检查持续时间和diff_corr 是否分布正确。然后在循环外执行group_by
  • 虽然你的解决方案对我没有用(可以让它工作),尤其是“范围”变量的想法启发了我做其他事情。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多