Пожалуйста, обратите внимание на фрагмент в конце поста. Я хотел бы иметь возможность сохранять (возможно, в виде RDS) результаты вычислений по мере их выполнения (например, каждый раз, когда обрабатываются новые 10% списка). Как мне это сделать?
library(tidyverse)
ll <- 1:1000
res <- map(ll, \(x) cos(x))
sessionInfo()
#> R version 4.4.1 (2024-06-14)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Debian GNU/Linux 12 (bookworm)
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.11.0
#> LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.11.0
#>
#> locale:
#> [1] LC_CTYPE=en_GB.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_GB.UTF-8 LC_COLLATE=en_GB.UTF-8
#> [5] LC_MONETARY=en_GB.UTF-8 LC_MESSAGES=en_GB.UTF-8
#> [7] LC_PAPER=en_GB.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_GB.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: Europe/Brussels
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] lubridate_1.9.3 forcats_1.0.0 stringr_1.5.1 dplyr_1.1.4
#> [5] purrr_1.0.2 readr_2.1.5 tidyr_1.3.1 tibble_3.2.1
#> [9] ggplot2_3.5.1 tidyverse_2.0.0
#>
#> loaded via a namespace (and not attached):
#> [1] gtable_0.3.5 compiler_4.4.1 reprex_2.1.0 tidyselect_1.2.1
#> [5] scales_1.3.0 yaml_2.3.8 fastmap_1.1.1 R6_2.5.1
#> [9] generics_0.1.3 knitr_1.46 munsell_0.5.1 R.cache_0.16.0
#> [13] tzdb_0.4.0 pillar_1.9.0 R.utils_2.12.3 rlang_1.1.3
#> [17] utf8_1.2.4 stringi_1.8.4 xfun_0.43 fs_1.6.4
#> [21] timechange_0.3.0 cli_3.6.2 withr_3.0.0 magrittr_2.0.3
#> [25] digest_0.6.35 grid_4.4.1 hms_1.1.3 lifecycle_1.0.4
#> [29] R.methodsS3_1.8.2 R.oo_1.26.0 vctrs_0.6.5 evaluate_0.23
#> [33] glue_1.7.0 styler_1.10.3 fansi_1.0.6 colorspace_2.1-0
#> [37] rmarkdown_2.26 tools_4.4.1 pkgconfig_2.0.3 htmltools_0.5.8.1
Created on 2024-06-27 with reprex v2.1.0





Оказывается, для этого есть пакет currr («КПП» + purrr). Он не сохраняется именно в указанной вами форме (но см. ниже, как получить доступ к промежуточным результатам), но эти функции (cp_map() например)
создайте секретную папку в текущем рабочем каталоге и сохраните результаты, если они достигнут заданной контрольной точки. Таким образом, если вы перезапустите код, он прочитает результат из папки кэша и начнет оценивать, где вы закончили. [немного отредактировано из оригинала]
cp_map() имеет аргумент cp_option=, который позволяет вам указать, как часто проверять точки (т. е. сколько точек проверки на задание) и где хранить результаты.
library(currr)
options(currr.n_checkpoint = 10, currr.folder = "checkpoints")
cc <- cp_map(1:1000, name = "cos_results", cos)
list.files("checkpoints/cos_results")
Если вы хотите просмотреть эти промежуточные выходные данные напрямую (а не использовать их через пакет в качестве автоматизированной системы контрольных точек), вам придется выяснить, что это за файлы: похоже, что файлы out* хранят фрагменты вывода (например, out_301.rds есть результаты для cos(301:400)).
[1] "et_1.rds" "et_101.rds" "et_201.rds" "et_301.rds" "et_401.rds"
[6] "et_501.rds" "et_601.rds" "et_701.rds" "et_801.rds" "et_901.rds"
[11] "f.rds" "id_1.rds" "id_101.rds" "id_201.rds" "id_301.rds"
[16] "id_401.rds" "id_501.rds" "id_601.rds" "id_701.rds" "id_801.rds"
[21] "id_901.rds" "meta.rds" "out_1.rds" "out_101.rds" "out_201.rds"
[26] "out_301.rds" "out_401.rds" "out_501.rds" "out_601.rds" "out_701.rds"
[31] "out_801.rds" "out_901.rds" "st_1.rds" "st_101.rds" "st_201.rds"
[36] "st_301.rds" "st_401.rds" "st_501.rds" "st_601.rds" "st_701.rds"
[41] "st_801.rds" "st_901.rds" "x.rds"
Другой вариант — создать функциональный оператор и сохранить выходные данные в среде функции. Затем вы можете получить эту информацию после (или сохранить ее, используя readRDS()):
library(purrr)
funop <- function(f, niter, update_every = 0.10) {
force(f)
force(niter)
i <- 0
i_update <- floor(quantile(seq(niter), seq(update_every, 1, by = update_every)))
output <- vector("list", length = niter)
function(...) {
i <<- i + 1
val <- f(...)
if (i %in% i_update) {
output[[i]] <<- val # saveRDS() call here
}
return(val)
}
}
mycos <- funop(cos, length(ll))
res <- map(ll, mycos)
compact(environment(mycos)$output) |> head(3)
# [[1]]
# [1] -0.8390715
#
# [[2]]
# [1] 0.4080821
#
# [[3]]
# [1] 0.1542514
## OR saveRDS() after
saveRDS("results", compact(environment(mycos)$output))
При этом вывод просто сохраняется в объекте списка в среде функции, но вы можете легко заменить его вызовом saveRDS(). Тогда вам не нужно будет создавать объект списка output.
library(tidyverse)
ll <- 1:1000
split_ll <- split(ll, (-1 + ll) %/% 100)
res <- imap(
split_ll,
\(ll_chunk, name){
interim_res <- map(ll_chunk, \(x) cos(x))
saveRDS(interim_res, file = paste0("ch_", name, ".RDS"))
interim_res
}
)
res_vec <- unlist(res)
# you also have 10 ch_*.RDS files with the partial results
Спасибо! Эта система контрольно-пропускных пунктов — именно то, что я искал. Я могу запустить большое задание map(), завершить его через некоторое время и перезапустить позже с последней контрольной точки. Это удовлетворяет мою потребность.