【问题标题】:R: modify a variable conditioned on data from multiple previous rowsR:修改一个以来自多个先前行的数据为条件的变量
【发布时间】:2018-11-23 02:52:03
【问题描述】:

您好,非常感谢您的帮助,我在之前的问题中确实找不到解决方案。

我有一个长格式的小标题(按 id 分组并按时间排列的行)。 我想基于“varx”创建一个变量“eleg”。对于每个 ID,如果前 3 行中的“varx” == 0 和当前行 varx == 1,如果不是 = 0,则条件是“eleg”= 1。如果可能的话,使用 dplyr。

id <- c(1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3)
time <- c(1,2,3,4,5,6,7,1,2,3,4,5,6,1,2,3,4)
varx <- c(0,0,0,0,1,1,0,0,1,1,1,1,1,0,0,0,1)
eleg <- c(0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1)
table <- data.frame(id, time, varx, eleg)

在我的真实数据集中,条件是“在前 24 行中”,如果符合条件,同一个 ID 可能会多次出现 eleg == 1。

谢谢。

【问题讨论】:

  • 您是要按id 组执行此操作还是遍历整个df?
  • 谢谢!我会澄清的!

标签: r if-statement panel-data dplyr


【解决方案1】:
library(data.table)
df %>% 
mutate(elegnew = ifelse(Reduce("+", shift(df$varx, 1:3)) == 0 & df$varx == 1, 1, 0))

   id time varx eleg elegnew
1   1    1    0    0       0
2   1    2    0    0       0
3   1    3    0    0       0
4   1    4    0    0       0
5   1    5    1    1       1
6   1    6    1    0       0
7   1    7    0    0       0
8   2    1    0    0       0
9   2    2    1    0       0
10  2    3    1    0       0
11  2    4    1    0       0
12  2    5    1    0       0
13  2    6    1    0       0
14  3    1    0    0       0
15  3    2    0    0       0
16  3    3    0    0       0
17  3    4    1    1       1

【讨论】:

  • 谢谢,我 group_by(id)mutate 之前工作过!
  • 虽然前 3 行中有 1 行,但它给了我 NA
【解决方案2】:

其中一种方法可能是

library(dplyr)

m <- 3     #number of times previous rows are looked back

df %>%
  group_by(id) %>%
  mutate(eleg = ifelse(rowSums(sapply(1:m, function(k) lag(varx, n = k, order_by = id, default = 1) == 0)) == m & varx == 1, 
                       1, 
                       0)) %>%
  data.frame()

给了

   id time varx eleg
1   1    1    0    0
2   1    2    0    0
3   1    3    0    0
4   1    4    0    0
5   1    5    1    1
6   1    6    1    0
7   1    7    0    0
8   2    1    0    0
9   2    2    1    0
10  2    3    1    0
11  2    4    1    0
12  2    5    1    0
13  2    6    1    0
14  3    1    0    0
15  3    2    0    0
16  3    3    0    0
17  3    4    1    1


样本数据:

df <- structure(list(id = c(1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 
3, 3, 3, 3), time = c(1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 
1, 2, 3, 4), varx = c(0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 
0, 0, 0, 1)), .Names = c("id", "time", "varx"), row.names = c(NA, 
-17L), class = "data.frame")

【讨论】:

    【解决方案3】:

    这是另一种方法,使用dplyrzoo

    library(dplyr)
    library(zoo)
    
    df %>% 
      group_by(id) %>% 
      mutate(elegnew = as.integer(varx == 1 & 
                          rollsum(varx == 1, k = 4, align = "right", fill = 0) == 1))
    
    # # A tibble: 17 x 5
    # # Groups:   id [3]
    # id  time  varx  eleg elegnew
    # <dbl> <dbl> <dbl> <dbl>   <int>
    #   1    1.    1.    0.    0.       0
    # 2    1.    2.    0.    0.       0
    # 3    1.    3.    0.    0.       0
    # 4    1.    4.    0.    0.       0
    # 5    1.    5.    1.    1.       1
    # 6    1.    6.    1.    0.       0
    # 7    1.    7.    0.    0.       0
    # 8    2.    1.    0.    0.       0
    # 9    2.    2.    1.    0.       0
    # 10    2.    3.    1.    0.       0
    # 11    2.    4.    1.    0.       0
    # 12    2.    5.    1.    0.       0
    # 13    2.    6.    1.    0.       0
    # 14    3.    1.    0.    0.       0
    # 15    3.    2.    0.    0.       0
    # 16    3.    3.    0.    0.       0
    # 17    3.    4.    1.    1.       1
    

    想法是按 id 分组,然后检查 a) varx 是否为 1 和 b) 前 3 行加上当前行 (k=4) 中 varx=1 事件的总和是否为 1(这意味着所有前 3必须为 0)。我假设 varx 是 0 或 1。

    【讨论】:

    • 谢谢,它有效。另外,感谢您的解释,确实 varx 是 0 或 1。
    【解决方案4】:

    您已要求提供dplyr 解决方案,最好是。
    下面是一个base R,有一个函数可以适应“在前24行”,只需将n = 24传递给函数即可。

    fun <- function(DF, crit = "varx", new = "eleg", n = 3){
      DF[[new]] <- 0
      for(i in seq_len(nrow(DF))[-seq_len(n)]){
        if(all(DF[[crit]][(i - n):(i - 1)] == 0) && DF[[crit]][i] == 1)
          DF[[new]][i] <- 1
      }
      DF
    }
    
    
    sp <- split(table[-4], table[-4]$id)
    new_df <- do.call(rbind, lapply(sp, fun))
    row.names(new_df) <- NULL
    identical(table, new_df)
    #[1] TRUE
    

    请注意,如果您要创建一个新列 eleg,您可能不需要拆分 table[-4],只需拆分 table,因为第 4 列尚不存在。
    你可以做do.call(rbind, lapply(sp, fun, n = 24)),其余的都是一样的。

    【讨论】:

      猜你喜欢
      • 2020-03-11
      • 1970-01-01
      • 2014-11-03
      • 2020-06-05
      • 1970-01-01
      • 2020-07-24
      • 2016-10-21
      • 2015-01-21
      • 2023-02-08
      相关资源
      最近更新 更多