J'ai du mal à trouver une solution au problème suivant. J'ai un df avec id / dob's
et un autre monthbucket df comme suit
microbenchmark::microbenchmark( MM= monthbucket %>% group_by_all %>% expand(id=df$id) %>% left_join(.,{df %>% mutate(birth_month =cut(dob, "month"))},by="id") %>% mutate(age=time_length(difftime(startmonth, birth_month),"years")) %>% mutate(age_cat=case_when(age<19 ~ "<19", age>64 ~ ">64",TRUE ~ "19-64")) %>% group_by(month) %>% count(age_cat) %>% gather(variable, count, n) %>% unite(variable, age_cat) %>% spread(variable, count) , AkselA = {ages <- as.data.frame(t(unclass(outer(monthbucket$startmonth, df$dob, "-")/365.25))) ages <- do.call(data.frame, lapply(ages, cut, c(0, 19, 64, Inf), c("0-19", "19-64", "64+"))) ages <- sapply(ages, table) colnames(ages) <- monthbucket$month }, Cole1 ={t(table(apply(X = outer(month_bucket, birth_days, `-`) / 365.25, MARGIN = 2, FUN = cut, c(0,19,65, Inf)), rep(format(month_bucket,'%Y-%m'), length(birth_days)))) }, # cole2={ cast(CJ(month_bucket, birth_days)[, .N, by = .(month_bucket , cut(as.numeric(month_bucket - birth_days)/365.25, c(0,19,65,Inf)))], month_bucket ~ cut, value.var = 'N') # }, # Cole3={crossing(month_bucket, birth_days)%>%count(month_bucket, age_range = cut(as.numeric(month_bucket - birth_days) / 365.25, c(0,19,65,Inf)))%>%spread(age_range, n) }, Cole4={all_combos <- expand.grid(month_bucket = month_bucket, birth_days = birth_days) all_combos$age <- as.numeric(all_combos$month_bucket - all_combos$birth_days) / 365.25 all_combos$cut_r <- cut(all_combos$age, c(0,19,65,Inf)) reshape(data = aggregate( all_combos$month_bucket, by = list(bucket = all_combos$month_bucket,age_group = all_combos$cut_r), FUN = length), timevar = 'age_group' , idvar = 'bucket', direction = 'wide' ) }, times = 1L) Unit: milliseconds expr min lq mean median uq max neval MM 4249.02810 4249.02810 4249.02810 4249.02810 4249.02810 4249.02810 1 AkselA 17.12697 17.12697 17.12697 17.12697 17.12697 17.12697 1 Cole1 3237.94534 3237.94534 3237.94534 3237.94534 3237.94534 3237.94534 1 Cole3 23.63945 23.63945 23.63945 23.63945 23.63945 23.63945 1 Cole4 877.92782 877.92782 877.92782 877.92782 877.92782 877.92782 1
Je veux obtenir une sortie qui me donne le nombre de membres dans les groupes d'âge ( 64)
pour chacun de mes buckets mensuels. Le décompte change évidemment selon l'année lorsque les gens ont des anniversaires.
J'ai obtenu le calcul de l'âge avec quelque chose comme:
set.seed(33) df <- data.frame(dob = sample(seq(as.Date('1940/01/01'), as.Date('2010/01/01'), by="day"), 10000), id = seq(1:10000) ) monthbucket <- data.frame(month = format(seq(as.Date("2010-01-01"),as.Date("2011-01-01"),by="months"),'%Y-%m'), startmonth = seq(as.Date("2010-01-01"),as.Date("2011-01-01"),by="months"), endmonth = seq(as.Date("2010-02-01"),as.Date("2011-02-01"),by="months")-1) birth_days <- df$dob month_bucket <- monthbucket$startmonth
Je suppose que l'approche générale serait pour calculer l'âge de chaque moisbucket, attribuez-le à l'une des 3 tranches d'âge
et comptez-le par mois. Des suggestions?
MODIFIER 1.
Merci pour toutes les différentes approches, je viens d'exécuter un bref benchmark sur les solutions pour déterminer quelle réponse accepter. D'une manière ou d'une autre, la solution de table de données n'a pas fonctionné sur mon ensemble de données de test, mais je vérifierai dès que j'aurai quelques minutes dans les prochains jours.
age.fct <- function(dob, bucketdate) { period <- as.period(interval(dob, bucketdate),unit = "year") period$year}
et le benchmark
set.seed(33) df <- data.frame(dob = sample(seq(as.Date('1940/01/01'), as.Date('2010/01/01'), by="day"), 10), id = seq(1:10) ) monthbucket <- data.frame(month = format(seq(as.Date("2010-01-01"),as.Date("2011-01-01"),by="months"),'%Y-%m'), startmonth = seq(as.Date("2010-01-01"),as.Date("2011-01-01"),by="months"), endmonth = seq(as.Date("2010-02-01"),as.Date("2011-02-01"),by="months")-1)
Basé sur la vitesse, l'approche d'AkselA semble être la plus rapide mais j'obtiens un résultat différent pour l'approche de MM par rapport à tous les autres (une fois que AkselA passe à 65 dans la partie coupée cut, c (0, 19, 64, Inf) .. . J'accepterai la réponse en fonction de la vitesse mais je regarderai les différences dans les résultats!
3 Réponses :
Pas très sophistiqué mais j'ai rejoint les deux tables (d'abord développé monthbucket
sur df $ id
) puis j'ai calculé l'âge (comme vous avez tout le mois, je viens de calculé difftime
avec le premier jour du mois de naissance et startmonth
). Ensuite, pour chaque mois (compartiment), j'ai compté le nombre de groupes d'âge différents et à la fin j'ai converti le format long en large pour une meilleure illustration.
library(lubridate) library(tidyverse) monthbucket %>% group_by_all %>% expand(id=df$id) %>% left_join(.,{df %>% mutate(birth_month =cut(dob, "month"))}, by="id") %>% mutate(age=time_length(difftime(startmonth, birth_month),"years")) %>% mutate(age_cat=case_when(age<19 ~ "<19", age>64 ~ ">64", TRUE ~ "19-64")) %>% group_by(month) %>% count(age_cat) %>% gather(variable, count, n) %>% unite(variable, age_cat) %>% spread(variable, count) #> # A tibble: 13 x 4 #> # Groups: month [13] #> month `<19` `>64` `19-64` #> <fct> <int> <int> <int> #> 1 2010-01 3 2 5 #> 2 2010-02 3 2 5 #> 3 2010-03 3 2 5 #> 4 2010-04 3 2 5 #> 5 2010-05 3 2 5 #> 6 2010-06 3 2 5 #> 7 2010-07 3 2 5 #> 8 2010-08 3 2 5 #> 9 2010-09 3 2 5 #> 10 2010-10 3 2 5 #> 11 2010-11 3 2 5 #> 12 2010-12 3 2 5 #> 13 2011-01 3 2 5
Créé le 03/07/2019 par le package reprex (v0.3.0)
En supposant que je comprends votre demande.
ages <- as.data.frame(t(unclass(outer(monthbucket$startmonth, df$dob, "-")/365.25))) ages <- do.call(data.frame, lapply(ages, cut, c(0, 19, 64, Inf), c("0-19", "19-64", "64+"))) ages <- sapply(ages, table) colnames(ages) <- monthbucket$month ages # 2010-01 2010-02 2010-03 2010-04 2010-05 2010-06 2010-07 2010-08 2010-09 2010-10 2010-11 2010-12 2011-01 # 0-19 2 2 2 2 2 2 2 2 2 2 2 2 2 # 19-64 7 7 7 7 7 7 7 7 7 7 7 7 7 # 64+ 1 1 1 1 1 1 1 1 1 1 1 1 1 #
Il y a quelques similitudes avec la réponse de @ AkselA car elle dépend de external ()
, cut ()
et table ()
. XXX
Je me sentais bizarre d'avoir une solution similaire alors voici data.table
:
all_combos <- expand.grid(month_bucket = month_bucket, birth_days = birth_days) all_combos$age <- as.numeric(all_combos$month_bucket - all_combos$birth_days) / 365.25 all_combos$cut_r <- cut(all_combos$age, c(0,19,65,Inf)) reshape( data = aggregate( all_combos$month_bucket , by = list(bucket = all_combos$month_bucket ,age_group = all_combos$cut_r) , FUN = length) , timevar = 'age_group' , idvar = 'bucket' , direction = 'wide' )
dplyr
et tidyr
:
library(dplyr) library(tidyr) crossing(month_bucket, birth_days)%>% count(month_bucket , age_range = cut(as.numeric(month_bucket - birth_days) / 365.25, c(0,19,65,Inf)) )%>% spread(age_range, n)
Et une approche similaire de base dont je ne suis pas complètement satisfait.
XXX
Pour l'option
data.table
, c'estdcast (...)
au lieu decast (...)
. De plus, j'obtiens des repères extrêmement différents des vôtres, les médianes étant MM 59, Ansel 3.3, Cole_outer 2.8, Cole_dt 6.5, Cole_dplyr 5.9, Cole_reshape 5.3 en millisecondes. C'est avec 100 fois en microbenchmark.