Я выполняю моделирование дискретных событий в R. «Сердце» моего алгоритма выполняет следующее (псевдокод):
1) Итерация по events
a) Изменить event[i]
в зависимости от resources
b) Изменить resources
в зависимости от результата шага a)
В следующем воспроизводимом примере рассматриваются основные аспекты:
Генерация некоторых данных:
set.seed(4)
n <- 3
nr_resources <- 2
events <- data.frame(
t = as.integer(trunc(cumsum(rexp(n)))),
resource = NA,
worktime = as.integer(trunc(runif(n)*10))
)
resources <- data.frame(
id = 1:nr_resources,
t_free = 0L
)
events
resources
# > events
# t resource worktime
# 0 NA 2
# 4 NA 8
# 5 NA 2
# > resources
# id t_free
# 1 0
# 2 0
Теперь мы можем смоделировать диспетчеризацию ресурсов:
for (i in 1:n) {
events$resource[i] <- resources$id[resources$t_free <= events$t[i]][1]
resources$t_free[events$resource[i]] <- events$t[i] + events$worktime[i]
}
events
resources
# > events
# t resource worktime
# 0 1 2
# 4 1 8
# 5 2 2
# > resources
# id t_free
# 1 12
# 2 7
Этот подход работает хорошо, но есть ряд недостатков, которые я хотел быустранить.Поскольку events
и resources
разделены на два набора данных, существует довольно много подмножеств (поиск и замена), происходящих в двух наборах данных.Это не совсем читабельно.И в реальном приложении это даже становится узким местом производительности.(... конечно, реальный пример немного сложнее ..)
Поэтому я спрашиваю себя, есть ли лучшие способы выполнить эту задачу в R.
Я думал о замене цикла for общей функцией высшего порядка, но не дал никаких результатов.
- Типичный подход R
lapply
не работает, потому что lapply
не построен для этого итеративных изменений во входных данных.(Насколько я вижу ..) - Моя задача немного похожа на шаблон
Reduce
.Поскольку Reduce(sum, 1:3, accumulate = TRUE)
использует промежуточные результаты, а также сохраняет их, я подумал, что могу использовать функцию Reduce
, но не достиг никаких результатов.
Я также думал о реструктуризации своих данных, но безуспешно досейчас.
Что я пытался подробно
На алгоритмической стороне:
Неудачный подход с lapply
:
l <- list(events = events, resources = resources)
l <- lapply(l, function(x) {
l$events$resource <- l$resources$id[l$resources$t_free <= l$events$t][1]
l$resources$t_free[l$events$resource] <- l$events$t + l$events$worktime
return(l)
})
l$events
l$resources
В результате получается:
# $events
# t resource worktime
# 1 0 1 2
# 2 4 1 8
# 3 5 1 2
#
# $resources
# id t_free
# 1 1 7
# 2 2 0
Промежуточные изменения ресурсов теряются и, следовательно, всегда резервируется ресурс 1.
Неудачный подход с Reduce
:
l <- list(events = events, resources = resources)
l <- Reduce(function(l) {
l$events$resource <- l$resources$id[l$resources$t_free <= l$events$t][1]
l$resources$t_free[l$events$resource] <- l$events$t + l$events$worktime
return(l)}, l, accumulate = TRUE)
Сбой с
Ошибка в f (init, x [[i]]): неиспользованный аргумент (x [[i]])
На стороне data :
Другой подход, который я могу придумать, состоит в том, чтобы изменить данные набыть представленным в одном наборе данных.Например, путем умножения событий на количество ресурсов.Я попробовал следующее:
data <- merge(events, resources)
data <- data[order(data$t), ]
data
# t resource worktime id t_free
# 0 NA 2 1 0
# 0 NA 2 2 0
# 4 NA 8 1 0
# 4 NA 8 2 0
# 5 NA 2 1 0
# 5 NA 2 2 0
for (i in seq_along(data)) {
if ( is.na(data$resource[i])) {
data$resource[data$t == data$t[i]] <- data$id[data$t_free <= data$t[i]][1]
data$t_free[data$id == data$resource[i]] <- data$t[i] + data$worktime[i]
}
}
data
# t resource worktime id t_free
# 0 1 2 1 12
# 0 1 2 2 7
# 4 1 8 1 12
# 4 1 8 2 7
# 5 2 2 1 12
# 5 2 2 2 7
events <- unique(data[,1:3])
events
# t resource worktime
# 0 1 2
# 4 1 8
# 5 2 2
resources <- unique(data[,4:5])
resources
# id t_free
# 1 12
# 2 7
Это также работает, но я не уверен, если это приведет к повышению производительности, читаемости и изменчивости при масштабировании ..
Так что мойвопрос:
Существуют ли альтернативы на стороне с алгоритмической или на стороне data , которые улучшают мои фактические решения?