【问题标题】:Replace cells when sequence of row elements identified in data frame在数据框中识别出行元素序列时替换单元格
【发布时间】:2019-07-15 08:46:59
【问题描述】:

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

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 行)中的状态和编号。最终结果应如下所示:

  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。

【问题讨论】:

  • 可以有多次运行FALSE,因此每次运行TRUE 对应的“数字”应该由下一次后续运行FALSE 的值填充?例如。对于 ID = 1,有第四行(或更多)FALSE
  • 另外,TRUE 的原始“数字”总是0 吗?
  • @Hendrik,对于相同的 ID,可能会有 TRUE FALSE TRUE FALSE ... 的顺序。但是,对于每个 ID,所有 TRUE 行和所有 FALSE 行的 Number 都是相同的,所以在我看来,我们用哪一行 FALSE 替换它应该无关紧要。关于你的第二个问题:是的,在我的数据集中,所有状态为 TRUE 的行也将被分配数字 0。
  • "对于每个 ID,数字将与 [...] 所有 FALSE 行相同"。我认为情况并非如此,例如FALSE ID 4 中的数字:6 和 3。还请为第 12 行添加所需的结果。
  • @Henrik,抱歉。我是 SO 新手,仍然习惯于这个论坛的细节。没错,在我的原始数据集中,所有行(给定 ID)都具有相同的编号。但是,我在 ID = 4 中添加了通用性,因为将来我可能会遇到类似的场景,我需要这个技巧。感谢您帮助我改进我的帖子!

标签: r dataframe filter tidyverse


【解决方案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     

【讨论】:

  • 感谢您的回复。当我尝试运行您的解决方案时,我收到以下错误消息:“mutate_impl(.data, dots) 中的错误:与请求的类型不兼容:[type=character; target=integer]。”我对 R 相当陌生,不熟悉 coalesce 或您的解决方案的语法 - 知道我做错了什么吗?
  • 不太确定这是怎么回事。我已经在您的示例中再次运行它并且它有效。你的dplyr 是什么版本?也许更新到最新的,然后再试一次。
  • 谢谢。这确实是我遇到的问题。我能再问你一个问题吗?我也尝试为 Numbers 列复制您的解决方案,以便它显示来自后续行的结果,将这一行添加到 mutate: Number2 = ifelse(any(flag == TRUE), lead(Number[Status == FALSE] ), Number。但是,不幸的是,这会强制 NA。您是否知道如何解决此问题?提前非常感谢!
  • 为此,最好使用所需的输出编辑您的问题 - 因为我不是 100% 的样子。
  • 在编辑#2 中,我展示了我想要的输出并试图更清楚地说明问题。感谢您的帮助!
【解决方案2】:

另一个选项,我们 replace 的所有值 cumsum(!Status) &lt; 1TRUEFALSE,即第一个 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 

【讨论】:

  • 这正是我想要的。谢谢!我刚刚编辑了您的完整答案(关于我可以根据您的解决方案自行调整的数字)。
  • 再想一想,只要我有一个状态始终为真的 ID(即 ID = 4,Status = c(TRUE, TRUE, TRUE)),这对我不起作用。这会是一个选择吗? df %>% group_by(ID) %>% mutate(realStatus = !all(Status), new_status = if(!all(Status)) replace(Status, cumsum(!Status)
  • @WillyWonka 为了帮助人们,请编辑您的问题以包含一个足够复杂的示例。还将your comment above 中的信息添加到问题中。干杯
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-29
  • 1970-01-01
  • 2021-10-11
  • 2022-08-04
  • 2018-10-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多