【问题标题】:dplyr mutate function to evaluate values within columns (current, previous, next) verticallydplyr mutate 函数以垂直评估列(当前、上一个、下一个)中的值
【发布时间】:2016-06-06 19:04:50
【问题描述】:

我已经搜索了一种方法来实现我需要的东西,而无需运气,所以就这样吧。 不久前,我发现了 dplyr 包及其潜力。我在想这个包可以做我想做的事,我只是不知道怎么做。这是我数据的一小部分,但应该能代表我的问题。

    dummy<-structure(list(time = structure(1:20, .Label = c("2015-03-25 12:24:00", 
    "2015-03-25 21:08:00", "2015-03-25 21:13:00", "2015-03-25 21:47:00", 
    "2015-03-26 03:08:00", "2015-04-01 20:30:00", "2015-04-01 20:34:00", 
    "2015-04-01 20:42:00", "2015-04-01 20:45:00", "2015-09-29 18:26:00", 
    "2015-09-29 19:11:00", "2015-09-29 21:21:00", "2015-09-29 22:03:00", 
    "2015-09-29 22:38:00", "2015-09-30 00:48:00", "2015-09-30 01:38:00", 
    "2015-09-30 01:41:00", "2015-09-30 01:45:00", "2015-09-30 01:47:00", 
    "2015-09-30 01:49:00"), class = "factor"), ID = c(1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 
    2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
    2L), station = c(1L, 1L, 1L, 2L, 3, 
    4L, 4L, 4L, 4L, 5L, 5L, 6L, 
    6L, 5, 5, 5L, 7, 7, 7L, 
    7)), .Names = c("time", "ID", "station"), class = "data.frame", row.names = c(NA, 
    -20L))

我希望以 ID 和站列为条件评估时间列中的行。具体来说,我希望函数 (dplyr?) 评估每个时间行,并将时间与上一次 (row-1) 和下一次 (row+1) 进行比较。如果当前行的时间在上一行和/或下一行时间的 1 小时内,并且当前行的 ID 和站与上一行和/或下一行匹配,那么我想在新行中添加 1 , 否则为 0。

我将如何使用 dplyr 实现这一目标?

预期的结果应该是这样的:

                  time ID station new.value
1  2015-03-25 12:24:00  1       1         0
2  2015-03-25 21:08:00  1       1         1
3  2015-03-25 21:13:00  1       1         1
4  2015-03-25 21:47:00  1       2         0
5  2015-03-26 03:08:00  1       3         0
6  2015-04-01 20:30:00  1       4         1
7  2015-04-01 20:34:00  1       4         1
8  2015-04-01 20:42:00  1       4         1
9  2015-04-01 20:45:00  1       4         1
10 2015-09-29 18:26:00  2       5         1
11 2015-09-29 19:11:00  2       5         1
12 2015-09-29 21:21:00  2       6         1
13 2015-09-29 22:03:00  2       6         1
14 2015-09-29 22:38:00  2       5         0
15 2015-09-30 00:48:00  2       5         1
16 2015-09-30 01:38:00  2       5         1
17 2015-09-30 01:41:00  2       7         1
18 2015-09-30 01:45:00  2       7         1
19 2015-09-30 01:47:00  2       7         1
20 2015-09-30 01:49:00  2       7         1

【问题讨论】:

  • 我认为您不需要 dplyr,我认为您只需要几个 diff 操作。请注意,“类“POSIXct”表示自 1970 年初(在 UTC 时区中)以来的(带符号的)秒数作为数字向量。”所以你会想要寻找小于 3600 秒的差异。
  • 不确定dplyr,但 data.table 具有滚动连接,这似乎以最有效的方式解决了您的问题。最近的一篇文章详细描述了该功能:Understanding data.table Rolling Joins。要提供一个小时滚动,只需使用 60*60(因为 POSIXct 是秒数)。这样,您应该能够使用二进制合并检测这些行。如果纯粹的性能不是您的目标,那么 shift(x)c(NA, x[-.N]) / c(x[-1L], NA) 应该足以创建列进行比较。

标签: r dplyr


【解决方案1】:

这是一个使用difftimedplyr mutate 函数的选项。首先,我们使用group_by 操作来确保比较在ID 和Station 的每个唯一组合内。 difftime可以用来计算时差,这里为了方便将单位设置为hourslaglead 函数也来自 dplyr 包,它们向后或向前移动选定的列。结合difftime的向量化操作,可以计算出当前行与上/下一行的时间差。我们使用abs 来确保结果是绝对值。 &lt;1 的条件确保差异在一个小时内。 as.integer将逻辑值(T或F)对应转换为(1或0)。

library(dplyr)
dummy %>% group_by(ID, station) %>% 
          mutate(new.value = as.integer(
                 abs(difftime(time, lag(time, default = Inf), units = "hours")) < 1 | 
                 abs(difftime(time, lead(time, default = Inf), units = "hours")) < 1))

Source: local data frame [20 x 4]
Groups: ID, station [7]

                  time    ID station new.value
                (time) (int)   (dbl)     (int)
1  2015-03-25 12:24:00     1       1         0
2  2015-03-25 21:08:00     1       1         1
3  2015-03-25 21:13:00     1       1         1
4  2015-03-25 21:47:00     1       2         0
5  2015-03-26 03:08:00     1       3         0
6  2015-04-01 20:30:00     1       4         1
7  2015-04-01 20:34:00     1       4         1
8  2015-04-01 20:42:00     1       4         1
9  2015-04-01 20:45:00     1       4         1
10 2015-09-29 18:26:00     2       5         1
11 2015-09-29 19:11:00     2       5         1
12 2015-09-29 21:21:00     2       6         1
13 2015-09-29 22:03:00     2       6         1
14 2015-09-29 22:38:00     2       5         0
15 2015-09-30 00:48:00     2       5         1
16 2015-09-30 01:38:00     2       5         1
17 2015-09-30 01:41:00     2       7         1
18 2015-09-30 01:45:00     2       7         1
19 2015-09-30 01:47:00     2       7         1
20 2015-09-30 01:49:00     2       7         1

【讨论】:

  • 这确实是我想要的。你能详细说明一下吗?谢谢。
【解决方案2】:

Psidom 的回答很棒——这是data.table 方法。

library(data.table)
setDT(dummy)
# you do NOT want a factor for your time variable
dummy[, time := as.POSIXct(time) ]
dummy[, `:=`(lag_diff = c(Inf, diff(as.numeric(time))),
             lead_diff = c(diff(as.numeric(time)), Inf)),
      by = .(ID, station) ]
dummy[, new.value := as.integer(lag_diff < 3600 | lead_diff < 3600) ]
dummy

【讨论】:

    【解决方案3】:

    使用 R 基函数(sapplydifftime)的另一种解决方案:

    n=nrow(dummy)
    dummy$new.value=
    as.numeric(sapply(1:n, function(i) 
    (i<n && (dummy[i,"ID"]==dummy[i+1,"ID"] && dummy[i,"station"]==dummy[i+1,"station"]) 
    && abs(as.numeric(difftime(dummy[i,"time"], dummy[i+1,"time"]), "hours"))<=1) 
    || 
    (i>1 && (dummy[i,"ID"]==dummy[i-1,"ID"] && dummy[i,"station"]==dummy[i-1,"station"]) 
    && abs(as.numeric(difftime(dummy[i,"time"], dummy[i-1,"time"]), "hours"))<=1) 
    ))
    
    # > dummy
                      # time ID station new.value
    # 1  2015-03-25 12:24:00  1       1         0
    # 2  2015-03-25 21:08:00  1       1         1
    # 3  2015-03-25 21:13:00  1       1         1
    # 4  2015-03-25 21:47:00  1       2         0
    # 5  2015-03-26 03:08:00  1       3         0
    # 6  2015-04-01 20:30:00  1       4         1
    # 7  2015-04-01 20:34:00  1       4         1
    # 8  2015-04-01 20:42:00  1       4         1
    # 9  2015-04-01 20:45:00  1       4         1
    # 10 2015-09-29 18:26:00  2       5         1
    # 11 2015-09-29 19:11:00  2       5         1
    # 12 2015-09-29 21:21:00  2       6         1
    # 13 2015-09-29 22:03:00  2       6         1
    # 14 2015-09-29 22:38:00  2       5         0
    # 15 2015-09-30 00:48:00  2       5         1
    # 16 2015-09-30 01:38:00  2       5         1
    # 17 2015-09-30 01:41:00  2       7         1
    # 18 2015-09-30 01:45:00  2       7         1
    # 19 2015-09-30 01:47:00  2       7         1
    # 20 2015-09-30 01:49:00  2       7         1
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-04
      • 2014-03-05
      相关资源
      最近更新 更多