每次该组出现在大型数据集中时,提取每个组的第一个和最后一个观察值?

Extract the first and last observation of each group, every time that group appears within a large dataset?

我有一个大型的鱼类检测数据集(约 300,000 行)。每个检测都有一个日期、一个站(位置)和一个 tagID,以及许多其他变量,如温度、深度等。每次鱼访问该站时,我想提取每个站的第一个和最后一个检测。最终目标是计算每个站点移动前的驻留时间,以及返回时的驻留时间。

这里是数据的一个小例子

tagID <- c("8272", "8272", "8272", "8272", "8272", "8272", "8272", "8272", "8272", "8272")
date <- c("2020-07-12", "2020-07-12", "2020-07-13", "2020-07-13", "2020-07-16", "2020-07-17", "2020-07-20", "2020-07-29", "2020-07-30", "2020-08-04")
station <- c("4", "4", "4", "5", "5", "6", "6", "6", "4", "4")
temp <- c("10", "9", "11", "12", "10", "12", "11", "12", "12", "9")
depth <- c("6.14", "34.2", "21", "23.5", "15.4", "54", "32.4", "23", "33.3", "32.7")
df <- data.frame(tagID, date, station, temp, depth)

数据框看起来像:

 tagID       date station temp depth
1   8272 2020-07-12       4   10  6.14
2   8272 2020-07-12       4    9  34.2
3   8272 2020-07-13       4   11    21
4   8272 2020-07-13       5   12  23.5
5   8272 2020-07-16       5   10  15.4
6   8272 2020-07-17       6   12    54
7   8272 2020-07-20       6   11  32.4
8   8272 2020-07-29       6   12    23
9   8272 2020-07-30       4   12  33.3
10  8272 2020-08-04       4    9  32.7

我想找到一种有效的方法来遍历所有 300K 行并提取如下内容:

 tagID       date station temp depth
1   8272 2020-07-12       4   10  6.14
3   8272 2020-07-13       4   11    21
4   8272 2020-07-13       5   12  23.5
5   8272 2020-07-16       5   10  15.4
6   8272 2020-07-17       6   12    54
8   8272 2020-07-29       6   12    23
9   8272 2020-07-30       4   12  33.3
10  8272 2020-08-04       4    9  32.7

这显示了鱼在第 4 站时的第一次和最后一次检测,然后当鱼在本季节晚些时候回到第 4 站时再次检测到第一次和最后一次。

我看过 and Select the first and last row by group in a data frame 等问题和其他类似问题,但其中 none 占第 2(第 3、4、n... 次)小组(在我的案例:站)出现在数据中。

如果您能提供帮助,请告诉我。谢谢你。 (这是我关于堆栈溢出的第一个问题,任何对以后问题的提示都有帮助)

我这里的方法是标记鱼每次换站的情况,然后计算这些换站的累计次数。然后我们可以按鱼和# of station 更改分组,并过滤​​每个的第一个和最后一个。

library(dplyr)
df %>%
  group_by(tagID) %>%
  mutate(station_chg = station != lag(station, default = ""),
         station_cuml = cumsum(station_chg)) %>%
  group_by(tagID, station_cuml) %>%
  slice(1, n()) %>%
  ungroup()

结果

# A tibble: 8 x 7
  tagID date       station temp  depth station_chg station_cuml
  <chr> <chr>      <chr>   <chr> <chr> <lgl>              <int>
1 8272  2020-07-12 4       10    6.14  TRUE                   1
2 8272  2020-07-13 4       11    21    FALSE                  1
3 8272  2020-07-13 5       12    23.5  TRUE                   2
4 8272  2020-07-16 5       10    15.4  FALSE                  2
5 8272  2020-07-17 6       12    54    TRUE                   3
6 8272  2020-07-29 6       12    23    FALSE                  3
7 8272  2020-07-30 4       12    33.3  TRUE                   4
8 8272  2020-08-04 4       9     32.7  FALSE                  4

这是一个data.table方法。正如@Henrik 在评论中提到的,您可以使用 rleid 创建一个新列作为分组依据,而不是使用 station 作为 station 的重复值。 rleid。然后,对于每个组,它将包括第一个和最后一个 .N 值。请注意,添加 unique 是为了考虑给定组可能只存在一行数据的情况。我希望这对您来说可能是一个快速的解决方案。

library(data.table)

setDT(df)

df[ , id := rleid(station)][ , .SD[unique(c(1, .N))], by = id]

输出

   id tagID       date station temp depth
1:  1  8272 2020-07-12       4   10  6.14
2:  1  8272 2020-07-13       4   11    21
3:  2  8272 2020-07-13       5   12  23.5
4:  2  8272 2020-07-16       5   10  15.4
5:  3  8272 2020-07-17       6   12    54
6:  3  8272 2020-07-29       6   12    23
7:  4  8272 2020-07-30       4   12  33.3
8:  4  8272 2020-08-04       4    9  32.7