Примечание. Аналогичный вопрос, который я задал для SQL - Как использовать оконную функцию, чтобы определять, когда выполнять различные задачи в Hive или Postgres?
Данные
У меня есть некоторые данные, показывающие день начала и день окончания для различных предварительно приоритетных задач на человека:
input_df <- data.frame(person = c(rep("Kate", 2), rep("Adam", 2), rep("Eve", 2), rep("Jason", 5)),
task_key = c(c("A","B"), c("A","B"), c("A","B"), c("A","B","C","D","E")),
start_day = c(c(1L,1L), c(1L,2L), c(2L,1L), c(1L,4L,3L,5L,4L)),
end_day = 5L)
person task_key start_day end_day 1 Kate A 1 5 2 Kate B 1 5 3 Adam A 1 5 4 Adam B 2 5 5 Eve A 2 5 6 Eve B 1 5 7 Jason A 1 5 8 Jason B 4 5 9 Jason C 3 5 10 Jason D 5 5 11 Jason E 4 5
ПРИМЕЧАНИЕ. Клавиша задачи упорядочена так, что более высокие буквы имеют более высокий приоритет.
Вопрос
Мне нужно решить, над какой задачей каждый человек должен работать каждый день, при условии, что:
- Задачи с более высокими буквами имеют приоритет над задачами с более низкими буквами.
- Если задача с более высокими буквами перекрывает любую часть задачи с более низкими буквами, то для задачи с более низкими буквами устанавливается значение NA (чтобы обозначить, что человек не должен работать над ней когда-либо).
Упрощение
В реальных данных end_day всегда равен 5 в исходной таблице, т.е. изменяется только start_day, но end_day является постоянным. Это означает, что желаемый результат будет иметь то же количество строк, что и моя исходная таблица :)
Вывод
Это именно тот результат, который мне нужен (Джейсон более репрезентативен для данных, которые у меня есть, которые могут включать более 100 задач, охватывающих период 90 дней):
output_df <- data.frame(person = c(rep("Kate", 2), rep("Adam", 2), rep("Eve", 2), rep("Jason", 5)),
task_key = c(c("A","B"), c("A","B"), c("A","B"), c("A","B","C","D","E")),
start_day = c(c(1L,1L), c(1L,2L), c(2L,1L), c(1L,4L,3L,5L,4L)),
end_day = 5L,
valid_from = c( c(NA,1L), c(1L,2L), c(NA,1L), c(1L,NA,3L,NA,4L) ),
valid_to = c( c(NA,5L), c(2L,5L), c(NA,5L), c(3L,NA,4L,NA,5L) ))
person task_key start_day end_day valid_from valid_to 1 Kate A 1 5 NA NA 2 Kate B 1 5 1 5 3 Adam A 1 5 1 2 4 Adam B 2 5 2 5 5 Eve A 2 5 NA NA 6 Eve B 1 5 1 5 7 Jason A 1 5 1 3 8 Jason B 4 5 NA NA 9 Jason C 3 5 3 4 10 Jason D 5 5 NA NA 11 Jason E 4 5 4 5
Первые мысли
Работает, но мне нужно решение, которое работает с использованием функций пакета dbplyr и что-то, что обычно лучше, чем это:
tmp <- input_df %>% filter(person == "Jason")
num_rows <- nrow(tmp)
tmp$valid_from <- NA
tmp$valid_to <- NA
for(i in 1:num_rows) {
# Curent value
current_value <- tmp$start_day[i]
# Values to test against
vec <- lead(tmp$start, i)
# test
test <- current_value >= vec
# result
if(any(test, na.rm = TRUE) & i!=num_rows) {
tmp$valid_from[i] <- NA
tmp$valid_to[i] <- NA
} else if(i!=num_rows) {
tmp$valid_from[i] <- current_value
tmp$valid_to[i] <- min(vec, na.rm = TRUE)
} else {
tmp$valid_from[i] <- current_value
tmp$valid_to[i] <- max(tmp$end_day, na.rm = TRUE)
}
}
tmp
person task_number start_day end_day valid_from valid_to 1 Jason A 1 5 1 3 2 Jason B 4 5 NA NA 3 Jason C 3 5 3 4 4 Jason D 5 5 NA NA 5 Jason E 4 5 4 5
Дополнительный вопрос
В конце концов мне нужно будет сделать это в SQL, но это кажется слишком сложным. Я слышал, что пакет dbply может помочь мне здесь, потому что, если я смогу решить эту проблему с помощью функций dplyr, он каким-то образом преобразует это в действительный запрос SQL?