Создание таблицы переписи на основе нескольких переменных в R

#r #dataframe #date #census #gather

Вопрос:

Я новичок в R и действительно борюсь с тем, что кажется простой проблемой (на которую я не смог найти ответов).

У меня есть относительно большая таблица данных, которая, по сути, включает в себя-людей-где они живут-что они делают-даты въезда-даты выезда. Моя цель состоит в том, чтобы получить текущую таблицу еженедельных переписей, в которой каждая неделя представлена в виде строки и столбца для каждой профессии и города, заполненного численностью населения на тот момент.

 #MRE  library(tidyverse)  library(lubridate)  data lt;- data.frame(  first_names = c("joe", "sally", "bob", "frank", "susy"),  move_in = as.Date(c("2020-01-01", "2021-01-04", "2020-04-01", "2018-12-20", "2019-10-12")),  move_out = as.Date(c("2021-01-01", NA, "2021-10-01", NA, NA)),  city = c("Denver", "Phoenix", "Austin", "Denver", "Seattle"),  occupation = c("doctor", "doctor", "architect", "teacher", "teacher"))  #what I've tried :   cities = unique(data$city)[!is.na(unique(data$city))] occupations = unique(data$occupation)[!is.na(unique(data$occupation))] weeks lt;- (date = seq(from = as.Date("2020-12-27"), to = as.Date(today()), by="1 week"))  census lt;- matrix(data=NA, nrows=44, ncols=12)   for (i in seq(cities)){  for (j in seq(occupations)){  count lt;- data %gt;%   filter(cities == i) %gt;%  filter(occupations == j) %gt;%   sapply(weeks, function(x)  sum(  ((as.Date(data$move_in)) lt;= as.Date(x) amp;  (as.Date(data$move_out)) gt; as.Date(x))|  ((as.Date(data$move_in)) lt;= as.Date(x) amp;  is.na(data$move_out))))    census[j,x] lt;- count }}  

Любая помощь будет очень признательна!

Ответ №1:

Вот возможное решение с использованием некоторых глаголов tidyverse, поскольку вы загрузили этот пакет. Мы перебираем недели, в течение которых вы заинтересованы в использовании map_dfr функции, и за каждую неделю мы собираем подмножество людей, которые там используют ваше логическое утверждение выше. Затем мы можем использовать group_by , чтобы пропустить двойной внешний цикл и count их напрямую. Наконец, мы mutate создали новую колонку на неделю, чтобы сохранить их сразу после того, как они будут связаны вместе. За пределами цикла мы затем pivot_wider получим формат по одному столбцу на занятие и по одной строке в неделю, который вы ищете.

 library(tidyverse)  data lt;- data.frame(  first_names = c("joe", "sally", "bob", "frank", "susy"),  move_in = as.Date(c("2020-01-01", "2021-01-04", "2020-04-01", "2018-12-20", "2019-10-12")),  move_out = as.Date(c("2021-01-01", NA, "2021-10-01", NA, NA)),  city = c("Denver", "Phoenix", "Austin", "Denver", "Seattle"),  occupation = c("doctor", "doctor", "architect", "teacher", "teacher"))  # Avoid needing to load lubridate by using Sys.Date() instead of today() weeks lt;- (date = seq(from = as.Date("2020-12-27"), to = as.Date(Sys.Date()), by="1 week"))  map_dfr(weeks, function(week_i){  data %gt;%  filter(move_inlt;week_i amp; move_out gt; week_i | move_in lt; week_i amp; is.na(move_out)) %gt;%  group_by(city, occupation) %gt;%  count() %gt;%  mutate(week=week_i) }) %gt;%  pivot_wider(values_from = n, names_from = occupation, values_fill = 0)   

который возвращает

 # A tibble: 170 x 5 # Groups: city [4]  city week architect doctor teacher  lt;chrgt; lt;dategt; lt;intgt; lt;intgt; lt;intgt;  1 Austin 2020-12-27 1 0 0  2 Denver 2020-12-27 0 1 1  3 Seattle 2020-12-27 0 0 1  4 Austin 2021-01-03 1 0 0  5 Denver 2021-01-03 0 0 1  6 Seattle 2021-01-03 0 0 1  7 Austin 2021-01-10 1 0 0  8 Denver 2021-01-10 0 0 1  9 Phoenix 2021-01-10 0 1 0 10 Seattle 2021-01-10 0 0 1 # ... with 160 more rows  

Похоже, вы получаете ошибки из-за пары опечаток. Вы используете filter глагол, чтобы запросить cities столбец, но в данных есть только city столбец в образце набора данных. То же самое для occupations vs occupation . Хорошо иметь в виду на будущее, но отличное первое усилие и прекрасный пример!

Комментарии:

1. Это прекрасно работает! Большое вам спасибо за помощь, объяснение и совет.

Ответ №2:

Я использовал данные.таблица. lubridate не требуется, я использовал Sys.Date().
Я также сделал перепись data.table вместо матрицы.
data.table::CJ почти такой же, как expand.grid.
Затем использовал mapply вместо циклов for.
Наконец, перестроен с длинного на широкий, как я думаю, это то, что вы хотели.
Я ушел во всех комбинациях city_occupation — не уверен, что это было намерением.

 library(data.table) library(magrittr)  data lt;- data.frame(  first_names = c("joe", "sally", "bob", "frank", "susy"),  move_in = as.Date(c("2020-01-01", "2021-01-04", "2020-04-01", "2018-12-20", "2019-10-12")),  move_out = as.Date(c("2021-01-01", NA, "2021-10-01", NA, NA)),  city = c("Denver", "Phoenix", "Austin", "Denver", "Seattle"),  occupation = c("doctor", "doctor", "architect", "teacher", "teacher")) cities lt;- unique(data$city)[!is.na(unique(data$city))] occupations lt;- unique(data$occupation)[!is.na(unique(data$occupation))] weeks lt;- (date = seq(from = as.Date("2020-12-27"), to = Sys.Date(), by="1 week"))  data %gt;% setDT() census lt;- CJ(week = weeks, city = cities, occupation = occupations) %gt;%   .[, count := mapply(function(wk, cty, occ) {  data[city == cty amp; occupation == occ,   sum(move_in lt;= wk amp; (move_out gt; wk | is.na(move_out)))]  }, week, city, occupation)]  census %lt;gt;% dcast(week ~ city   occupation, value.var = 'count')  

Дает:

 census  week Austin_architect Austin_doctor Austin_teacher Denver_architect  1: 2020-12-27 1 0 0 0  2: 2021-01-03 1 0 0 0  3: 2021-01-10 1 0 0 0  4: 2021-01-17 1 0 0 0  5: 2021-01-24 1 0 0 0  6: 2021-01-31 1 0 0 0  7: 2021-02-07 1 0 0 0  8: 2021-02-14 1 0 0 0  9: 2021-02-21 1 0 0 0 10: 2021-02-28 1 0 0 0 11: 2021-03-07 1 0 0 0 12: 2021-03-14 1 0 0 0 13: 2021-03-21 1 0 0 0 14: 2021-03-28 1 0 0 0 15: 2021-04-04 1 0 0 0 16: 2021-04-11 1 0 0 0 17: 2021-04-18 1 0 0 0 18: 2021-04-25 1 0 0 0 19: 2021-05-02 1 0 0 0 20: 2021-05-09 1 0 0 0 21: 2021-05-16 1 0 0 0 22: 2021-05-23 1 0 0 0 23: 2021-05-30 1 0 0 0 24: 2021-06-06 1 0 0 0 25: 2021-06-13 1 0 0 0 26: 2021-06-20 1 0 0 0 27: 2021-06-27 1 0 0 0 28: 2021-07-04 1 0 0 0 29: 2021-07-11 1 0 0 0 30: 2021-07-18 1 0 0 0 31: 2021-07-25 1 0 0 0 32: 2021-08-01 1 0 0 0 33: 2021-08-08 1 0 0 0 34: 2021-08-15 1 0 0 0 35: 2021-08-22 1 0 0 0 36: 2021-08-29 1 0 0 0 37: 2021-09-05 1 0 0 0 38: 2021-09-12 1 0 0 0 39: 2021-09-19 1 0 0 0 40: 2021-09-26 1 0 0 0 41: 2021-10-03 0 0 0 0 42: 2021-10-10 0 0 0 0 43: 2021-10-17 0 0 0 0 44: 2021-10-24 0 0 0 0  week Austin_architect Austin_doctor Austin_teacher Denver_architect  Denver_doctor Denver_teacher Phoenix_architect Phoenix_doctor  1: 1 1 0 0  2: 0 1 0 0  3: 0 1 0 1  4: 0 1 0 1  5: 0 1 0 1  6: 0 1 0 1  7: 0 1 0 1  8: 0 1 0 1  9: 0 1 0 1 10: 0 1 0 1 11: 0 1 0 1 12: 0 1 0 1 13: 0 1 0 1 14: 0 1 0 1 15: 0 1 0 1 16: 0 1 0 1 17: 0 1 0 1 18: 0 1 0 1 19: 0 1 0 1 20: 0 1 0 1 21: 0 1 0 1 22: 0 1 0 1 23: 0 1 0 1 24: 0 1 0 1 25: 0 1 0 1 26: 0 1 0 1 27: 0 1 0 1 28: 0 1 0 1 29: 0 1 0 1 30: 0 1 0 1 31: 0 1 0 1 32: 0 1 0 1 33: 0 1 0 1 34: 0 1 0 1 35: 0 1 0 1 36: 0 1 0 1 37: 0 1 0 1 38: 0 1 0 1 39: 0 1 0 1 40: 0 1 0 1 41: 0 1 0 1 42: 0 1 0 1 43: 0 1 0 1 44: 0 1 0 1  Denver_doctor Denver_teacher Phoenix_architect Phoenix_doctor  Phoenix_teacher Seattle_architect Seattle_doctor Seattle_teacher  1: 0 0 0 1  2: 0 0 0 1  3: 0 0 0 1  4: 0 0 0 1  5: 0 0 0 1  6: 0 0 0 1  7: 0 0 0 1  8: 0 0 0 1  9: 0 0 0 1 10: 0 0 0 1 11: 0 0 0 1 12: 0 0 0 1 13: 0 0 0 1 14: 0 0 0 1 15: 0 0 0 1 16: 0 0 0 1 17: 0 0 0 1 18: 0 0 0 1 19: 0 0 0 1 20: 0 0 0 1 21: 0 0 0 1 22: 0 0 0 1 23: 0 0 0 1 24: 0 0 0 1 25: 0 0 0 1 26: 0 0 0 1 27: 0 0 0 1 28: 0 0 0 1 29: 0 0 0 1 30: 0 0 0 1 31: 0 0 0 1 32: 0 0 0 1 33: 0 0 0 1 34: 0 0 0 1 35: 0 0 0 1 36: 0 0 0 1 37: 0 0 0 1 38: 0 0 0 1 39: 0 0 0 1 40: 0 0 0 1 41: 0 0 0 1 42: 0 0 0 1 43: 0 0 0 1 44: 0 0 0 1  Phoenix_teacher Seattle_architect Seattle_doctor Seattle_teacher  

Комментарии:

1. Спасибо тебе, Брайан, за помощь! Это решение полностью работает и помогло мне узнать кучу полезных вещей, которые я буду иметь в виду (об использовании mapply вместо циклов for и функции CJ). В конечном счете, когда я применил его к своему реальному набору данных, это заставило меня понять, что структурирование его так, как я изначально предполагал, привело к слишком большому количеству комбинаций «профессия-город», и он стал громоздким. Решение @Dubukay в конечном итоге сохранило «город» в виде столбца и привело к созданию более удобной таблицы для моих целей.