【问题标题】:R: new column for unique observations over time elapsed (indexing from previous)R:随着时间推移的独特观察的新列(从以前的索引)
【发布时间】:2019-12-07 11:29:37
【问题描述】:

我正在尝试创建一个新列,仅当记录的观察发生在最后一次观察之后的特定时间之后才为观察(行)分配唯一值(请参阅数据框)。

上下文: 我设置了摄像头陷阱来观察哪些物种访问了特定的地块,species 的每次访问都应该得到一个唯一的visitID。实际的数据库包含更多的复杂性,但这是我遇到的主要问题。

new.df <- data.frame(
   species = c("A", "A", "A", "A", "A", "A", "B", "B", "B", "B", "B", "B"), 
   visit.time = c(seq(ymd_hm('2015-01-01 00:00'), ymd_hm('2015-01-01 00:10'), by = '2 mins'), 
                  seq(ymd_hm('2015-01-01 00:00'), ymd_hm('2015-01-01 00:10'), by = '2 mins'))
      )

> new.df
   species          visit.time
1        A 2015-01-01 00:00:00
2        A 2015-01-01 00:02:00
3        A 2015-01-01 00:04:00
4        A 2015-01-01 00:06:00
5        A 2015-01-01 00:08:00
6        A 2015-01-01 00:10:00
7        B 2015-01-01 00:00:00
8        B 2015-01-01 00:02:00
9        B 2015-01-01 00:04:00
10       B 2015-01-01 00:06:00
11       B 2015-01-01 00:08:00
12       B 2015-01-01 00:10:00

我想创建一个名为“visitID”的新列,用于记录每个物种的访问。但是,我只想为上次记录的访问后至少 2 分钟发生的访问分配一个唯一编号:

    > new.df
   species          visit.time visitID
1        A 2015-01-01 00:00:00 1
2        A 2015-01-01 00:02:00 -
3        A 2015-01-01 00:04:00 2
4        A 2015-01-01 00:06:00 -
5        A 2015-01-01 00:08:00 3
6        A 2015-01-01 00:10:00 -
7        B 2015-01-01 00:00:00 1
8        B 2015-01-01 00:02:00 -
9        B 2015-01-01 00:04:00 2
10       B 2015-01-01 00:06:00 -
11       B 2015-01-01 00:08:00 3
12       B 2015-01-01 00:10:00 -

其中- 只是一个NA

我通常会尝试使用dplyr:mutate 和条件条件ifelse,问题是我不知道如何计算上次访问的时间。

如果有更多细节可以提供,请告诉我。谢谢!

【问题讨论】:

  • 您的示例数据中的所有访问都发生在上次访问后两分钟。他们不应该都有一个ID吗?
  • 那是我的错,我试图让这个例子简单,所以我决定用 2 分钟。但是下面的回复都很棒!

标签: r datetime indexing conditional-statements dplyr


【解决方案1】:

从您想要的输出看来,当当前和上次记录的访问之间的时间差超过 2 分钟时,您需要一个新 ID。在这种情况下,我们可以使用在某个阈值处重置的累积和。我已经使用了这个答案中的函数:dplyr / R cumulative sum with reset

sum_reset_at <- function(thresh) {
  function(x) {
    accumulate(x, ~if_else(.x>thresh, .y, .x+.y))
  }  
}

new.df <- new.df %>%
  group_by(species) %>% # group df by species
  arrange(species, visit.time) %>% # sort the data
  mutate(
    time.elapsed = as.numeric(difftime(visit.time, lag(visit.time), units = "mins")), # calculate time difference in minutes
    time.elapsed = ifelse(is.na(time.elapsed), 0, time.elapsed), # replace NAs at first entries with 0s
    time.elapsed.cum = sum_reset_at(2)(time.elapsed), # build cumulative sum that resets once the value is greater (not greater or equal) to two
    newID = ifelse(time.elapsed.cum > 2, TRUE, FALSE), # build logical vector that marks the position where a new ID starts
    visitID = cumsum(newID) + 1, # generate visit IDs
    visitID = replace(visitID, duplicated(visitID), NA) # keep only first entry of an id, replace rest with NA
  )

输出:

> new.df
# A tibble: 12 x 6
# Groups:   species [2]
   species visit.time          time.elapsed time.elapsed.cum newID visitID
   <fct>   <dttm>                     <dbl>            <dbl> <lgl>   <dbl>
 1 A       2015-01-01 00:00:00            0                0 FALSE       1
 2 A       2015-01-01 00:02:00            2                2 FALSE      NA
 3 A       2015-01-01 00:04:00            2                4 TRUE        2
 4 A       2015-01-01 00:06:00            2                2 FALSE      NA
 5 A       2015-01-01 00:08:00            2                4 TRUE        3
 6 A       2015-01-01 00:10:00            2                2 FALSE      NA
 7 B       2015-01-01 00:00:00            0                0 FALSE       1
 8 B       2015-01-01 00:02:00            2                2 FALSE      NA
 9 B       2015-01-01 00:04:00            2                4 TRUE        2
10 B       2015-01-01 00:06:00            2                2 FALSE      NA
11 B       2015-01-01 00:08:00            2                4 TRUE        3
12 B       2015-01-01 00:10:00            2                2 FALSE      NA

所以基本上我们将时间差相加,直到它们超过两分钟,然后我们将总和重置为零。如果这个 cumsum 大于 2,我们需要添加一个新 ID。我们通过添加一个逻辑向量并构建该向量的累积和来做到这一点(因为 TRUE = 1 和 FALSE = 0)。最后,我们替换组中重复的 ID 以获得您指定的输出。我们可以删除您不需要的列:

> new.df %>% select(-c(time.elapsed, time.elapsed.cum, newID))
# A tibble: 12 x 3
# Groups:   species [2]
   species visit.time          visitID
   <fct>   <dttm>                <dbl>
 1 A       2015-01-01 00:00:00       1
 2 A       2015-01-01 00:02:00      NA
 3 A       2015-01-01 00:04:00       2
 4 A       2015-01-01 00:06:00      NA
 5 A       2015-01-01 00:08:00       3
 6 A       2015-01-01 00:10:00      NA
 7 B       2015-01-01 00:00:00       1
 8 B       2015-01-01 00:02:00      NA
 9 B       2015-01-01 00:04:00       2
10 B       2015-01-01 00:06:00      NA
11 B       2015-01-01 00:08:00       3
12 B       2015-01-01 00:10:00      NA

【讨论】:

  • 非常感谢@breeljausn 我能够根据我的真实数据集调整您的代码
【解决方案2】:

您可以使用diff() 返回差异。只需确保在每组species 前加上2,即c(2, diff(visit.time) / 60),这样每个物种的第一次访问总是得到一个ID(否则R 会抛出错误)。

您为visitID 给出的唯一标准是每个物种的值是唯一的,而不是它们是连续的,所以我假设1 5 61 2 3 一样有效。这大大简化了事情:

library(dplyr)

df %>% 
    group_by(species) %>% 
    mutate(tdiff = c(2, diff(visit.time) / 60),
           visitID = seq_along(species),
           visitID = ifelse(tdiff >= 2, visitID, NA)
           )

这将返回以下数据框:

# A tibble: 12 x 4
# Groups:   species [2]
   species visit.time          tdiff visitID
   <fct>   <dttm>              <dbl>   <int>
 1 A       2015-01-01 00:02:10 2           1
 2 A       2015-01-01 00:03:00 0.833      NA
 3 A       2015-01-01 00:03:10 0.167      NA
 4 A       2015-01-01 00:04:00 0.833      NA
 5 A       2015-01-01 00:07:40 3.67        5
 6 A       2015-01-01 00:09:40 2           6
 7 B       2015-01-01 00:00:40 2           1
 8 B       2015-01-01 00:01:10 0.5        NA
 9 B       2015-01-01 00:04:10 3           3
10 B       2015-01-01 00:05:40 1.5        NA
11 B       2015-01-01 00:09:40 4           5
12 B       2015-01-01 00:09:50 0.167      NA

请注意,我使用了修改后的数据集,因为您提供的示例中的时间差都是 == 2。

数据:

df <- structure(list(species = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 
2L, 2L, 2L, 2L, 2L, 2L), .Label = c("A", "B"), class = "factor"), 
    visit.time = structure(c(1420070530, 1420070580, 1420070590, 
    1420070640, 1420070860, 1420070980, 1420070440, 1420070470, 
    1420070650, 1420070740, 1420070980, 1420070990), class = c("POSIXct", 
    "POSIXt"), tzone = "UTC")), class = "data.frame", row.names = c(NA, 
-12L))

【讨论】:

  • 如果几个小时间差累计超过2分钟,好像检测不到新的visitID。
  • @JorisChau OP 的问题中没有任何内容表明这将是一个问题。我认为他们对没有访问的较长时间间隔感兴趣,而不是对大于 2 分钟的累积差异感兴趣。您可能想直接向 OP 澄清。
  • 谢谢格什特,这绝对足以让我继续我的工作!
猜你喜欢
  • 1970-01-01
  • 2020-07-07
  • 2018-02-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-25
  • 1970-01-01
  • 2012-10-06
  • 2016-02-13
相关资源
最近更新 更多