0

I'm trying to apply a rule for a group of IDs that, upon the first instance where the value for a variable in one row equals 1, all values for another variable in all subsequent rows in that group equal 1.

Essentially, here is what I am trying to do:

I have:

ID D
1  1
1  0
1  0
2  0
2  0
3  1
3  0
3  0
4  1
4  0
4  1
4  1
4  1
4  0

I want:

ID D PREV
1  1  0
1  0  1
1  0  1
2  0  0
2  0  0
3  1  0
3  0  1
3  0  1
4  1  0
4  0  1
4  1  1
4  1  1
4  0  1

I'm trying to use dplyr to iterate through a series of grouped rows, in each one applying an ifelse function. My code looks like this:

data$prev = 0
data <-   
data %>%
group_by(id)%>%
mutate(prev = if_else(lag(prev) == 1 | lag(d) == 1, 1, 0))

But for some reason, this is not applying the ifelse function over the whole group, resulting in data that looks something like this:

ID D PREV
1  1  0
1  0  1
1  0  0
2  0  0
2  0  0
3  1  0
3  0  1
3  0  0
4  1  0
4  0  1
4  1  0
4  1  1
4  0  1

Can anyone help me with this?

3 Answers 3

1

What about this:

library(dplyr)
df %>% 
 group_by(ID) %>% 
 mutate(prev = +(cumsum(c(0, D[-length(D)])) > 0)) %>% 
 ungroup()

#> # A tibble: 14 x 3
#>       ID     D  prev
#>    <int> <int> <int>
#>  1     1     1     0
#>  2     1     0     1
#>  3     1     0     1
#>  4     2     0     0
#>  5     2     0     0
#>  6     3     1     0
#>  7     3     0     1
#>  8     3     0     1
#>  9     4     1     0
#> 10     4     0     1
#> 11     4     1     1
#> 12     4     1     1
#> 13     4     1     1
#> 14     4     0     1

To explain what it does, let's just take a simple vector. The calc will be the same for each group.

Be x our vector

x <- c(0,0,0,1,1,0,0,2,3,4)

Do the cumulative sum over x

cumsum(x)
#>  [1]  0  0  0  1  2  2  2  4  7 11

You are interested only on value above zeros, therefore:

cumsum(x)>0
#>  [1] FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE

You don't want logical, but numeric. Just a + makes the trick

+(cumsum(x)>0)
#>  [1] 0 0 0 1 1 1 1 1 1 1

However, you want the 1s delayed by 1. Thus, we had a zero on top of x

+(cumsum(c(0,x))>0)
#>  [1] 0 0 0 0 1 1 1 1 1 1 1

We need to keep the same length, so we remove the last value of x.

+(cumsum(c(0, x[-length(x)])) > 0)
#>  [1] 0 0 0 0 1 1 1 1 1 1

And that makes the trick.

Sign up to request clarification or add additional context in comments.

3 Comments

This worked! How did this do what I wanted?
I've added an edit to explain what it does
However, @akrun solution is better. You should use that code! :-)
1

We can use lag

library(dplyr)
df %>% 
   group_by(ID) %>%
   mutate(prev = lag(cumsum(D) > 0, default = 0))

-output

# A tibble: 14 x 3
# Groups:   ID [4]
#      ID     D  prev
#   <dbl> <dbl> <dbl>
# 1     1     1     0
# 2     1     0     1
# 3     1     0     1
# 4     2     0     0
# 5     2     0     0
# 6     3     1     0
# 7     3     0     1
# 8     3     0     1
# 9     4     1     0
#10     4     0     1
#11     4     1     1
#12     4     1     1
#13     4     1     1
#14     4     0     1

data

df <- data.frame(
    ID = c(1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4),
    D = c(1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0)
)

Comments

0

You can use a new function from dplyr dplyr::group_modify to apply function over groups

df <- data.frame(
    ID = c(1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4),
    D = c(1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0)
)
df %>% group_by(ID) %>% group_modify(
    function(x, y){
        boo <- x[1, ]$D == 1
        ifelse(boo, 
               {x$prev = 1
                x$prev[1] = 0
               },
               {x$prev = 0})
        
        x
    }
)

# A tibble: 14 x 3
# Groups:   ID [4]
      ID     D  prev
   <dbl> <dbl> <dbl>
 1     1     1     0
 2     1     0     1
 3     1     0     1
 4     2     0     0
 5     2     0     0
 6     3     1     0
 7     3     0     1
 8     3     0     1
 9     4     1     0
10     4     0     1
11     4     1     1
12     4     1     1
13     4     1     1
14     4     0     1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.