I have the following:
df <- data.frame(ID = c(1,1,1,1,1,1,1,1,1,1,2,2,2),
Obs = c(0,1, 1, 0, 1,0,0, 1, 1, 1, 0,0,1))
And I want this:
df <- data.frame(ID = c(1,1,1,1,1,1,1,1,1,1,2,2,2),
Obs = c(0,1, 1, 0, 1,0,0, 1, 1, 1, 0,0,1),
Cluster = c(0,1,1,1,2,2,2,3,3,3,0,0,1))
How can I obtain 'Cluster' column in which I have to sequence the number of 1 until the first 0 appears, with dplyr?
Consecutive 0's have to maintain the value until a new one appears.
EDIT
How can I do that, with many columns?
Suppose that I have 99 obs columns, and I would like to create 99 clusters, one for each column. Like this:
df <- data.frame(ID = c(1,1,1,1,1,1,1,1,1,1,2,2,2),
Obs1 = c(0,1, 1, 0, 1,0,0, 1, 1, 1, 0,0,1),
Obs2 = c(0,0, 0, 1, 1,1,0, 1, 0, 1, 0,0,1),
ClusterObs1 = c(0,1,1,1,2,2,2,3,3,3,0,0,1),
ClusterObs2 = c(0,0,0,1,1,1,1,2,2,3,0,0,1))
Here's an option using rle
:
df %>%
group_by(ID) %>%
mutate(clust = with(rle(Obs), rep(cumsum(values == 1), lengths)))
# # A tibble: 13 x 4
# # Groups: ID [2]
# ID Obs Cluster clust
# <dbl> <dbl> <dbl> <int>
# 1 1. 0. 0. 0
# 2 1. 1. 1. 1
# 3 1. 1. 1. 1
# 4 1. 0. 1. 1
# 5 1. 1. 2. 2
# 6 1. 0. 2. 2
# 7 1. 0. 2. 2
# 8 1. 1. 3. 3
# 9 1. 1. 3. 3
# 10 1. 1. 3. 3
# 11 2. 0. 0. 0
# 12 2. 0. 0. 0
# 13 2. 1. 1. 1
Here's the main part of it:
rle(df$Obs)
#Run Length Encoding
# lengths: int [1:8] 1 2 1 1 2 3 2 1
# values : num [1:8] 0 1 0 1 0 1 0 1
This tells you how long each stretch of 1s or 0s was in the Obs-column (I ignore the ID-grouping for now).
What we need now, is to count cumulatively how many times there were strectches of 1s and to do that we simply cumsum where the values are 1:
with(rle(df$Obs), cumsum(values == 1))
#[1] 0 1 1 2 2 3 3 4
So far so good, now we need to repeat those values as many times as those stretches were long, hence we use rep
and the lengths
information from rle:
with(rle(df$Obs), rep(cumsum(values == 1), lengths))
# [1] 0 1 1 1 2 2 2 3 3 3 3 3 4
Finally, we do this by group of ID.
If you need to create several cluster-columns for different obs-columns, you can easily do it as follows:
df %>%
group_by(ID) %>%
mutate_at(vars(starts_with("Obs")),
funs(cluster= with(rle(.), rep(cumsum(values == 1), lengths))))
# # A tibble: 13 x 7
# # Groups: ID [2]
# ID Obs1 Obs2 ClusterObs1 ClusterObs2 Obs1_cluster Obs2_cluster
# <dbl> <dbl> <dbl> <dbl> <dbl> <int> <int>
# 1 1. 0. 0. 0. 0. 0 0
# 2 1. 1. 0. 1. 0. 1 0
# 3 1. 1. 0. 1. 0. 1 0
# 4 1. 0. 1. 1. 1. 1 1
# 5 1. 1. 1. 2. 1. 2 1
# 6 1. 0. 1. 2. 1. 2 1
# 7 1. 0. 0. 2. 1. 2 1
# 8 1. 1. 1. 3. 2. 3 2
# 9 1. 1. 0. 3. 2. 3 2
# 10 1. 1. 1. 3. 3. 3 3
# 11 2. 0. 0. 0. 0. 0 0
# 12 2. 0. 0. 0. 0. 0 0
# 13 2. 1. 1. 1. 1. 1 1
where df is:
df <- data.frame(ID = c(1,1,1,1,1,1,1,1,1,1,2,2,2), Obs1 = c(0,1, 1, 0, 1,0,0, 1, 1, 1, 0,0,1), Obs2 = c(0,0, 0, 1, 1,1,0, 1, 0, 1, 0,0,1), ClusterObs1 = c(0,1,1,1,2,2,2,3,3,3,0,0,1), ClusterObs2 = c(0,0,0,1,1,1,1,2,2,3,0,0,1))
This is such a fun problem so here comes a data.table solution:
# Packages used
library(data.table)
library(magrittr)
# Setup
setDT(df)
df[, Obs := as.integer(Obs)]
# Calculations
df[, Cluster := cumsum(!Obs), by = ID] %>%
.[, Cluster := Cluster - rowid(Obs) * !Obs, by = rleid(Obs)] %>%
.[, Cluster := frank(Cluster, ties.method = "dense") - 1L, by = ID]
df
ID Obs Cluster
1: 1 0 0
2: 1 1 1
3: 1 1 1
4: 1 0 1
5: 1 1 2
6: 1 0 2
7: 1 0 2
8: 1 1 3
9: 1 1 3
10: 1 1 3
11: 2 0 0
12: 2 0 0
13: 2 1 1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With