возрастные группы в месячные ведра - PullRequest
3 голосов
/ 04 июля 2019

Я изо всех сил пытаюсь найти решение для следующей проблемы.У меня есть df с id's/ dob's и еще один месяц месяцем, как указано ниже


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)

Я пытаюсь получить вывод, который дает мне количество членов в возрастных группах (below 19\ 19-64\ above 64) для каждого из моих ежемесячных сегментов.Подсчет, очевидно, переключается на год, когда у людей есть дни рождения.

Я получил расчет возраста с помощью чего-то вроде

age.fct <- function(dob, bucketdate) {

  period <- as.period(interval(dob, bucketdate),unit = "year")
  period$year}

Я предполагаю, что общий подход заключается в том, чтобы рассчитать возраст для каждого месяца,назначить в один из 3 age groups и считать по месяцам.Любые предложения?

РЕДАКТИРОВАТЬ 1.

Спасибо за все различные подходы, я просто провел краткий тест на решения, чтобы определить, какой ответ принять.Каким-то образом решение с таблицей данных не сработало в моем наборе тестовых данных, но я проверю, как только у меня будет несколько минут в следующие дни.

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

и эталонный тест


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

Основанный на скорости подход AkselA кажется самым быстрым, но я получаю другой результат для подхода MM по сравнению со всеми другими (как только AkselA изменится на 65 в отрезанной части cut, c(0, 19, 64, Inf)... Я приму ответ на основе скорости, но я посмотрю наразличия в результатах!

Ответы [ 3 ]

3 голосов
/ 04 июля 2019

Не очень сложно, но я присоединился к двум таблицам (сначала расширен monthbucket на df$id), а затем вычислил возраст (как у вас есть весь месяц, я только что подсчитал difftime с первым днем ​​месяца рожденияи startmonth).Затем для каждого месяца (группы) я подсчитывал количество разных возрастных групп и в конце конвертировал длинный формат в широкий для лучшей иллюстрации.

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

Создано в 2019-07-03 с помощью представительный пакет (v0.3.0)

2 голосов
/ 04 июля 2019

Есть некоторые сходства с ответом @ AkselA, поскольку оно зависит от outer(), cut() и table().

set.seed(33)
birth_days <- sample(seq(as.Date('1940/01/01'), as.Date('2010/01/01'), by="day"), 10)
month_bucket <- seq(as.Date("2010-01-01"),as.Date("2011-01-01"),by="months")

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))
  )
)

          (0,19] (19,65] (65,Inf]
  2010-01      2       7        1
  2010-02      2       7        1
  2010-03      2       7        1
  2010-04      2       7        1
  2010-05      2       7        1
  2010-06      2       7        1
  2010-07      2       7        1
  2010-08      2       7        1
  2010-09      2       7        1
  2010-10      2       7        1
  2010-11      2       7        1
  2010-12      2       7        1
  2011-01      2       7        1

Я чувствовал себя странно, имея такое похожее решение, так что вот data.table:

library(data.table)

dcast(
  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')

dplyr и 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)

И похожий подход к базе, которым я не совсем доволен.

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'
)
2 голосов
/ 04 июля 2019

Если я понимаю ваш запрос.

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
# 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...