Добавить индикатор выполнения для функции загрузки в R

Я пытаюсь добавить индикатор выполнения в функцию начальной загрузки в R. Я попытался сделать пример функции максимально простым (поэтому в этом примере я использую среднее значение).

library(boot)
v1 <- rnorm(1000)
rep_count = 1

m.boot <- function(data, indices) {
  d <- data[indices]
  setWinProgressBar(pb, rep_count)
  rep_count <- rep_count + 1
  Sys.sleep(0.01)
  mean(d, na.rm = T) 
  }

tot_rep <- 200
pb <- winProgressBar(title = "Bootstrap in progress", label = "",
                     min = 0, max = tot_rep, initial = 0, width = 300)
b <- boot(v1, m.boot, R = tot_rep)
close(pb)

Начальная загрузка работает правильно, но проблема в том, что значение rep_count не увеличивается в цикле, а индикатор выполнения остается замороженным во время процесса.

Если я проверю значение rep_count после завершения начальной загрузки, оно по-прежнему равно 1.

Что я делаю не так? может функция загрузки не просто вставляет в цикл функцию m.boot и поэтому переменные в ней не увеличиваются?

Спасибо.


person fzara    schedule 07.06.2016    source источник
comment
пакет pbapply — это простой способ показать индикатор выполнения любой задачи применения функции с помощью apply семья. github.com/psolymos/pbapply . Если бы вы могли использовать m.boot внутри какой-либо формы apply, это было бы очень просто.   -  person rafa.pereira    schedule 07.06.2016


Ответы (6)


Пакет pbapply был разработан для работы с векторизованными функциями. В контексте этого вопроса есть два способа добиться этого: (1) написать оболочку, как было предложено, которая не будет создавать тот же объект класса 'boot'; (2) альтернативно, строка lapply(seq_len(RR), fn) может быть записана как pblapply(seq_len(RR), fn). Вариант 2 может произойти либо путем локального копирования/обновления функции boot, как показано в примере ниже, либо путем обращения к сопровождающему пакета, Брайану Рипли, с просьбой добавить индикатор выполнения напрямую или через pbapply в качестве зависимости. .

Мое решение (изменения указаны комментариями):

library(boot)
library(pbapply)
boot2 <- function (data, statistic, R, sim = "ordinary", stype = c("i", 
    "f", "w"), strata = rep(1, n), L = NULL, m = 0, weights = NULL, 
    ran.gen = function(d, p) d, mle = NULL, simple = FALSE, ..., 
    parallel = c("no", "multicore", "snow"), ncpus = getOption("boot.ncpus", 
        1L), cl = NULL) 
{
call <- match.call()
stype <- match.arg(stype)
if (missing(parallel)) 
    parallel <- getOption("boot.parallel", "no")
parallel <- match.arg(parallel)
have_mc <- have_snow <- FALSE
if (parallel != "no" && ncpus > 1L) {
    if (parallel == "multicore") 
        have_mc <- .Platform$OS.type != "windows"
    else if (parallel == "snow") 
        have_snow <- TRUE
    if (!have_mc && !have_snow) 
        ncpus <- 1L
    loadNamespace("parallel")
}
if (simple && (sim != "ordinary" || stype != "i" || sum(m))) {
    warning("'simple=TRUE' is only valid for 'sim=\"ordinary\", stype=\"i\", n=0', so ignored")
    simple <- FALSE
}
if (!exists(".Random.seed", envir = .GlobalEnv, inherits = FALSE)) 
    runif(1)
seed <- get(".Random.seed", envir = .GlobalEnv, inherits = FALSE)
n <- NROW(data)
if ((n == 0) || is.null(n)) 
    stop("no data in call to 'boot'")
temp.str <- strata
strata <- tapply(seq_len(n), as.numeric(strata))
t0 <- if (sim != "parametric") {
    if ((sim == "antithetic") && is.null(L)) 
        L <- empinf(data = data, statistic = statistic, stype = stype, 
            strata = strata, ...)
    if (sim != "ordinary") 
        m <- 0
    else if (any(m < 0)) 
        stop("negative value of 'm' supplied")
    if ((length(m) != 1L) && (length(m) != length(table(strata)))) 
        stop("length of 'm' incompatible with 'strata'")
    if ((sim == "ordinary") || (sim == "balanced")) {
        if (isMatrix(weights) && (nrow(weights) != length(R))) 
            stop("dimensions of 'R' and 'weights' do not match")
    }
    else weights <- NULL
    if (!is.null(weights)) 
        weights <- t(apply(matrix(weights, n, length(R), 
            byrow = TRUE), 2L, normalize, strata))
    if (!simple) 
        i <- index.array(n, R, sim, strata, m, L, weights)
    original <- if (stype == "f") 
        rep(1, n)
    else if (stype == "w") {
        ns <- tabulate(strata)[strata]
        1/ns
    }
    else seq_len(n)
    t0 <- if (sum(m) > 0L) 
        statistic(data, original, rep(1, sum(m)), ...)
    else statistic(data, original, ...)
    rm(original)
    t0
}
else statistic(data, ...)
pred.i <- NULL
fn <- if (sim == "parametric") {
    ran.gen
    data
    mle
    function(r) {
        dd <- ran.gen(data, mle)
        statistic(dd, ...)
    }
}
else {
    if (!simple && ncol(i) > n) {
        pred.i <- as.matrix(i[, (n + 1L):ncol(i)])
        i <- i[, seq_len(n)]
    }
    if (stype %in% c("f", "w")) {
        f <- freq.array(i)
        rm(i)
        if (stype == "w") 
            f <- f/ns
        if (sum(m) == 0L) 
            function(r) statistic(data, f[r, ], ...)
        else function(r) statistic(data, f[r, ], pred.i[r, 
            ], ...)
    }
    else if (sum(m) > 0L) 
        function(r) statistic(data, i[r, ], pred.i[r, ], 
            ...)
    else if (simple) 
        function(r) statistic(data, index.array(n, 1, sim, 
            strata, m, L, weights), ...)
    else function(r) statistic(data, i[r, ], ...)
}
RR <- sum(R)
res <- if (ncpus > 1L && (have_mc || have_snow)) {
    if (have_mc) {
        parallel::mclapply(seq_len(RR), fn, mc.cores = ncpus)
    }
    else if (have_snow) {
        list(...)
        if (is.null(cl)) {
            cl <- parallel::makePSOCKcluster(rep("localhost", 
              ncpus))
            if (RNGkind()[1L] == "L'Ecuyer-CMRG") 
              parallel::clusterSetRNGStream(cl)
            res <- parallel::parLapply(cl, seq_len(RR), fn)
            parallel::stopCluster(cl)
            res
        }
        else parallel::parLapply(cl, seq_len(RR), fn)
    }
}
else pblapply(seq_len(RR), fn) #### changed !!!
t.star <- matrix(, RR, length(t0))
for (r in seq_len(RR)) t.star[r, ] <- res[[r]]
if (is.null(weights)) 
    weights <- 1/tabulate(strata)[strata]
boot.return(sim, t0, t.star, temp.str, R, data, statistic, 
    stype, call, seed, L, m, pred.i, weights, ran.gen, mle)
}
## Functions not exported by boot
isMatrix <- boot:::isMatrix
index.array <- boot:::index.array
boot.return <- boot:::boot.return
## Now the example
m.boot <- function(data, indices) {
  d <- data[indices]
  mean(d, na.rm = T) 
}
tot_rep <- 200
v1 <- rnorm(1000)
b <- boot2(v1, m.boot, R = tot_rep)
person psolymos    schedule 07.06.2016
comment
Это хорошее решение, но я не думаю, что сопровождающий пакета заинтересован в изначальном добавлении какого-либо индикатора выполнения, так как это неизбежно замедлит работу функции boot. Тем не менее, это может быть элегантным решением с использованием копии функции boot, как это сделали вы! - person fzara; 08.06.2016

Вы можете использовать пакет progress, как показано ниже:

library(boot)
library(progress)

v1 <- rnorm(1000)

#add progress bar as parameter to function
m.boot <- function(data, indices, prog) {
  
  #display progress with each run of the function
  prog$tick()
  
  d <- data[indices]
  Sys.sleep(0.01)
  mean(d, na.rm = T) 
  
}

tot_rep <- 200

#initialize progress bar object
pb <- progress_bar$new(total = tot_rep + 1) 

#perform bootstrap
boot(data = v1, statistic = m.boot, R = tot_rep, prog = pb)

Я еще не совсем понял, почему необходимо установить количество итераций для progress_bar равным +1 общему количеству реплик бутстрапа (параметр R), но это то, что было необходимо в моем собственном коде, иначе он выдает ошибку. Похоже, что функция начальной загрузки запускается на один раз больше, чем вы указываете в параметре R, поэтому, если индикатор выполнения настроен на запуск только R раз, он думает, что задание завершено, прежде чем это действительно так.

person Lindsay Lee    schedule 20.07.2020
comment
Причина, по которой необходимо установить total = R+1, вероятно, заключается в том, что boot() вычисляет статистику R + 1 - одну для фактических данных (t0) и статистику R t для образцов начальной загрузки R. Следовательно, boot() вызывается R+1 раз. - person A.Fischer; 03.06.2021

Увеличенное значение rep_count является локальной переменной и теряется после каждого вызова функции. На следующей итерации функция снова получает rep_count из глобального окружения, т. е. ее значение равно 1.

Вы можете использовать <<-:

rep_count <<- rep_count + 1

Это присваивается rep_count, первому найденному на пути поиска вне функции. Конечно, использование <<- обычно не рекомендуется, потому что следует избегать побочных эффектов функций, но здесь у вас есть законный вариант использования. Однако вам, вероятно, следует обернуть все это в функцию, чтобы избежать побочного эффекта в глобальной среде.

Возможно, есть лучшие решения...

person Roland    schedule 07.06.2016
comment
Я думаю, что это более правильный способ с точки зрения программиста. Но из-за моих скудных способностей в программировании я думаю, что буду придерживаться решения, представленного ниже. Большое спасибо! - person fzara; 07.06.2016
comment
Вам не нужны продвинутые навыки программирования, чтобы изменить одну строку кода. - person Roland; 07.06.2016
comment
Вы правы, мне просто нравится использовать чужие функции, так как я думаю, что они запрограммированы лучше, чем мои. Например, возможность использовать пакет pbapply добавляет возможность изменять стиль и тип индикатора выполнения всего за несколько секунд. Я пытаюсь найти способ использовать pbapply с bootstrap. Но может быть кто-то найдет решение раньше меня! - person fzara; 07.06.2016
comment
Если вы хотите использовать функцию типа apply, вы не можете использовать boot. Вам придется написать свою собственную загрузку (что было бы несложно и, возможно, познавательно). - person Roland; 07.06.2016

Я думаю, что нашел возможное решение. Это объединяет ответ @Roland с удобством пакета pbapply, используя его функции startpb(), closepb() и т. д.

library(boot)
library(pbapply)

v1 <- rnorm(1000)
rep_count = 1
tot_rep = 200

m.boot <- function(data, indices) {
  d <- data[indices]
  setpb(pb, rep_count)
  rep_count <<- rep_count + 1
  Sys.sleep(0.01)                #Just to slow down the process
  mean(d, na.rm = T) 
}

pb <- startpb(min = 0, max = tot_rep)
b <- boot(v1, m.boot, R = tot_rep)
closepb(pb)
rep_count = 1

Как предлагалось ранее, обертывание всего в функцию позволяет избежать возни с переменной rep_count.

person fzara    schedule 07.06.2016

Индикатор выполнения из пакета dplyr работает хорошо:

library(dplyr)
library(boot)

v1 <- rnorm(1000)

m.boot <- function(data, indices) {
  d <- data[indices]
  p$tick()$print()  # update progress bar
  Sys.sleep(0.01)
  mean(d, na.rm = T) 
}

tot_rep <- 200
p <- progress_estimated(tot_rep+1)  # init progress bar
b <- boot(v1, m.boot, R = tot_rep)
person hypothesis    schedule 22.03.2018

Вы можете использовать пакет pbapply

library(boot)
library(pbapply)
v1 <- rnorm(1000)
rep_count = 1

# your m.boot function ....
m.boot <- function(data, indices) {
                                   d <- data[indices]
                                   mean(d, na.rm = T) 
                                   }

# ... wraped in `bootfunc`
bootfunc <- function(x) { boot(x, m.boot, R = 200) }

# apply function to v1 , returning progress bar
pblapply(v1, bootfunc)

# > b <- pblapply(v1, bootfunc)
# >   |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% Elapsed time: 02s
person rafa.pereira    schedule 07.06.2016
comment
У меня проблема. Эта функция запускает функцию начальной загрузки несколько раз, получая объект b, который является не одним объектом начальной загрузки, а вектором из 1000 объектов начальной загрузки. Я думаю, что pbapply плохо работает с этой функцией. - person fzara; 07.06.2016
comment
Действительно, @fzara, я думаю об этом и вернусь с решением этой проблемы. - person rafa.pereira; 07.06.2016
comment
Большое спасибо! Тем временем я нашел обходной путь, надеюсь, он будет полезен и вам. - person fzara; 07.06.2016