【问题标题】:Comparing consecutive rows over specific conditions by group按组比较特定条件下的连续行
【发布时间】:2017-11-17 03:43:36
【问题描述】:

对于每个参与者和每个试验,我需要检查 CURRENT_ID 中的所有连续行,第一行在 A 列中的值为 0,最后一行在 B 列中的值为 0。如果这两个条件都满足,我希望新列 C 中的值为 0,如果不满足,我希望值为 1。

head(mydf, 10)

#> # A tibble: 10 x 6
#>        A     B participant trial CURRENT_ID     C
#>    <dbl> <dbl> <chr>       <dbl>      <dbl> <dbl>
#>  1     0     1 ppt01          45          3     0
#>  2     1     0 ppt01          45          4     0
#>  3     0     1 ppt01          45         10     0
#>  4     0     0 ppt01          45         11     0
#>  5     1     0 ppt01          45         12     0
#>  6     0     1 ppt01          87          2     0
#>  7     1     0 ppt01          87          3     0
#>  8     1     1 ppt01          87          4     1
#>  9     1     1 ppt01          87          5     1
#> 10     0     1 ppt01          34          6     0

我需要考虑每个参与者和试验的每一对连续行(基于 CURRENT_ID 的值是连续的)。在上面的示例中,第 8 行和第 9 行在新列 C 中的值为 1,因为第 8 行在 A 列中有 1(而不是 0),而第 9 行在 B 列中有 1(而不是 0)。

这里是如何比较行的示例,参与者 ppt01 和试验 87

    A   B    participant   trial   CURRENT_ID      C

    0   1    ppt01         87      2               0
    1   0    ppt01         87      3               0

    1   0    ppt01         87      3               0
    1   1    ppt01         87      4               1

    1   1    ppt01         87      4               1
    1   1    ppt01         87      5               1

数据:

mydf <- structure(list(A = c(0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 
1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 
1, 1), B = c(1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 
0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1
), participant = c("ppt01", "ppt01", "ppt01", "ppt01", "ppt01", 
"ppt01", "ppt01", "ppt01", "ppt01", "ppt01", "ppt01", "ppt01", 
"ppt01", "ppt01", "ppt01", "ppt01", "ppt01", "ppt01", "ppt01", 
"ppt01", "ppt01", "ppt02", "ppt02", "ppt02", "ppt02", "ppt02", 
"ppt02", "ppt02", "ppt02", "ppt02", "ppt02", "ppt02", "ppt02", 
"ppt02", "ppt02", "ppt02", "ppt02"), trial = c(45, 45, 45, 45, 
45, 87, 87, 87, 87, 34, 34, 34, 34, 34, 34, 8, 8, 8, 8, 8, 8, 
87, 87, 87, 87, 55, 55, 55, 55, 55, 55, 22, 22, 22, 22, 22, 22
), CURRENT_ID = c(3, 4, 10, 11, 12, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
11, 5, 6, 9, 10, 11, 12, 2, 3, 4, 5, 5, 6, 9, 10, 11, 12, 2, 
3, 4, 10, 11, 12), C = c(0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 
1, 0, 1, 1)), class = c("spec_tbl_df", "tbl_df", "tbl", "data.frame"
), row.names = c(NA, -37L), spec = structure(list(cols = list(
    A = structure(list(), class = c("collector_double", "collector"
    )), B = structure(list(), class = c("collector_double", "collector"
    )), participant = structure(list(), class = c("collector_character", 
    "collector")), trial = structure(list(), class = c("collector_double", 
    "collector")), CURRENT_ID = structure(list(), class = c("collector_double", 
    "collector")), C = structure(list(), class = c("collector_double", 
    "collector"))), default = structure(list(), class = c("collector_guess", 
"collector")), skip = 1), class = "col_spec"))

【问题讨论】:

  • 您是否尝试通过participant 和/或trial 执行此操作?您是否也尝试对CURRENT_ID 的连续值执行此操作?
  • 数据中提供的 C 列是否正确 - 为什么第 8,9 行会得到 1 - 与我的描述不匹配!?
  • @akash87 我需要考虑每个参与者和试验的 CURRENT_ID 的连续值。
  • @BigDataScientist 我添加了一个编辑来更好地解释为什么第 8 行和第 9 行在 C 中的值为 1。
  • 那么为什么在当前行中A = 0B=0上方的行时,试用22有C=1

标签: r conditional-statements


【解决方案1】:

如果您想将 AB 对分组到参与者试验组中,这应该可行:

d %>% group_by(participant, trial) %>% mutate(AB = ceiling(1:n()/2)) %>% group_by(participant, trial, AB) %>% mutate(newC = ifelse(length(A) == 1 | (A[1] == 0 & B[2] == 0), 0, 1))

我已将新列保留在其中,以便您了解这是如何完成的。

输出:

# A tibble: 15 x 8
       A     B participant trial CURRENT_ID     C    AB  newC
   <int> <int>       <chr> <int>      <int> <int> <dbl> <dbl>
 1     0     1       ppt01    45          3     0     1     0
 2     1     0       ppt01    45          4     0     1     0
 3     0     1       ppt01    45         10     0     2     0
 4     0     0       ppt01    45         11     0     2     0
 5     1     0       ppt01    45         12     0     3     0
 6     0     1       ppt01    87          2     0     1     0
 7     1     0       ppt01    87          3     0     1     0
 8     1     1       ppt01    87          4     1     2     1
 9     1     1       ppt01    87          5     1     2     1
10     0     1       ppt01    34          6     0     1     0
11     0     0       ppt01    34          7     0     1     0
12     0     0       ppt01    34          8     0     2     0
13     0     0       ppt01    34          9     0     2     0
14     0     0       ppt01    34         10     0     3     0
15     1     0       ppt01    34         11     0     3     0

否则,如最初所述:

require(dplyr)
d %>% group_by(participant, trial) %>% mutate(newC = ifelse(A[1] == 0 & B[n()] == 0, 0, 1))

输出:

Source: local data frame [15 x 7]
Groups: participant, trial [3]

# A tibble: 15 x 7
       A     B participant trial CURRENT_ID     C  newC
   <int> <int>       <chr> <int>      <int> <int> <dbl>
 1     0     1       ppt01    45          3     0     0
 2     1     0       ppt01    45          4     0     0
 3     0     1       ppt01    45         10     0     0
 4     0     0       ppt01    45         11     0     0
 5     1     0       ppt01    45         12     0     0
 6     0     1       ppt01    87          2     0     1
 7     1     0       ppt01    87          3     0     1
 8     1     1       ppt01    87          4     1     1
 9     1     1       ppt01    87          5     1     1
10     0     1       ppt01    34          6     0     0
11     0     0       ppt01    34          7     0     0
12     0     0       ppt01    34          8     0     0
13     0     0       ppt01    34          9     0     0
14     0     0       ppt01    34         10     0     0
15     1     0       ppt01    34         11     0     0

我使用dput() 使用了您的数据子集:

d <- structure(
  list(
    A = c(0L, 1L, 0L, 0L, 1L, 0L, 1L, 1L, 1L, 0L,
          0L, 0L, 0L, 0L, 1L),
    B = c(1L, 0L, 1L, 0L, 0L, 1L, 0L, 1L, 1L,
          1L, 0L, 0L, 0L, 0L, 0L),
    participant = c(
      "ppt01",
      "ppt01",
      "ppt01",
      "ppt01",
      "ppt01",
      "ppt01",
      "ppt01",
      "ppt01",
      "ppt01",
      "ppt01",
      "ppt01",
      "ppt01",
      "ppt01",
      "ppt01",
      "ppt01"
    ),
    trial = c(
      45L,
      45L,
      45L,
      45L,
      45L,
      87L,
      87L,
      87L,
      87L,
      34L,
      34L,
      34L,
      34L,
      34L,
      34L
    ),
    CURRENT_ID = c(3L, 4L, 10L, 11L, 12L, 2L, 3L, 4L, 5L, 6L,
                   7L, 8L, 9L, 10L, 11L),
    C = c(0L, 0L, 0L, 0L, 0L, 0L, 0L, 1L,
          1L, 0L, 0L, 0L, 0L, 0L, 0L)
  ),
  .Names = c("A", "B", "participant",
             "trial", "CURRENT_ID", "C"),
  class = "data.frame",
  row.names = c(NA,-15L)
)

【讨论】:

  • 在我的理解中,行 6-9 不应该为行 newC 获得 1。你还为trial分组还是我错过了什么?
  • 是的,他说他希望满足这两个条件。如果他有其他意图,他必须澄清。
  • @ssp3nc3r 感谢您的回答。但是,第 6 行和第 7 行的值应为 0,因为第 6 行在 A 列中的值为 0,第 7 行在 B 列中的值为 0。第 8 行和第 9 行是正确的。
  • 根据您的解释,参与者和试验的唯一配对创建了一个组,并且在该组内,您希望第一行的A有一个0,最后一行的B有一个0. 如果这不是你想要解释的,你需要修改你的意思。
  • @ssp3nc3r 我添加了 EDIT2 以更好地解释如何考虑连续行。
【解决方案2】:

一个老问题,但仍然相关,还没有被接受的答案!

问题中的条件并不是很清楚,但我找到了两种解释方法。

1) 如果 B == 1lag(A) == 1 为 TRUE

2) 如果 A == 1B == 1 在至少两个连续的行中,则为 TRUE。

我正在尝试为两种解释 OP 的方式提供解决方案。

library(tidyverse)

 mydf2 <- mydf %>%
  group_by(participant, trial) %>%
  mutate(consec_rows = cumsum(c(1, diff(CURRENT_ID) != 1))) %>%
  group_by(participant, trial, consec_rows) %>%
  mutate(cond_consec = B & lag(A))

 as.data.frame(mydf2)
#>    A B participant trial CURRENT_ID C consec_rows cond_consec
#> 1  0 1       ppt01    45          3 0           1          NA
#> 2  1 0       ppt01    45          4 0           1       FALSE
#> 3  0 1       ppt01    45         10 0           2          NA
#> 4  0 0       ppt01    45         11 0           2       FALSE
#> 5  1 0       ppt01    45         12 0           2       FALSE
#> 6  0 1       ppt01    87          2 0           1          NA
#> 7  1 0       ppt01    87          3 0           1       FALSE
#> 8  1 1       ppt01    87          4 1           1        TRUE
#> 9  1 1       ppt01    87          5 1           1        TRUE
#> 10 0 1       ppt01    34          6 0           1          NA
#> 11 0 0       ppt01    34          7 0           1       FALSE
#> 12 0 0       ppt01    34          8 0           1       FALSE
#> 13 0 0       ppt01    34          9 0           1       FALSE
#> 14 0 0       ppt01    34         10 0           1       FALSE
#> 15 1 0       ppt01    34         11 0           1       FALSE
#> 16 0 1       ppt01     8          5 0           1          NA
#> 17 1 0       ppt01     8          6 0           1       FALSE
#> 18 0 1       ppt01     8          9 0           2          NA
#> 19 0 0       ppt01     8         10 0           2       FALSE
#> 20 0 0       ppt01     8         11 0           2       FALSE
#> 21 1 0       ppt01     8         12 0           2       FALSE
#> 22 0 1       ppt02    87          2 0           1          NA
#> 23 0 0       ppt02    87          3 0           1       FALSE
#> 24 0 0       ppt02    87          4 0           1       FALSE
#> 25 1 0       ppt02    87          5 0           1       FALSE
#> 26 0 1       ppt02    55          5 0           1          NA
#> 27 1 0       ppt02    55          6 0           1       FALSE
#> 28 0 1       ppt02    55          9 0           2          NA
#> 29 1 0       ppt02    55         10 0           2       FALSE
#> 30 0 1       ppt02    55         11 1           2        TRUE
#> 31 1 0       ppt02    55         12 0           2       FALSE
#> 32 0 1       ppt02    22          2 0           1          NA
#> 33 1 0       ppt02    22          3 0           1       FALSE
#> 34 0 1       ppt02    22          4 1           1        TRUE
#> 35 0 1       ppt02    22         10 0           2          NA
#> 36 1 0       ppt02    22         11 1           2       FALSE
#> 37 1 1       ppt02    22         12 1           2        TRUE

据我了解 OP,我认为第 36 行中所需的 C == 1 实际上应该为零。

reprex package (v0.3.0) 于 2020-05-16 创建

或者,如果条件是连续行中的 A == 1 & B == 1:

mydf %>%
  group_by(participant, trial, consec = cumsum(c(1, diff(CURRENT_ID) != 1))) %>%
  mutate(cond_consec = 
           rep(rle(A & B)$values & rle(A & B)$lengths >= 2, rle(A & B)$lengths))

#> # A tibble: 37 x 8
#> # Groups:   participant, trial, consec [11]
#>        A     B participant trial CURRENT_ID     C consec cond_consec
#>    <dbl> <dbl> <chr>       <dbl>      <dbl> <dbl>  <dbl> <lgl>      
#>  1     0     1 ppt01          45          3     0      1 FALSE      
#>  2     1     0 ppt01          45          4     0      1 FALSE      
#>  3     0     1 ppt01          45         10     0      2 FALSE      
#>  4     0     0 ppt01          45         11     0      2 FALSE      
#>  5     1     0 ppt01          45         12     0      2 FALSE      
#>  6     0     1 ppt01          87          2     0      3 FALSE      
#>  7     1     0 ppt01          87          3     0      3 FALSE      
#>  8     1     1 ppt01          87          4     1      3 TRUE       
#>  9     1     1 ppt01          87          5     1      3 TRUE       
#> 10     0     1 ppt01          34          6     0      3 FALSE  

说明第 1 部分

  • "groups" of consecutive numbers分组
  • 0 和 1 被解释为 TRUE 和 FALSE,因此我们可以省略 == 运算符。
    B &amp; lag(A) 等价于B == 1 &amp; lag(A) == 1
  • 参考是 B 和 A 的一个滞后 - 允许跨行比较值。

第 2 部分(如果 A == 1 &amp; B == 1 在连续行中)

  • 使用条件语句的rle 并检查rle$length 是否为TRUE >= 2
  • 重复生成的向量以便将其与 mutate 一起使用

【讨论】:

  • 我认为在不编写编译代码的情况下最快可以使用rleidv
  • @jangorecki 我不知道 rleidv。我会检查一下。谢谢!
【解决方案3】:

基础 R 解决方案:

mydf$grouping_vec <- with(mydf, paste(participant, trial, 
                           ave(CURRENT_ID, participant, trial, FUN = function(x){
    cumsum(c(1, ifelse(diff(x) > 1, 1, 0))) 
  }), sep = " - "))

data.frame(do.call("rbind", 
  lapply(split(mydf, mydf$grouping_vec),
    function(x) {
      data.frame(cbind(x[, names(x) != "C"], 
                      C = rep(if(x$A[1] == 0 & x$B[nrow(x)] == 0){0}else{1}, nrow(x))))}
  )),
  row.names = NULL)

Tidyverse 解决方案:

library(tidyverse)

mydf %>% 
  mutate(grouping_vec = str_c(participant, 
                              trial, 
                              cumsum(c(1, diff(CURRENT_ID) != 1))),
                              sep = " - ")) %>% 
  group_by(grouping_vec) %>% 
  mutate(C = if_else(first(A) == 0 & last(B) == 0, 0, 1)) %>% 
  ungroup() %>% 
  select(-grouping_vec)

【讨论】:

  • 感谢您的贡献!不幸的是,这两种解决方案都没有给出 C 列中显示的所需输出。诚然,我也不确定我是否完全理解这种情况 - 而且我相信 C 列(第 36 行)可能存在错误
  • @Tjebo 这个问题的措辞很糟糕。我认为 OP 意味着他们想要考虑 A 和 B 的累积差异,但老实说谁知道。
  • 但是请把这个答案留在这里——我喜欢使用 first 和 last ,它肯定回答了 one 解释答案的方式。我看到你已经更新了你的 tidyverse 解决方案。您可以通过仅使用不带 ifelse 的条件来简化分组语句(例如我在帖子中使用的)cumsum(c(1, diff(CURRENT_ID) != 1)
  • @Tjebo True 将进行相应的编辑!感谢您的帮助,也感谢您的鼓励。也为您的解决方案 +1,您的解决方案似乎是唯一一个匹配的 OP 示例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-19
  • 1970-01-01
相关资源
最近更新 更多