#r #dplyr
#r #dplyr
Вопрос:
Допустим, у меня есть data.frame, который выглядит следующим образом:
user_df = read.table(text = "person_id job_number job_type start_date end_date
1 1 B 2012-11-01 2014-01-01
1 2 A 2016-02-01 2016-10-01
1 3 A 2016-12-01 2020-01-01
1 4 B 2020-01-01 2021-01-01
2 1 A 2011-03-01 2012-08-01
2 2 B 2013-01-01 2020-01-01
2 3 A 2020-01-01 2021-01-01
2 4 B 2021-01-01 2021-01-17
3 1 A 2005-03-01 2011-03-01
3 2 B 2012-01-01 2014-01-01", header = T)
У каждого person_id
из них есть дата начала и окончания для данного задания. Я хотел бы вставить строки между пустым пространством между заданиями и создать дополнительный вызываемый столбец unemployed
, для которого установлено значение 1 для этих столбцов.
Результирующий data.frame для первых нескольких строк будет выглядеть следующим образом:
user_df = read.table(text = "person_id job_number job_type start_date end_date unemployed
1 1 B 2012-11-01 2014-01-01 0
1 1 B 2014-01-01 2016-02-01 1
1 2 A 2016-02-01 2016-10-01 0
1 2 A 2016-10-01 2016-12-01 1
1 3 A 2016-12-01 2020-01-01 0
1 4 B 2020-01-01 2021-01-01 0
2 1 A 2011-03-01 2012-08-01 0
2 1 A 2012-08-01 2013-01-01 1
2 2 B 2013-01-01 2020-01-01 0", header = T)
Поэтому я, по сути, вставляю новую строку с датой окончания предыдущих строк в качестве даты начала и датой начала следующей строки в качестве даты окончания.
Даже не знаю, с чего начать с этого. Я смог вычислить общее количество времени, проведенного без работы, просто суммируя дни, охватывающие самую раннюю начальную дату и последнюю конечную дату, и вычитая это из общего времени, фактически накопленного каждой строкой. Но я не уверен, как я буду программно вставлять строки в цепочку dplyr, чтобы заполнить свободное время.
Ответ №1:
library(dplyr)
user_df %>%
arrange(start_date) %>%
group_by(person_id) %>%
mutate(nextstart = lead(start_date)) %>%
filter(end_date < nextstart) %>%
mutate(start_date = end_date, end_date = nextstart, unemployed = 1L) %>%
select(-nextstart) %>%
bind_rows(mutate(user_df, unemployed = 0L)) %>%
arrange(person_id, start_date) %>%
ungroup()
# # A tibble: 14 x 6
# person_id job_number job_type start_date end_date unemployed
# <int> <int> <chr> <chr> <chr> <int>
# 1 1 1 B 2012-11-01 2014-01-01 0
# 2 1 1 B 2014-01-01 2016-02-01 1
# 3 1 2 A 2016-02-01 2016-10-01 0
# 4 1 2 A 2016-10-01 2016-12-01 1
# 5 1 3 A 2016-12-01 2020-01-01 0
# 6 1 4 B 2020-01-01 2021-01-01 0
# 7 2 1 A 2011-03-01 2012-08-01 0
# 8 2 1 A 2012-08-01 2013-01-01 1
# 9 2 2 B 2013-01-01 2020-01-01 0
# 10 2 3 A 2020-01-01 2021-01-01 0
# 11 2 4 B 2021-01-01 2021-01-17 0
# 12 3 1 A 2005-03-01 2011-03-01 0
# 13 3 1 A 2011-03-01 2012-01-01 1
# 14 3 2 B 2012-01-01 2014-01-01 0
Технически это сравнение по алфавитному сортировке дат; в этом случае его эффект тот же (формат подходит для этого), хотя он будет немного менее эффективным (целочисленная / числовая сортировка быстрее, чем алфавитная сортировка).
Это работает, сначала создавая, а затем фиксируя только незанятые периоды времени,
user_df %>%
arrange(start_date) %>%
group_by(person_id) %>%
mutate(nextstart = lead(start_date)) %>%
filter(end_date < nextstart)
# # A tibble: 4 x 6
# # Groups: person_id [3]
# person_id job_number job_type start_date end_date nextstart
# <int> <int> <chr> <chr> <chr> <chr>
# 1 3 1 A 2005-03-01 2011-03-01 2012-01-01
# 2 2 1 A 2011-03-01 2012-08-01 2013-01-01
# 3 1 1 B 2012-11-01 2014-01-01 2016-02-01
# 4 1 2 A 2016-02-01 2016-10-01 2016-12-01
затем переместите переменные, затем добавьте unemployed
и, наконец, верните его в исходный набор данных. В этом случае я добавил unemployed
к исходному mid- bind_rows
; где это сделать, в основном предпочтение.
Комментарии:
1. Вау, это фантастически умно! большое спасибо. Многому научился, прочитав этот код.
2. надеюсь, быстрый последующий вопрос. Я заметил, что в моих данных у некоторых участников есть даты в одной строке, которые находятся между начальной и конечной датами другой строки. Если, например, они выполняют две работы одновременно, одну между 2005-03-01 — 2011-03-01, а другую между 2006-03-01 и 2007-03-01. Есть ли у вас какие-либо предположения относительно того, как обращаться с этими случаями?
3. Это вопрос с подвохом: для меня это совершенно достоверные данные. Похоже, что это не соответствует вашей последующей обработке. Например, вы предполагаете, что никакие две строки (для одного человека) не могут перекрываться вообще ? Если это так, как вы хотите обрабатывать конфликты? Самая короткая работа? Самая длинная работа? Объединение двух заданий (минимальное начало и максимальное завершение)? Это полностью зависит от контекста, и вам нужно полностью определить. И нет, нет инструмента, который сделает все это за вас, поскольку это ваши ограничения. (Но да, это можно сделать с помощью четко определенных правил.)
4. Хм, извините — я должен был быть более конкретным. Я собираюсь создать новый вопрос, и я свяжу его здесь, если вам интересно ответить.