johnny
johnny

Reputation: 473

Replace value based on row number in r

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

Answers (5)

tmfmnk
tmfmnk

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

GKi
GKi

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

lroha
lroha

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

Ronak Shah
Ronak Shah

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

chinsoon12
chinsoon12

Reputation: 25225

An option using data.table:

library(data.table)
setDT(df)[, x := replace(rep(0L, .N), match(1L, x), 1L), id]

Upvotes: 1

Related Questions