每次在一组观察中出现时,我如何要求 R 标记特定模式(即列值从 1 变为 0)?

How can I ask R to flag a specific pattern (i.e., column value changes from 1 to 0) each time it occurs within a group of observations?

下面的 reprex 模仿了我的数据:对于每个人,我在不同时间有不同的 'res' 值。我需要一个指示变量 ('flag') 来告诉我每次 'res' 在给定的人中从 1 变为 0,并且我希望 'flag' 第一次等于 1(并且第一次 只有) 'res'= 0 在 'res' = 1 之后。最后,我想计算 'flag' = 1 的次数每个人。

我的代码有两个问题:

  1. 它每次在 'res'= 1 之后标记 'res' = 0(但我需要 'flag'= 1 only 第一次'res'=0).
  2. 计算次数 'flag' = 1 不起作用。

注:最后的'res_next_time'必然是NA。根据我的数据定义,我永远不会在这里有 'flag'=1,所以它默认为 0 没关系。

感谢您的帮助!

#Load packages
library(Hmisc)
#> Loading required package: lattice
#> Loading required package: survival
#> Loading required package: Formula
#> Loading required package: ggplot2
#> 
#> Attaching package: 'Hmisc'
#> The following objects are masked from 'package:base':
#> 
#>     format.pval, units
library(dplyr)
#> Warning: package 'dplyr' was built under R version 4.0.4
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:Hmisc':
#> 
#>     src, summarize
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(tidyr)
#> Warning: package 'tidyr' was built under R version 4.0.5

#Create data set
person <- c(1, 1, 1, 2, 3, 3, 3, 3, 3, 3)
time <- c(1, 2, 3, 1, 2, 1, 2, 3, 4, 5)
res <- c(1, 0, 1, 1, 1, 0, 0, 1, 0, 1)

#Populate data frame
d <- cbind(person, time, res)
d <- as.data.frame(d)

#Create new variable equal to 'res' at the person's next time point
d$res_next_time <- Lag(d$res, -1)

#Group times by person
d %>% 
  group_by(person) %>% 
#Create a new variable 'flag' = 1 when a person's 'res' changes from 1 to 0, and 'flag' = 0 otherwise
  mutate(flag = case_when(res_next_time < 1 ~ 1, TRUE ~ 0)) %>%
#Because 'flag'= 1 is at the time of 'res'= 1 before 'res'= 0, we lag it to have 'flag' = 1 at 'res' = 0
  mutate(flag_res0 = Lag(flag, +1)) %>%
#Replace the NAs in 'flag_res0' with 0
  replace_na(list(flag_res0 = 0)) %>%
  #mutate(flag_res0 = as.numeric(flag_res0 & cumsum(flag_res0) <= 1)) %>%
#Count number of flags per person
  mutate(mig_freq = sum(flag_res0)) %>%
#Limit the data to only include the final indicator
  select('person', 'time', 'res', 'flag_res0')
#> # A tibble: 10 x 4
#> # Groups:   person [3]
#>    person  time   res flag_res0
#>     <dbl> <dbl> <dbl>     <dbl>
#>  1      1     1     1         0
#>  2      1     2     0         1
#>  3      1     3     1         0
#>  4      2     1     1         0
#>  5      3     2     1         0
#>  6      3     1     0         1
#>  7      3     2     0         1
#>  8      3     3     1         0
#>  9      3     4     0         1
#> 10      3     5     1         0

reprex package (v0.3.0)

于 2021-04-15 创建

这里给出分两步解决问题的方案:

  1. 使用dplyr的lag函数计算res的前一个值,而不是res的下一个值。我们在分组数据框中执行此操作,因此 res_last_time 的第一个值对于每个人都是 NA。
  2. 在分组数据框中使用cumsum只为每个人保留第一个标志=1。
d %>% 
    group_by(person) %>% 
    mutate(res_last_time = lag(res, 1)) %>%
    mutate(flag = res == 0 & res_last_time == 1) %>%
    mutate(flag = as.numeric(flag & cumsum(flag) <= 1))

使用相同的 d data.frame,这是我得到的结果:

#> # A tibble: 10 x 5
#> # Groups:   person [3]
#>    person  time   res res_last_time  flag
#>     <dbl> <dbl> <dbl>         <dbl> <dbl>
#>  1      1     1     1            NA     0
#>  2      1     2     0             1     1
#>  3      1     3     1             0     0
#>  4      2     1     1            NA     0
#>  5      3     2     1            NA     0
#>  6      3     1     0             1     1
#>  7      3     2     0             0     0
#>  8      3     3     1             0     0
#>  9      3     4     0             1     0
#> 10      3     5     1             0     0

reprex package (v1.0.0)

于 2021-04-15 创建

我的解决方案不需要 res_next_time 列。我认为@Paul PR 更简洁。

# using your data d
d %>% 
  group_by(person) %>% 
  mutate(flag2 = if_else(lag(res) == 1 & res == 0 &  
                           !(duplicated(lag(res) == 1 & res == 0)),1, 0, 0))

你可以在最后加上ungroup()。这可能很重要,具体取决于接下来会发生什么。这基本上是 'if TRUE TRUE and not duplicated, then...'

您的评论表明您不是在寻找第一次出现,而是在组内出现的任何一次。

其实就简单多了。

(d %>% 
  group_by(person) %>% 
  mutate(flag = if_else(lag(res) == 1 & res == 0, 1, 0, 0)))

输出如下所示。 (我在您的示例数据末尾添加了数据以显示我出现的情况。)

# # A tibble: 13 x 4
# # Groups:   person [3]
#    person  time   res  flag
#     <dbl> <dbl> <dbl> <dbl>
#  1      1     1     1     0
#  2      1     2     0     1
#  3      1     3     1     0
#  4      2     1     1     0
#  5      3     2     1     0
#  6      3     1     0     1
#  7      3     2     0     0
#  8      3     3     1     0
#  9      3     4     0     1
# 10      3     5     1     0
# 11      3     6     0     1
# 12      1     7     1     0
# 13      1     8     0     1