R:根据多个先前行的数据修改变量

时间:2018-06-13 13:36:34

标签: r if-statement dplyr panel-data mutate

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

我有长格式的tibble(按ID分组并按时间排列的行)。 我想创建一个变量" eleg"基于" varx"。条件是" eleg" = 1如果" varx"在前3行== 0和当前行varx == 1,如果不是= 0,则为每个ID。如果可能的话使用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)

在我的真实数据集中,条件是&#34;在之前的24行&#34;如果符合条件,相同的ID可以多次使用eleg == 1。

谢谢。

4 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

其中一种方法可能是

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")

答案 2 :(得分:0)

这是另一种方法,使用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。

答案 3 :(得分:0)

您最好要求dplyr解决方案 以下是基础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)),其余的都是一样的。