在数据帧中识别出行元素序列时替换单元格

时间:2019-02-21 12:26:33

标签: r dataframe filter tidyverse

我目前有一个数据集,可以简化如下:

df <- data.frame(c(1,1,1,2,2,2,3,3,3),c(TRUE,FALSE,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,FALSE), 
           c(0,3,0,5,5,0,0,0,7), c("a","b","c","d","a","b","c","d","a"))
colnames(df) <- c("ID", "Status", "Number", "Letter")

  ID Status Number Letter
1  1   TRUE      0      a
2  1  FALSE      3      b
3  1   TRUE      0      c
4  2  FALSE      5      d
5  2  FALSE      5      a
6  2   TRUE      0      b
7  3   TRUE      0      c
8  3   TRUE      0      d
9  3  FALSE      7      a

基本上,我想标识在FALSE之前出现TRUE的ID。然后,我想用随后的FALSE行(即第2、9、9行)替换显示为TRUE的行(即第1,7,8行)中的Status和Number。最终结果应如下所示:

  ID Status Number Letter
1  1  FALSE      3      a
2  1  FALSE      3      b
3  1   TRUE      0      c
4  2  FALSE      5      d
5  2  FALSE      5      a
6  2   TRUE      0      b
7  3  FALSE      7      c
8  3  FALSE      7      d
9  3  FALSE      7      a

最后一列仅表示我还有其他特定于观察的变量,我不能简单地用下一个替换整个行。

到目前为止,从这篇文章(R - Identify a sequence of row elements by groups in a dataframe)开始,我设法获得了第一部分:

library(tidyverse)
extract <- df %>% group_by(ID) %>%
  filter(ifelse(Status == FALSE,
                lag(Status) == TRUE,
                lead(Status) == FALSE)) 

# A tibble: 4 x 4
# Groups:   ID [2]
     ID Status Number Letter
  <dbl> <lgl>   <dbl> <fct> 
1    1. TRUE       0. a     
2    1. FALSE      3. b     
3    3. TRUE       0. d     
4    3. FALSE      7. a  

非常感谢您从这里开始的任何帮助。我试图尽可能准确地指出我的问题,如果有任何不清楚的地方,请告诉我。


编辑:按照@Henrik的建议,我将为我的问题增加一些复杂性。该数据集应在这方面起作用:

df <- data.frame(c(1,1,1,2,2,2,3,3,3,4,4,4,4,4),c(TRUE,TRUE,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,
                                                  FALSE,TRUE,FALSE,TRUE,FALSE,TRUE), 
                 c(0,0,0,5,5,0,0,0,7,0,6,0,3,0), c("a","b","c","d","a","b","c","d","a","b",
                                                   "c","d","a","b"))
colnames(df) <- c("ID", "Status", "Number", "Letter")

> df
   ID Status Number Letter
1   1   TRUE      0      a
2   1   TRUE      0      b
3   1   TRUE      0      c
4   2  FALSE      5      d
5   2  FALSE      5      a
6   2   TRUE      0      b
7   3   TRUE      0      c
8   3   TRUE      0      d
9   3  FALSE      7      a
10  4   TRUE      0      b
11  4  FALSE      6      c
12  4   TRUE      0      d
13  4  FALSE      3      a
14  4   TRUE      0      b

这是我建议的解决方案,但是不适用于单独的行(请参阅12):

df2 <- df %>% 
  group_by(ID) %>%
  mutate(Status2 = if (!all(Status)) replace(Status, cumsum(!Status) < 1, FALSE) else TRUE,
         Number2 = if (!all(Status)) replace(Number, cumsum(!Status) < 1,
                                                      first(Number[Status == FALSE])) 
                   else first(replace(Number, cumsum(!Status) < 1, Number[Status == TRUE])))

> df2
# A tibble: 14 x 6
# Groups:   ID [4]
      ID Status Number Letter Status2 Number2
   <dbl> <lgl>   <dbl> <fct>  <lgl>     <dbl>
 1    1. TRUE       0. a      TRUE         0.
 2    1. TRUE       0. b      TRUE         0.
 3    1. TRUE       0. c      TRUE         0.
 4    2. FALSE      5. d      FALSE        5.
 5    2. FALSE      5. a      FALSE        5.
 6    2. TRUE       0. b      TRUE         0.
 7    3. TRUE       0. c      FALSE        7.
 8    3. TRUE       0. d      FALSE        7.
 9    3. FALSE      7. a      FALSE        7.
10    4. TRUE       0. b      FALSE        6.
11    4. FALSE      6. c      FALSE        6.
12    4. TRUE       0. d      TRUE         0.
13    4. FALSE      3. a      FALSE        3.
14    4. TRUE       0. b      TRUE         0.

第12行中Number2的结果应对应于下一行,即为3。

2 个答案:

答案 0 :(得分:1)

您可以这样做:

library(dplyr)

df %>%
  group_by(ID) %>%
  mutate(flag = coalesce(Status == TRUE &
                           lead(Status == FALSE), FALSE)) %>%
  group_by(ID, grp = cumsum(+(Status != lag(Status, default = "rndom")))) %>%
  mutate(Status = ifelse(any(flag == TRUE), FALSE, Status)) %>% ungroup() %>%
  select(-flag, -grp)

输出:

# A tibble: 9 x 4
     ID Status Number Letter
  <dbl> <lgl>   <dbl> <fct> 
1     1 FALSE       0 a     
2     1 FALSE       3 b     
3     1 TRUE        0 c     
4     2 FALSE       5 d     
5     2 FALSE       5 a     
6     2 TRUE        0 b     
7     3 FALSE       0 c     
8     3 FALSE       0 d     
9     3 FALSE       7 a     

答案 1 :(得分:1)

另一个选项,其中我们用replace cumsum(!Status) < 1 TRUEFALSE的所有值,即替换第一个FALSE之前的所有值。

df %>% 
  group_by(ID) %>% 
  mutate(new_status = replace(Status, cumsum(!Status) < 1, FALSE))
# A tibble: 9 x 5
# Groups:   ID [3]
#     ID Status Number Letter new_status
#  <dbl> <lgl>   <dbl> <fct>  <lgl>     
#1     1 TRUE        0 a      FALSE     
#2     1 FALSE       3 b      FALSE     
#3     1 TRUE        0 c      TRUE      
#4     2 FALSE       5 d      FALSE     
#5     2 FALSE       5 a      FALSE     
#6     2 TRUE        0 b      TRUE      
#7     3 TRUE        0 c      FALSE     
#8     3 TRUE        0 d      FALSE     
#9     3 FALSE       7 a      FALSE