【问题标题】:Count the number of value changes within the past n days统计过去 n 天内值变化的次数
【发布时间】:2020-03-08 13:27:54
【问题描述】:

我想计算过去 d 天内不等于前一个 numnum 的数量,最好是

我的数据如下所示:

d <- 3
   df1 <- structure(list(LET = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 
                                         2L, 2L, 2L, 2L), 
                                       .Label = c("a", "b"), class = "factor"), 
                   day = c(1L, 2L, 3L, 4L, 5L, 1L, 2L, 3L, 4L, 5L),
                   num = c(5, 6, 6, 7, 7, 1, 1, 3, 1, 1)), 
              class = c("data.table", "data.frame"), 
              row.names = c(NA, -10L))
    LET day num
 1:   a   1   5
 2:   a   2   6
 3:   a   3   6
 4:   a   4   7
 5:   a   5   7
 6:   b   1   1
 7:   b   2   1
 8:   b   3   3
 9:   b   4   1
10:   b   5   1

我可以使用 shift 来判断 num 是否等于其滞后值,num 是否已更改:

df1[,hasChanged := num!=shift(num, n=1, fill=FALSE), by='LET']

但我想计算 num 更改的次数,其中 day currentRowDay - d ,LET

输出如下所示(hasChanged 列是可选的):

df1[,NumberOfChangesPast3Days := c(1,2,2,2,1,1,1,2,2,2),]

    LET day num hasChanged NumberOfChangesPast3Days
 1:   a   1   5       TRUE                        1
 2:   a   2   6       TRUE                        2
 3:   a   3   6      FALSE                        2
 4:   a   4   7       TRUE                        2
 5:   a   5   7      FALSE                        1
 6:   b   1   1       TRUE                        1
 7:   b   2   1      FALSE                        1
 8:   b   3   3       TRUE                        2
 9:   b   4   1       TRUE                        2
10:   b   5   1      FALSE                        2

【问题讨论】:

  • 我认为这并不完全清楚 - 您需要发布您的预期输出。
  • 发布的预期输出。

标签: data.table r data.table


【解决方案1】:

一个可能的解决方案是使用Reduceshift

df1[, N_Changes := Reduce(`+`, shift(hasChanged, n = 0:(d-1), fill = FALSE))
    , by = LET]

给出:

> df1
    LET day num hasChanged N_Changes
 1:   a   1   5       TRUE         1
 2:   a   2   6       TRUE         2
 3:   a   3   6      FALSE         2
 4:   a   4   7       TRUE         2
 5:   a   5   7      FALSE         1
 6:   b   1   1       TRUE         1
 7:   b   2   1      FALSE         1
 8:   b   3   3       TRUE         2
 9:   b   4   1       TRUE         2
10:   b   5   1      FALSE         2

【讨论】:

  • 请注意,使用shift() 只会在日期连续(即没有任何间隔)的情况下返回正确答案。
【解决方案2】:

到目前为止发布的所有解决方案都使用shift() 函数,该函数要求天数是连续的,即没有间隙。答案只是偶然返回预期结果,因为样本数据集没有任何间隙。

OP已经明确表示喜欢

计算 num 变化的数量 where day currentRowDay - d

这甚至可以通过在非等自连接中聚合来实现,该非等自连接聚合在 3 天的滚动窗口中(不是 3 行!):

library(data.table)
setDT(df1)[
  , c("hasChanged", "day_start") := 
    .(c(TRUE, diff(num) != 0), day - d), by = LET][
      , sprintf("NumberOfChangesPast%iDays", d) := 
        .SD[.SD, on = .(LET, day > day_start, day <= day), sum(hasChanged), 
            by = .EACHI]$V1][]
    LET day num hasChanged day_start NumberOfChangesPast3Days
 1:   a   1   5       TRUE        -2                        1
 2:   a   2   6       TRUE        -1                        2
 3:   a   3   6      FALSE         0                        2
 4:   a   4   7       TRUE         1                        2
 5:   a   5   7      FALSE         2                        1
 6:   b   1   1       TRUE        -2                        1
 7:   b   2   1      FALSE        -1                        1
 8:   b   3   3       TRUE         0                        2
 9:   b   4   1       TRUE         1                        2
10:   b   5   1      FALSE         2                        2

对于有间隙的数据,例如,

df2 <- structure(list(LET = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 
                                        2L, 2L, 2L, 2L), 
                                      .Label = c("a", "b"), class = "factor"), 
                      day = c(1L, 2L, 3L, 5L, 7L, 1L, 2L, 3L, 4L, 6L),
                      num = c(5, 6, 6, 7, 7, 1, 1, 3, 1, 1)), 
                 class = c("data.table", "data.frame"), 
                 row.names = c(NA, -10L))

代码

setDT(df2)[
  , c("hasChanged", "day_start") := 
    .(c(TRUE, diff(num) != 0), day - d), by = LET][
      , sprintf("NumberOfChangesPast%iDays", d) := 
        .SD[.SD, on = .(LET, day > day_start, day <= day), sum(hasChanged), 
            by = .EACHI]$V1][]

返回

    LET day num hasChanged day_start NumberOfChangesPast3Days
 1:   a   1   5       TRUE        -2                        1
 2:   a   2   6       TRUE        -1                        2
 3:   a   3   6      FALSE         0                        2
 4:   a   5   7       TRUE         2                        1
 5:   a   7   7      FALSE         4                        1
 6:   b   1   1       TRUE        -2                        1
 7:   b   2   1      FALSE        -1                        1
 8:   b   3   3       TRUE         0                        2
 9:   b   4   1       TRUE         1                        2
10:   b   6   1      FALSE         3                        1

请注意,day_start 不包含在滚动窗口中,因为 OP 已请求左打开间隔。

【讨论】:

    【解决方案3】:

    这将是使用data.tableshift 的一种方式:

    library(data.table)
    
    df1[, hasChanged := num != shift(num, n=1, fill=FALSE), by = LET][
      , NumberOfChangesPast3Days := colSums(do.call(rbind, shift(hasChanged, (d-1):0)), na.rm = TRUE), 
      by = LET
      ]
    

    输出:

        LET day num hasChanged NumberOfChangesPast3Days
     1:   a   1   5       TRUE                        1
     2:   a   2   6       TRUE                        2
     3:   a   3   6      FALSE                        2
     4:   a   4   7       TRUE                        2
     5:   a   5   7      FALSE                        1
     6:   b   1   1       TRUE                        1
     7:   b   2   1      FALSE                        1
     8:   b   3   3       TRUE                        2
     9:   b   4   1       TRUE                        2
    10:   b   5   1      FALSE                        2
    

    【讨论】:

    • 请注意,使用shift() 只会在日期连续的情况下返回正确答案,即没有任何间隔。
    • 谢谢@Uwe,确实-这仅适用于连续几天,已对您的答案表示赞同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-08
    • 2023-01-23
    • 1970-01-01
    • 2020-08-27
    • 1970-01-01
    • 2016-08-26
    • 1970-01-01
    相关资源
    最近更新 更多