Increase from previous value by condition in R [duplicate]
Increase from previous value by condition in R [duplicate]
This question already has an answer here:
I was searching for an answer to my specific problem, but I didn't find a conclusion.
I have a dataframe with data
ID a
1 0
2 0
3 1
4 1
5 1
6 1
7 0
8 1
9 1
10 0
11 1
12 0
13 0
Now i want to add "b" column with number increase from previous b if a == 1
Result like this
ID a b
1 0 0
2 0 0
3 1 1
4 1 2
5 1 3
6 1 4
7 0 0
8 1 1
9 1 2
10 0 0
11 1 1
12 0 0
13 0 0
14 1 1
15 1 2
16 1 3
17 1 4
Thanks in advance!
This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
3 Answers
3
Using dplyr an option can be to group on cumsum(a==0). This will create a group which got a previous row (if available with a=0 for all rows with a=1. Now, lag(cumsum(a==1)) will provide expected count.
dplyr
cumsum(a==0)
a=0
a=1
lag(cumsum(a==1))
library(dplyr)
df %>% group_by(grp = cumsum(a==0)) %>%
mutate(b = ifelse(a==1, lag(cumsum(a==1))+1,0)) %>%
ungroup() %>%
select(-grp) %>%
as.data.frame()
# ID a b
# 1 1 0 0
# 2 2 0 0
# 3 3 1 1
# 4 4 1 2
# 5 5 1 3
# 6 6 1 4
# 7 7 0 0
# 8 8 1 1
# 9 9 1 2
# 10 10 0 0
# 11 11 1 1
# 12 12 0 0
# 13 13 0 0
Data:
df <- read.table(text="
ID a
1 0
2 0
3 1
4 1
5 1
6 1
7 0
8 1
9 1
10 0
11 1
12 0
13 0",
header = TRUE, stringsAsFactors = FALSE)
How about the following using base R's rle
rle
df$b <- unlist(mapply(
function(len, val) if (val == 0) rep(0, len) else 1:len,
rle(df$a)$lengths, rle(df$a)$values));
df;
# ID a b
#1 1 0 0
#2 2 0 0
#3 3 1 1
#4 4 1 2
#5 5 1 3
#6 6 1 4
#7 7 0 0
#8 8 1 1
#9 9 1 2
#10 10 0 0
#11 11 1 1
#12 12 0 0
#13 13 0 0
df <- read.table(text =
"ID a
1 0
2 0
3 1
4 1
5 1
6 1
7 0
8 1
9 1
10 0
11 1
12 0
13 0", header = T)
Our result matches which means solutions are correct. I was slightly confused by
OP's expected data having 17 rows.– MKR
Jul 1 at 9:23
OP's
@MKR Yes so was I; I think OP must've used a slightly different input data for his expected output. @markus posted a a nice (and earlier) answer using
data.table::rleid, that he took down again, perhaps also because of this confusion. A pity.– Maurits Evers
Jul 1 at 9:28
data.table::rleid
You're very welcome @Quý!
– Maurits Evers
Jul 1 at 9:33
@MauritsEvers Undeleted my answer after reading your comment.
– markus
Jul 1 at 10:26
Here is one approach that uses rleid() from data.table to create a grouping variable that we use inside ave(). We then calculate the cumsum per group which will be 0 whenever a == 0.
rleid()
data.table
ave()
cumsum
0
a == 0
library(data.table)
df$new_b <- with(df, ave(a, rleid(a), FUN = cumsum))
df
# ID a b new_b
#1 1 0 0 0
#2 2 0 0 0
#3 3 1 1 1
#4 4 1 2 2
#5 5 1 3 3
#6 6 1 4 4
#7 7 0 0 0
#8 8 1 1 1
#9 9 1 2 2
#10 10 0 0 0
#11 11 1 1 1
#12 12 0 0 0
#13 13 0 0 0
#14 14 1 1 1
#15 15 1 2 2
#16 16 1 3 3
#17 17 1 4 4
Once data.table is loaded you could also do
data.table
setDT(df)[, new_b := cumsum(a), rleid(a)]
data
df <- structure(list(ID = 1:17, a = c(0L, 0L, 1L, 1L, 1L, 1L, 0L, 1L,
1L, 0L, 1L, 0L, 0L, 1L, 1L, 1L, 1L), b = c(0L, 0L, 1L, 2L, 3L,
4L, 0L, 1L, 2L, 0L, 1L, 0L, 0L, 1L, 2L, 3L, 4L)), .Names = c("ID",
"a", "b"), class = "data.frame", row.names = c(NA, -17L))
Yeah!! Thank you very much for help
– Quý
Jul 1 at 9:18