Reputation: 473
I have a data frame looking kind of like this:
id <- c(1, 1, 1, 2, 2, 2, 3, 3, 3)
x <- c(1, 1, 0, 0, 1, 1, 1, 1, 1)
df <- data.frame(id, x)
I want to keep only the first value that = 1 for each id, otherwise I want it to = 0, looking like this:
id x
<dbl> <dbl>
1 1 1
2 1 0
3 1 0
4 2 0
5 2 1
6 2 0
7 3 1
8 3 0
9 3 0
I've tried this code, but with no luck:
df %>%
group_by(id) %>%
mutate(x = if (any(x == 1)) replace(x,
row_number() != 1, 0) else x)
```
Upvotes: 4
Views: 2031
Reputation: 39858
One dplyr
solution could be:
df %>%
group_by(id) %>%
mutate(x = +(x == 1 & !duplicated(x)))
id x
<dbl> <int>
1 1 1
2 1 0
3 1 0
4 2 0
5 2 1
6 2 0
7 3 1
8 3 0
9 3 0
Upvotes: 0
Reputation: 39647
In base you can use ave
to group by id
and use ifelse
with cumsum
to replace after the first 1 with 0.
df$x <- ave(df$x, df$id, FUN=function(x) ifelse(cumsum(x)>1,0,x))
#df$x <- ifelse(ave(df$x, df$id, FUN=cumsum)>1, 0, df$x) #Alternativ
#df$x <- with(df, ifelse(ave(x, id, FUN=cumsum)>1, 0, x)) #Alternativ
df
# id x
#1 1 1
#2 1 0
#3 1 0
#4 2 0
#5 2 1
#6 2 0
#7 3 1
#8 3 0
#9 3 0
Upvotes: 3
Reputation: 34291
If x
is binary, you can do:
library(dplyr)
df %>%
group_by(id) %>%
mutate(x = +(which.max(x) == row_number()))
# A tibble: 9 x 2
# Groups: id [3]
id x
<dbl> <int>
1 1 1
2 1 0
3 1 0
4 2 0
5 2 1
6 2 0
7 3 1
8 3 0
9 3 0
If there are groups of x
that don't contain a 1
you will need an extra condition:
df %>%
group_by(id) %>%
mutate(x = +(x == 1 & which.max(x) == row_number()))
Upvotes: 3
Reputation: 388807
Using replace
you can do :
library(dplyr)
df %>% group_by(id) %>% mutate(y = replace(x, -match(1L, x), 0L))
#OR
#mutate(y = replace(x, which.max(x), 0L))
# id x y
# <dbl> <dbl> <dbl>
#1 1 1 1
#2 1 1 0
#3 1 0 0
#4 2 0 0
#5 2 1 1
#6 2 1 0
#7 3 1 1
#8 3 1 0
#9 3 1 0
Upvotes: 3
Reputation: 25225
An option using data.table
:
library(data.table)
setDT(df)[, x := replace(rep(0L, .N), match(1L, x), 1L), id]
Upvotes: 1