As part of a reproducible workflow, caching of function calls, code chunks, and other elements of a project is a critical component. The objective of a reproducible workflow is is likely that an entire work flow from raw data to publication, decision support, report writing, presentation building etc., could be built and be reproducible anywhere, on any computer, operating system, with any starting conditions, on demand. The reproducible::Cache
function is built to work with any R function.
Cache
uses 2 key the archivist
functions saveToLocalRepo
and loadFromLocalRepo
, but does not use archivist::cache
. Similar to archivist::cache
, there is some reliance on digest::digest
to determine whether the arguments are identical in subsequent iterations; however, it also uses fastdigest::fastdigest
to make it substantially faster in many cases. It also but does many things that make standard caching with digest::digest
don’t work reliably between systems. For these, the function .robustDigest
is introduced to make caching transferable between systems. This is relevant for file paths, environments, parallel clusters, functions (which are contained within an environment), and many others (e.g., see ?.robustDigest
for methods). Cache
also adds important elements like automated tagging and the option to retrieve disk-cached values via stashed objects in memory using memoise::memoise
. This means that running Cache
1, 2, and 3 times on the same function will get progressively faster. This can be extremely useful for web apps built with, say shiny
.
Any function can be cached using: Cache(FUN = functionName, ...)
.
This will be a slight change to a function call, such as: projectRaster(raster, crs = crs(newRaster))
to Cache(projectRaster, raster, crs = crs(newRaster))
.
This is particularly useful for expensive operations.
library(raster)
## Loading required package: sp
library(reproducible)
tmpDir <- file.path(tempdir(), "reproducible_examples", "Cache")
checkPath(tmpDir, create = TRUE)
## [1] "/tmp/RtmpgCZsyU/reproducible_examples/Cache"
ras <- raster(extent(0,1000,0,1000), vals = 1:1e6, res = 1)
crs(ras) <- "+proj=lcc +lat_1=48 +lat_2=33 +lon_0=-100 +ellps=WGS84"
newCRS <- "+init=epsg:4326 +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0"
# No Cache
system.time(map1 <- projectRaster(ras, crs = newCRS))
## user system elapsed
## 3.188 0.204 3.394
# With Cache -- a little slower the first time because saving to disk
system.time(map1 <- Cache(projectRaster, ras, crs = newCRS, cacheRepo = tmpDir,
notOlderThan = Sys.time()))
## user system elapsed
## 3.309 0.279 3.712
# vastly faster the second time
system.time(map2 <- Cache(projectRaster, ras, crs = newCRS, cacheRepo = tmpDir))
## loading cached result from previous projectRaster call, adding to memoised copy
## user system elapsed
## 0.213 0.000 0.212
# even faster the third time
system.time(map3 <- Cache(projectRaster, ras, crs = newCRS, cacheRepo = tmpDir))
## loading memoised result from previous projectRaster call.
## user system elapsed
## 0.061 0.000 0.061
all.equal(map1, map2) # TRUE
## [1] TRUE
all.equal(map1, map3) # TRUE
## [1] TRUE
library(raster)
# magrittr, if loaded, gives an error below
try(detach("package:magrittr", unload = TRUE), silent = TRUE)
try(clearCache(tmpDir), silent = TRUE) # just to make sure it is clear
ranNumsA <- Cache(rnorm, 10, 16, cacheRepo = tmpDir)
# All same
ranNumsB <- Cache(rnorm, 10, 16, cacheRepo = tmpDir) # recovers cached copy
## loading cached result from previous rnorm call, adding to memoised copy
ranNumsC <- rnorm(10, 16) %>% Cache(cacheRepo = tmpDir) # recovers cached copy
## loading memoised result from previous 'rnorm' pipe sequence call.
ranNumsD <- Cache(quote(rnorm(n = 10, 16)), cacheRepo = tmpDir) # recovers cached copy
## loading memoised result from previous rnorm call.
# Any minor change makes it different
ranNumsE <- rnorm(10, 6) %>% Cache(cacheRepo = tmpDir) # different
ranNumsA <- Cache(rnorm, 4, cacheRepo = tmpDir, userTags = "objectName:a")
ranNumsB <- Cache(runif, 4, cacheRepo = tmpDir, userTags = "objectName:b")
# access it again, from Cache
ranNumsA <- Cache(rnorm, 4, cacheRepo = tmpDir, userTags = "objectName:a")
## loading cached result from previous rnorm call, adding to memoised copy
wholeCache <- showCache(tmpDir)
## Cache size:
## Total (including Rasters): 12.3 Kb
## Selected objects (not including Rasters): 335 bytes
# keep only items accessed "recently" (i.e., only objectName:a)
onlyRecentlyAccessed <- showCache(tmpDir, userTags = max(wholeCache[tagKey == "accessed"]$tagValue))
## Cache size:
## Total (including Rasters): 12.3 Kb
## Selected objects (not including Rasters): 335 bytes
# inverse join with 2 data.tables ... using: a[!b]
# i.e., return all of wholeCache that was not recently accessed
toRemove <- unique(wholeCache[!onlyRecentlyAccessed], by = "artifact")$artifact
clearCache(tmpDir, toRemove) # remove ones not recently accessed
## Cache size:
## Total (including Rasters): 12.3 Kb
## Selected objects (not including Rasters): 335 bytes
showCache(tmpDir) # still has more recently accessed
## Cache size:
## Total (including Rasters): 12 Kb
## Selected objects (not including Rasters): 0 bytes
## Empty data.table (0 rows) of 3 cols: md5hash,name,createdDate
clearCache(tmpDir)
ranNumsA <- Cache(rnorm, 4, cacheRepo = tmpDir, userTags = "objectName:a")
ranNumsB <- Cache(runif, 4, cacheRepo = tmpDir, userTags = "objectName:b")
# keep only those cached items from the last 24 hours
oneDay <- 60 * 60 * 24
keepCache(tmpDir, after = Sys.time() - oneDay)
## Cache size:
## Total (including Rasters): 12.3 Kb
## Selected objects (not including Rasters): 338 bytes
## artifact tagKey
## 1: 68dd4432cd40fb6fe00bdcf5ed3f4794 format
## 2: 68dd4432cd40fb6fe00bdcf5ed3f4794 name
## 3: 68dd4432cd40fb6fe00bdcf5ed3f4794 class
## 4: 68dd4432cd40fb6fe00bdcf5ed3f4794 date
## 5: 68dd4432cd40fb6fe00bdcf5ed3f4794 cacheId
## 6: 68dd4432cd40fb6fe00bdcf5ed3f4794 objectName
## 7: 68dd4432cd40fb6fe00bdcf5ed3f4794 function
## 8: 68dd4432cd40fb6fe00bdcf5ed3f4794 object.size
## 9: 68dd4432cd40fb6fe00bdcf5ed3f4794 accessed
## 10: 68dd4432cd40fb6fe00bdcf5ed3f4794 otherFunctions
## 11: 68dd4432cd40fb6fe00bdcf5ed3f4794 otherFunctions
## 12: 68dd4432cd40fb6fe00bdcf5ed3f4794 otherFunctions
## 13: 68dd4432cd40fb6fe00bdcf5ed3f4794 otherFunctions
## 14: 68dd4432cd40fb6fe00bdcf5ed3f4794 otherFunctions
## 15: 68dd4432cd40fb6fe00bdcf5ed3f4794 preDigest
## 16: 68dd4432cd40fb6fe00bdcf5ed3f4794 preDigest
## 17: a1e91bd8155ec57f0badd5cda2b40539 format
## 18: a1e91bd8155ec57f0badd5cda2b40539 name
## 19: a1e91bd8155ec57f0badd5cda2b40539 class
## 20: a1e91bd8155ec57f0badd5cda2b40539 date
## 21: a1e91bd8155ec57f0badd5cda2b40539 cacheId
## 22: a1e91bd8155ec57f0badd5cda2b40539 objectName
## 23: a1e91bd8155ec57f0badd5cda2b40539 function
## 24: a1e91bd8155ec57f0badd5cda2b40539 object.size
## 25: a1e91bd8155ec57f0badd5cda2b40539 accessed
## 26: a1e91bd8155ec57f0badd5cda2b40539 otherFunctions
## 27: a1e91bd8155ec57f0badd5cda2b40539 otherFunctions
## 28: a1e91bd8155ec57f0badd5cda2b40539 otherFunctions
## 29: a1e91bd8155ec57f0badd5cda2b40539 otherFunctions
## 30: a1e91bd8155ec57f0badd5cda2b40539 otherFunctions
## 31: a1e91bd8155ec57f0badd5cda2b40539 preDigest
## 32: a1e91bd8155ec57f0badd5cda2b40539 preDigest
## artifact tagKey
## tagValue createdDate
## 1: rda 2018-07-05 13:06:29
## 2: Cache 2018-07-05 13:06:29
## 3: numeric 2018-07-05 13:06:29
## 4: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 5: e37bb635c97bc2eeecab63816b881bbc 2018-07-05 13:06:29
## 6: b 2018-07-05 13:06:29
## 7: runif 2018-07-05 13:06:29
## 8: 688 2018-07-05 13:06:29
## 9: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 10: process_file 2018-07-05 13:06:29
## 11: process_group 2018-07-05 13:06:29
## 12: process_group.block 2018-07-05 13:06:29
## 13: call_block 2018-07-05 13:06:29
## 14: block_exec 2018-07-05 13:06:29
## 15: n:969a49ec15bcd4323ff31538af321264 2018-07-05 13:06:29
## 16: .FUN:d2631d24c3b38b89c7bdd4ab7faaaac3 2018-07-05 13:06:29
## 17: rda 2018-07-05 13:06:29
## 18: Cache 2018-07-05 13:06:29
## 19: numeric 2018-07-05 13:06:29
## 20: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 21: 85874f26b2e0c1ef689a7d379d275ebf 2018-07-05 13:06:29
## 22: a 2018-07-05 13:06:29
## 23: rnorm 2018-07-05 13:06:29
## 24: 688 2018-07-05 13:06:29
## 25: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 26: process_file 2018-07-05 13:06:29
## 27: process_group 2018-07-05 13:06:29
## 28: process_group.block 2018-07-05 13:06:29
## 29: call_block 2018-07-05 13:06:29
## 30: block_exec 2018-07-05 13:06:29
## 31: n:969a49ec15bcd4323ff31538af321264 2018-07-05 13:06:29
## 32: .FUN:7e9a928f110f80b3612e71883a6ec1f4 2018-07-05 13:06:29
## tagValue createdDate
# Keep all Cache items created with an rnorm() call
keepCache(tmpDir, userTags = "rnorm")
## Cache size:
## Total (including Rasters): 12.3 Kb
## Selected objects (not including Rasters): 172 bytes
## Cache size:
## Total (including Rasters): 12.3 Kb
## Selected objects (not including Rasters): 166 bytes
## artifact tagKey
## 1: a1e91bd8155ec57f0badd5cda2b40539 format
## 2: a1e91bd8155ec57f0badd5cda2b40539 name
## 3: a1e91bd8155ec57f0badd5cda2b40539 class
## 4: a1e91bd8155ec57f0badd5cda2b40539 date
## 5: a1e91bd8155ec57f0badd5cda2b40539 cacheId
## 6: a1e91bd8155ec57f0badd5cda2b40539 objectName
## 7: a1e91bd8155ec57f0badd5cda2b40539 function
## 8: a1e91bd8155ec57f0badd5cda2b40539 object.size
## 9: a1e91bd8155ec57f0badd5cda2b40539 accessed
## 10: a1e91bd8155ec57f0badd5cda2b40539 otherFunctions
## 11: a1e91bd8155ec57f0badd5cda2b40539 otherFunctions
## 12: a1e91bd8155ec57f0badd5cda2b40539 otherFunctions
## 13: a1e91bd8155ec57f0badd5cda2b40539 otherFunctions
## 14: a1e91bd8155ec57f0badd5cda2b40539 otherFunctions
## 15: a1e91bd8155ec57f0badd5cda2b40539 preDigest
## 16: a1e91bd8155ec57f0badd5cda2b40539 preDigest
## tagValue createdDate
## 1: rda 2018-07-05 13:06:29
## 2: Cache 2018-07-05 13:06:29
## 3: numeric 2018-07-05 13:06:29
## 4: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 5: 85874f26b2e0c1ef689a7d379d275ebf 2018-07-05 13:06:29
## 6: a 2018-07-05 13:06:29
## 7: rnorm 2018-07-05 13:06:29
## 8: 688 2018-07-05 13:06:29
## 9: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 10: process_file 2018-07-05 13:06:29
## 11: process_group 2018-07-05 13:06:29
## 12: process_group.block 2018-07-05 13:06:29
## 13: call_block 2018-07-05 13:06:29
## 14: block_exec 2018-07-05 13:06:29
## 15: n:969a49ec15bcd4323ff31538af321264 2018-07-05 13:06:29
## 16: .FUN:7e9a928f110f80b3612e71883a6ec1f4 2018-07-05 13:06:29
# Remove all Cache items that happened within a rnorm() call
clearCache(tmpDir, userTags = "rnorm")
## Cache size:
## Total (including Rasters): 12.2 Kb
## Selected objects (not including Rasters): 172 bytes
showCache(tmpDir) ## empty
## Cache size:
## Total (including Rasters): 12 Kb
## Selected objects (not including Rasters): 0 bytes
## Empty data.table (0 rows) of 3 cols: md5hash,name,createdDate
# default userTags is "and" matching; for "or" matching use |
ranNumsA <- Cache(runif, 4, cacheRepo = tmpDir, userTags = "objectName:a")
ranNumsB <- Cache(rnorm, 4, cacheRepo = tmpDir, userTags = "objectName:b")
# show all objects (runif and rnorm in this case)
showCache(tmpDir)
## Cache size:
## Total (including Rasters): 12.3 Kb
## Selected objects (not including Rasters): 337 bytes
## artifact tagKey
## 1: f6cdb1c02db9a1b4cd723499f28e129e format
## 2: f6cdb1c02db9a1b4cd723499f28e129e name
## 3: f6cdb1c02db9a1b4cd723499f28e129e class
## 4: f6cdb1c02db9a1b4cd723499f28e129e date
## 5: f6cdb1c02db9a1b4cd723499f28e129e cacheId
## 6: f6cdb1c02db9a1b4cd723499f28e129e objectName
## 7: f6cdb1c02db9a1b4cd723499f28e129e function
## 8: f6cdb1c02db9a1b4cd723499f28e129e object.size
## 9: f6cdb1c02db9a1b4cd723499f28e129e accessed
## 10: f6cdb1c02db9a1b4cd723499f28e129e otherFunctions
## 11: f6cdb1c02db9a1b4cd723499f28e129e otherFunctions
## 12: f6cdb1c02db9a1b4cd723499f28e129e otherFunctions
## 13: f6cdb1c02db9a1b4cd723499f28e129e otherFunctions
## 14: f6cdb1c02db9a1b4cd723499f28e129e otherFunctions
## 15: f6cdb1c02db9a1b4cd723499f28e129e preDigest
## 16: f6cdb1c02db9a1b4cd723499f28e129e preDigest
## 17: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 format
## 18: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 name
## 19: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 class
## 20: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 date
## 21: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 cacheId
## 22: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 objectName
## 23: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 function
## 24: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 object.size
## 25: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 accessed
## 26: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 otherFunctions
## 27: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 otherFunctions
## 28: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 otherFunctions
## 29: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 otherFunctions
## 30: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 otherFunctions
## 31: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 preDigest
## 32: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 preDigest
## artifact tagKey
## tagValue createdDate
## 1: rda 2018-07-05 13:06:29
## 2: Cache 2018-07-05 13:06:29
## 3: numeric 2018-07-05 13:06:29
## 4: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 5: 85874f26b2e0c1ef689a7d379d275ebf 2018-07-05 13:06:29
## 6: b 2018-07-05 13:06:29
## 7: rnorm 2018-07-05 13:06:29
## 8: 688 2018-07-05 13:06:29
## 9: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 10: process_file 2018-07-05 13:06:29
## 11: process_group 2018-07-05 13:06:29
## 12: process_group.block 2018-07-05 13:06:29
## 13: call_block 2018-07-05 13:06:29
## 14: block_exec 2018-07-05 13:06:29
## 15: n:969a49ec15bcd4323ff31538af321264 2018-07-05 13:06:29
## 16: .FUN:7e9a928f110f80b3612e71883a6ec1f4 2018-07-05 13:06:29
## 17: rda 2018-07-05 13:06:29
## 18: Cache 2018-07-05 13:06:29
## 19: numeric 2018-07-05 13:06:29
## 20: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 21: e37bb635c97bc2eeecab63816b881bbc 2018-07-05 13:06:29
## 22: a 2018-07-05 13:06:29
## 23: runif 2018-07-05 13:06:29
## 24: 688 2018-07-05 13:06:29
## 25: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 26: process_file 2018-07-05 13:06:29
## 27: process_group 2018-07-05 13:06:29
## 28: process_group.block 2018-07-05 13:06:29
## 29: call_block 2018-07-05 13:06:29
## 30: block_exec 2018-07-05 13:06:29
## 31: n:969a49ec15bcd4323ff31538af321264 2018-07-05 13:06:29
## 32: .FUN:d2631d24c3b38b89c7bdd4ab7faaaac3 2018-07-05 13:06:29
## tagValue createdDate
# show objects that are both runif and rnorm
# (i.e., none in this case, because objecs are either or, not both)
showCache(tmpDir, userTags = c("runif", "rnorm")) ## empty
## Cache size:
## Total (including Rasters): 12.3 Kb
## Selected objects (not including Rasters): 0 bytes
## Empty data.table (0 rows) of 4 cols: artifact,tagKey,tagValue,createdDate
# show objects that are either runif or rnorm ("or" search)
showCache(tmpDir, userTags = "runif|rnorm")
## Cache size:
## Total (including Rasters): 12.3 Kb
## Selected objects (not including Rasters): 337 bytes
## artifact tagKey
## 1: f6cdb1c02db9a1b4cd723499f28e129e format
## 2: f6cdb1c02db9a1b4cd723499f28e129e name
## 3: f6cdb1c02db9a1b4cd723499f28e129e class
## 4: f6cdb1c02db9a1b4cd723499f28e129e date
## 5: f6cdb1c02db9a1b4cd723499f28e129e cacheId
## 6: f6cdb1c02db9a1b4cd723499f28e129e objectName
## 7: f6cdb1c02db9a1b4cd723499f28e129e function
## 8: f6cdb1c02db9a1b4cd723499f28e129e object.size
## 9: f6cdb1c02db9a1b4cd723499f28e129e accessed
## 10: f6cdb1c02db9a1b4cd723499f28e129e otherFunctions
## 11: f6cdb1c02db9a1b4cd723499f28e129e otherFunctions
## 12: f6cdb1c02db9a1b4cd723499f28e129e otherFunctions
## 13: f6cdb1c02db9a1b4cd723499f28e129e otherFunctions
## 14: f6cdb1c02db9a1b4cd723499f28e129e otherFunctions
## 15: f6cdb1c02db9a1b4cd723499f28e129e preDigest
## 16: f6cdb1c02db9a1b4cd723499f28e129e preDigest
## 17: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 format
## 18: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 name
## 19: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 class
## 20: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 date
## 21: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 cacheId
## 22: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 objectName
## 23: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 function
## 24: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 object.size
## 25: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 accessed
## 26: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 otherFunctions
## 27: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 otherFunctions
## 28: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 otherFunctions
## 29: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 otherFunctions
## 30: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 otherFunctions
## 31: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 preDigest
## 32: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 preDigest
## artifact tagKey
## tagValue createdDate
## 1: rda 2018-07-05 13:06:29
## 2: Cache 2018-07-05 13:06:29
## 3: numeric 2018-07-05 13:06:29
## 4: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 5: 85874f26b2e0c1ef689a7d379d275ebf 2018-07-05 13:06:29
## 6: b 2018-07-05 13:06:29
## 7: rnorm 2018-07-05 13:06:29
## 8: 688 2018-07-05 13:06:29
## 9: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 10: process_file 2018-07-05 13:06:29
## 11: process_group 2018-07-05 13:06:29
## 12: process_group.block 2018-07-05 13:06:29
## 13: call_block 2018-07-05 13:06:29
## 14: block_exec 2018-07-05 13:06:29
## 15: n:969a49ec15bcd4323ff31538af321264 2018-07-05 13:06:29
## 16: .FUN:7e9a928f110f80b3612e71883a6ec1f4 2018-07-05 13:06:29
## 17: rda 2018-07-05 13:06:29
## 18: Cache 2018-07-05 13:06:29
## 19: numeric 2018-07-05 13:06:29
## 20: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 21: e37bb635c97bc2eeecab63816b881bbc 2018-07-05 13:06:29
## 22: a 2018-07-05 13:06:29
## 23: runif 2018-07-05 13:06:29
## 24: 688 2018-07-05 13:06:29
## 25: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 26: process_file 2018-07-05 13:06:29
## 27: process_group 2018-07-05 13:06:29
## 28: process_group.block 2018-07-05 13:06:29
## 29: call_block 2018-07-05 13:06:29
## 30: block_exec 2018-07-05 13:06:29
## 31: n:969a49ec15bcd4323ff31538af321264 2018-07-05 13:06:29
## 32: .FUN:d2631d24c3b38b89c7bdd4ab7faaaac3 2018-07-05 13:06:29
## tagValue createdDate
# keep only objects that are either runif or rnorm ("or" search)
keepCache(tmpDir, userTags = "runif|rnorm")
## Cache size:
## Total (including Rasters): 12.3 Kb
## Selected objects (not including Rasters): 337 bytes
## artifact tagKey
## 1: f6cdb1c02db9a1b4cd723499f28e129e format
## 2: f6cdb1c02db9a1b4cd723499f28e129e name
## 3: f6cdb1c02db9a1b4cd723499f28e129e class
## 4: f6cdb1c02db9a1b4cd723499f28e129e date
## 5: f6cdb1c02db9a1b4cd723499f28e129e cacheId
## 6: f6cdb1c02db9a1b4cd723499f28e129e objectName
## 7: f6cdb1c02db9a1b4cd723499f28e129e function
## 8: f6cdb1c02db9a1b4cd723499f28e129e object.size
## 9: f6cdb1c02db9a1b4cd723499f28e129e accessed
## 10: f6cdb1c02db9a1b4cd723499f28e129e otherFunctions
## 11: f6cdb1c02db9a1b4cd723499f28e129e otherFunctions
## 12: f6cdb1c02db9a1b4cd723499f28e129e otherFunctions
## 13: f6cdb1c02db9a1b4cd723499f28e129e otherFunctions
## 14: f6cdb1c02db9a1b4cd723499f28e129e otherFunctions
## 15: f6cdb1c02db9a1b4cd723499f28e129e preDigest
## 16: f6cdb1c02db9a1b4cd723499f28e129e preDigest
## 17: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 format
## 18: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 name
## 19: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 class
## 20: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 date
## 21: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 cacheId
## 22: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 objectName
## 23: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 function
## 24: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 object.size
## 25: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 accessed
## 26: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 otherFunctions
## 27: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 otherFunctions
## 28: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 otherFunctions
## 29: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 otherFunctions
## 30: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 otherFunctions
## 31: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 preDigest
## 32: f90d7c5abe6ca13b1d79cdcb3bb2f6f3 preDigest
## artifact tagKey
## tagValue createdDate
## 1: rda 2018-07-05 13:06:29
## 2: Cache 2018-07-05 13:06:29
## 3: numeric 2018-07-05 13:06:29
## 4: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 5: 85874f26b2e0c1ef689a7d379d275ebf 2018-07-05 13:06:29
## 6: b 2018-07-05 13:06:29
## 7: rnorm 2018-07-05 13:06:29
## 8: 688 2018-07-05 13:06:29
## 9: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 10: process_file 2018-07-05 13:06:29
## 11: process_group 2018-07-05 13:06:29
## 12: process_group.block 2018-07-05 13:06:29
## 13: call_block 2018-07-05 13:06:29
## 14: block_exec 2018-07-05 13:06:29
## 15: n:969a49ec15bcd4323ff31538af321264 2018-07-05 13:06:29
## 16: .FUN:7e9a928f110f80b3612e71883a6ec1f4 2018-07-05 13:06:29
## 17: rda 2018-07-05 13:06:29
## 18: Cache 2018-07-05 13:06:29
## 19: numeric 2018-07-05 13:06:29
## 20: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 21: e37bb635c97bc2eeecab63816b881bbc 2018-07-05 13:06:29
## 22: a 2018-07-05 13:06:29
## 23: runif 2018-07-05 13:06:29
## 24: 688 2018-07-05 13:06:29
## 25: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 26: process_file 2018-07-05 13:06:29
## 27: process_group 2018-07-05 13:06:29
## 28: process_group.block 2018-07-05 13:06:29
## 29: call_block 2018-07-05 13:06:29
## 30: block_exec 2018-07-05 13:06:29
## 31: n:969a49ec15bcd4323ff31538af321264 2018-07-05 13:06:29
## 32: .FUN:d2631d24c3b38b89c7bdd4ab7faaaac3 2018-07-05 13:06:29
## tagValue createdDate
clearCache(tmpDir)
ras <- raster(extent(0, 5, 0, 5), res = 1,
vals = sample(1:5, replace = TRUE, size = 25),
crs = "+proj=lcc +lat_1=48 +lat_2=33 +lon_0=-100 +ellps=WGS84")
# A slow operation, like GIS operation
notCached <- suppressWarnings(
# project raster generates warnings when run non-interactively
projectRaster(ras, crs = crs(ras), res = 5, cacheRepo = tmpDir)
)
cached <- suppressWarnings(
# project raster generates warnings when run non-interactively
# using quote works also
Cache(projectRaster, ras, crs = crs(ras), res = 5, cacheRepo = tmpDir)
)
# second time is much faster
reRun <- suppressWarnings(
# project raster generates warnings when run non-interactively
Cache(projectRaster, ras, crs = crs(ras), res = 5, cacheRepo = tmpDir)
)
## loading cached result from previous projectRaster call, adding to memoised copy
# recovered cached version is same as non-cached version
all.equal(notCached, reRun) ## TRUE
## [1] TRUE
Nested caching, which is when Caching of a function occurs inside an outer function, which is itself cached. This is a critical element to working within a reproducible work flow. It is not enough during development to cache flat code chunks, as there will be many levels of “slow” functions. Ideally, at all points in a development cycle, it should be possible to get to any line of code starting from the very initial steps, running through everything up to that point, in less that 1 second. If the workflow can be kept very fast like this, then there is a guarantee that it will work at any point.
##########################
## Nested Caching
# Make 2 functions
inner <- function(mean) {
d <- 1
Cache(rnorm, n = 3, mean = mean)
}
outer <- function(n) {
Cache(inner, 0.1, cacheRepo = tmpdir2)
}
# make 2 different cache paths
tmpdir1 <- file.path(tempdir(), "first")
tmpdir2 <- file.path(tempdir(), "second")
# Run the Cache ... notOlderThan propagates to all 3 Cache calls,
# but cacheRepo is tmpdir1 in top level Cache and all nested
# Cache calls, unless individually overridden ... here inner
# uses tmpdir2 repository
Cache(outer, n = 2, cacheRepo = tmpdir1, notOlderThan = Sys.time())
## [1] 0.6137814 -0.9392360 -0.2488563
## attr(,"tags")
## [1] "cacheId:e09bf93970f9e94d5c639cfa8ca722f0"
## attr(,"newCache")
## [1] TRUE
## attr(,"call")
## [1] ""
showCache(tmpdir1) # 2 function calls
## Cache size:
## Total (including Rasters): 12.3 Kb
## Selected objects (not including Rasters): 327 bytes
## artifact tagKey
## 1: 281fd7d8498dcc7660b6dc7ca7952697 format
## 2: 281fd7d8498dcc7660b6dc7ca7952697 name
## 3: 281fd7d8498dcc7660b6dc7ca7952697 class
## 4: 281fd7d8498dcc7660b6dc7ca7952697 date
## 5: 281fd7d8498dcc7660b6dc7ca7952697 cacheId
## 6: 281fd7d8498dcc7660b6dc7ca7952697 function
## 7: 281fd7d8498dcc7660b6dc7ca7952697 object.size
## 8: 281fd7d8498dcc7660b6dc7ca7952697 accessed
## 9: 281fd7d8498dcc7660b6dc7ca7952697 otherFunctions
## 10: 281fd7d8498dcc7660b6dc7ca7952697 otherFunctions
## 11: 281fd7d8498dcc7660b6dc7ca7952697 otherFunctions
## 12: 281fd7d8498dcc7660b6dc7ca7952697 otherFunctions
## 13: 281fd7d8498dcc7660b6dc7ca7952697 otherFunctions
## 14: 281fd7d8498dcc7660b6dc7ca7952697 preDigest
## 15: 281fd7d8498dcc7660b6dc7ca7952697 preDigest
## 16: e3e1319b7b674a02605d1ed3470b7777 format
## 17: e3e1319b7b674a02605d1ed3470b7777 name
## 18: e3e1319b7b674a02605d1ed3470b7777 class
## 19: e3e1319b7b674a02605d1ed3470b7777 date
## 20: e3e1319b7b674a02605d1ed3470b7777 cacheId
## 21: e3e1319b7b674a02605d1ed3470b7777 function
## 22: e3e1319b7b674a02605d1ed3470b7777 object.size
## 23: e3e1319b7b674a02605d1ed3470b7777 accessed
## 24: e3e1319b7b674a02605d1ed3470b7777 otherFunctions
## 25: e3e1319b7b674a02605d1ed3470b7777 otherFunctions
## 26: e3e1319b7b674a02605d1ed3470b7777 otherFunctions
## 27: e3e1319b7b674a02605d1ed3470b7777 otherFunctions
## 28: e3e1319b7b674a02605d1ed3470b7777 otherFunctions
## 29: e3e1319b7b674a02605d1ed3470b7777 otherFunctions
## 30: e3e1319b7b674a02605d1ed3470b7777 preDigest
## 31: e3e1319b7b674a02605d1ed3470b7777 preDigest
## 32: e3e1319b7b674a02605d1ed3470b7777 preDigest
## artifact tagKey
## tagValue createdDate
## 1: rda 2018-07-05 13:06:30
## 2: Cache 2018-07-05 13:06:30
## 3: numeric 2018-07-05 13:06:30
## 4: 2018-07-05 13:06:30 2018-07-05 13:06:30
## 5: e09bf93970f9e94d5c639cfa8ca722f0 2018-07-05 13:06:30
## 6: outer 2018-07-05 13:06:30
## 7: 688 2018-07-05 13:06:30
## 8: 2018-07-05 13:06:30 2018-07-05 13:06:30
## 9: process_file 2018-07-05 13:06:30
## 10: process_group 2018-07-05 13:06:30
## 11: process_group.block 2018-07-05 13:06:30
## 12: call_block 2018-07-05 13:06:30
## 13: block_exec 2018-07-05 13:06:30
## 14: n:8128a6180a705341ab7c05cfa945edfb 2018-07-05 13:06:30
## 15: .FUN:b5f6bcbdd9f23e39c2c5d4020e73a6ff 2018-07-05 13:06:30
## 16: rda 2018-07-05 13:06:29
## 17: Cache 2018-07-05 13:06:29
## 18: numeric 2018-07-05 13:06:29
## 19: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 20: cec73d63ad3864af8bcd7efc5fae864d 2018-07-05 13:06:29
## 21: rnorm 2018-07-05 13:06:29
## 22: 688 2018-07-05 13:06:29
## 23: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 24: process_file 2018-07-05 13:06:29
## 25: process_group 2018-07-05 13:06:29
## 26: process_group.block 2018-07-05 13:06:29
## 27: call_block 2018-07-05 13:06:29
## 28: block_exec 2018-07-05 13:06:29
## 29: do.call 2018-07-05 13:06:29
## 30: n:4ae3e6b6364de42fdc243469d73448cc 2018-07-05 13:06:29
## 31: mean:c28b87a0be6a99966bdaa5e556974b43 2018-07-05 13:06:29
## 32: .FUN:7e9a928f110f80b3612e71883a6ec1f4 2018-07-05 13:06:29
## tagValue createdDate
showCache(tmpdir2) # 1 function call
## Cache size:
## Total (including Rasters): 12.2 Kb
## Selected objects (not including Rasters): 164 bytes
## artifact tagKey
## 1: 2509f70139c736598671529e31cada0b format
## 2: 2509f70139c736598671529e31cada0b name
## 3: 2509f70139c736598671529e31cada0b class
## 4: 2509f70139c736598671529e31cada0b date
## 5: 2509f70139c736598671529e31cada0b cacheId
## 6: 2509f70139c736598671529e31cada0b function
## 7: 2509f70139c736598671529e31cada0b object.size
## 8: 2509f70139c736598671529e31cada0b accessed
## 9: 2509f70139c736598671529e31cada0b otherFunctions
## 10: 2509f70139c736598671529e31cada0b otherFunctions
## 11: 2509f70139c736598671529e31cada0b otherFunctions
## 12: 2509f70139c736598671529e31cada0b otherFunctions
## 13: 2509f70139c736598671529e31cada0b otherFunctions
## 14: 2509f70139c736598671529e31cada0b otherFunctions
## 15: 2509f70139c736598671529e31cada0b preDigest
## 16: 2509f70139c736598671529e31cada0b preDigest
## tagValue createdDate
## 1: rda 2018-07-05 13:06:29
## 2: Cache 2018-07-05 13:06:29
## 3: numeric 2018-07-05 13:06:29
## 4: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 5: 19b808ac6871e0184e63c421a116cb61 2018-07-05 13:06:29
## 6: inner 2018-07-05 13:06:29
## 7: 688 2018-07-05 13:06:29
## 8: 2018-07-05 13:06:29 2018-07-05 13:06:29
## 9: process_file 2018-07-05 13:06:29
## 10: process_group 2018-07-05 13:06:29
## 11: process_group.block 2018-07-05 13:06:29
## 12: call_block 2018-07-05 13:06:29
## 13: block_exec 2018-07-05 13:06:29
## 14: do.call 2018-07-05 13:06:29
## 15: mean:c28b87a0be6a99966bdaa5e556974b43 2018-07-05 13:06:30
## 16: .FUN:56a1302d7ef43383766d7af6ca072c4e 2018-07-05 13:06:30
# userTags get appended
# all items have the outer tag propagate, plus inner ones only have inner ones
clearCache(tmpdir1)
outerTag <- "outerTag"
innerTag <- "innerTag"
inner <- function(mean) {
d <- 1
Cache(rnorm, n = 3, mean = mean, notOlderThan = Sys.time() - 1e5, userTags = innerTag)
}
outer <- function(n) {
Cache(inner, 0.1)
}
aa <- Cache(outer, n = 2, cacheRepo = tmpdir1, userTags = outerTag)
showCache(tmpdir1) # rnorm function has outerTag and innerTag, inner and outer only have outerTag
## Cache size:
## Total (including Rasters): 20.5 Kb
## Selected objects (not including Rasters): 491 bytes
## artifact tagKey
## 1: 53b155cdd05dd4c8c0c12fd1ac6f3817 format
## 2: 53b155cdd05dd4c8c0c12fd1ac6f3817 name
## 3: 53b155cdd05dd4c8c0c12fd1ac6f3817 class
## 4: 53b155cdd05dd4c8c0c12fd1ac6f3817 date
## 5: 53b155cdd05dd4c8c0c12fd1ac6f3817 cacheId
## 6: 53b155cdd05dd4c8c0c12fd1ac6f3817 outerTag
## 7: 53b155cdd05dd4c8c0c12fd1ac6f3817 function
## 8: 53b155cdd05dd4c8c0c12fd1ac6f3817 object.size
## 9: 53b155cdd05dd4c8c0c12fd1ac6f3817 accessed
## 10: 53b155cdd05dd4c8c0c12fd1ac6f3817 otherFunctions
## 11: 53b155cdd05dd4c8c0c12fd1ac6f3817 otherFunctions
## 12: 53b155cdd05dd4c8c0c12fd1ac6f3817 otherFunctions
## 13: 53b155cdd05dd4c8c0c12fd1ac6f3817 otherFunctions
## 14: 53b155cdd05dd4c8c0c12fd1ac6f3817 otherFunctions
## 15: 53b155cdd05dd4c8c0c12fd1ac6f3817 otherFunctions
## 16: 53b155cdd05dd4c8c0c12fd1ac6f3817 preDigest
## 17: 53b155cdd05dd4c8c0c12fd1ac6f3817 preDigest
## 18: d90eb10412dca4fe6f7ca34189baebe8 format
## 19: d90eb10412dca4fe6f7ca34189baebe8 name
## 20: d90eb10412dca4fe6f7ca34189baebe8 class
## 21: d90eb10412dca4fe6f7ca34189baebe8 date
## 22: d90eb10412dca4fe6f7ca34189baebe8 cacheId
## 23: d90eb10412dca4fe6f7ca34189baebe8 innerTag
## 24: d90eb10412dca4fe6f7ca34189baebe8 outerTag
## 25: d90eb10412dca4fe6f7ca34189baebe8 function
## 26: d90eb10412dca4fe6f7ca34189baebe8 object.size
## 27: d90eb10412dca4fe6f7ca34189baebe8 accessed
## 28: d90eb10412dca4fe6f7ca34189baebe8 otherFunctions
## 29: d90eb10412dca4fe6f7ca34189baebe8 otherFunctions
## 30: d90eb10412dca4fe6f7ca34189baebe8 otherFunctions
## 31: d90eb10412dca4fe6f7ca34189baebe8 otherFunctions
## 32: d90eb10412dca4fe6f7ca34189baebe8 otherFunctions
## 33: d90eb10412dca4fe6f7ca34189baebe8 otherFunctions
## 34: d90eb10412dca4fe6f7ca34189baebe8 preDigest
## 35: d90eb10412dca4fe6f7ca34189baebe8 preDigest
## 36: d90eb10412dca4fe6f7ca34189baebe8 preDigest
## 37: fe0e9711a58929b306091afc4cc17a9a format
## 38: fe0e9711a58929b306091afc4cc17a9a name
## 39: fe0e9711a58929b306091afc4cc17a9a class
## 40: fe0e9711a58929b306091afc4cc17a9a date
## 41: fe0e9711a58929b306091afc4cc17a9a cacheId
## 42: fe0e9711a58929b306091afc4cc17a9a outerTag
## 43: fe0e9711a58929b306091afc4cc17a9a function
## 44: fe0e9711a58929b306091afc4cc17a9a object.size
## 45: fe0e9711a58929b306091afc4cc17a9a accessed
## 46: fe0e9711a58929b306091afc4cc17a9a otherFunctions
## 47: fe0e9711a58929b306091afc4cc17a9a otherFunctions
## 48: fe0e9711a58929b306091afc4cc17a9a otherFunctions
## 49: fe0e9711a58929b306091afc4cc17a9a otherFunctions
## 50: fe0e9711a58929b306091afc4cc17a9a otherFunctions
## 51: fe0e9711a58929b306091afc4cc17a9a preDigest
## 52: fe0e9711a58929b306091afc4cc17a9a preDigest
## artifact tagKey
## tagValue createdDate
## 1: rda 2018-07-05 13:06:30
## 2: Cache 2018-07-05 13:06:30
## 3: numeric 2018-07-05 13:06:30
## 4: 2018-07-05 13:06:30 2018-07-05 13:06:30
## 5: 994d1330fbd961f795ab0dc508271963 2018-07-05 13:06:30
## 6: outerTag 2018-07-05 13:06:30
## 7: inner 2018-07-05 13:06:30
## 8: 688 2018-07-05 13:06:30
## 9: 2018-07-05 13:06:30 2018-07-05 13:06:30
## 10: process_file 2018-07-05 13:06:30
## 11: process_group 2018-07-05 13:06:30
## 12: process_group.block 2018-07-05 13:06:30
## 13: call_block 2018-07-05 13:06:30
## 14: block_exec 2018-07-05 13:06:30
## 15: do.call 2018-07-05 13:06:30
## 16: mean:c28b87a0be6a99966bdaa5e556974b43 2018-07-05 13:06:30
## 17: .FUN:b910401646b09073940de757678db03d 2018-07-05 13:06:30
## 18: rda 2018-07-05 13:06:30
## 19: Cache 2018-07-05 13:06:30
## 20: numeric 2018-07-05 13:06:30
## 21: 2018-07-05 13:06:30 2018-07-05 13:06:30
## 22: cec73d63ad3864af8bcd7efc5fae864d 2018-07-05 13:06:30
## 23: innerTag 2018-07-05 13:06:30
## 24: outerTag 2018-07-05 13:06:30
## 25: rnorm 2018-07-05 13:06:30
## 26: 688 2018-07-05 13:06:30
## 27: 2018-07-05 13:06:30 2018-07-05 13:06:30
## 28: process_file 2018-07-05 13:06:30
## 29: process_group 2018-07-05 13:06:30
## 30: process_group.block 2018-07-05 13:06:30
## 31: call_block 2018-07-05 13:06:30
## 32: block_exec 2018-07-05 13:06:30
## 33: do.call 2018-07-05 13:06:30
## 34: n:4ae3e6b6364de42fdc243469d73448cc 2018-07-05 13:06:30
## 35: mean:c28b87a0be6a99966bdaa5e556974b43 2018-07-05 13:06:30
## 36: .FUN:7e9a928f110f80b3612e71883a6ec1f4 2018-07-05 13:06:30
## 37: rda 2018-07-05 13:06:30
## 38: Cache 2018-07-05 13:06:30
## 39: numeric 2018-07-05 13:06:30
## 40: 2018-07-05 13:06:30 2018-07-05 13:06:30
## 41: 44f57deb36c53cd9c395e04c51fea77a 2018-07-05 13:06:30
## 42: outerTag 2018-07-05 13:06:30
## 43: outer 2018-07-05 13:06:30
## 44: 688 2018-07-05 13:06:30
## 45: 2018-07-05 13:06:30 2018-07-05 13:06:30
## 46: process_file 2018-07-05 13:06:30
## 47: process_group 2018-07-05 13:06:30
## 48: process_group.block 2018-07-05 13:06:30
## 49: call_block 2018-07-05 13:06:30
## 50: block_exec 2018-07-05 13:06:30
## 51: n:8128a6180a705341ab7c05cfa945edfb 2018-07-05 13:06:30
## 52: .FUN:62302feda89e19149a56ca40fde725e1 2018-07-05 13:06:30
## tagValue createdDate
Sometimes, it is not absolutely desirable to maintain the work flow intact because changes that are irrelevant to the analysis, such as changing messages sent to a user, may be changed, without a desire to rerun functions. The cacheId
argument is for this. Once a piece of code is run, then the cacheId
can be manually extracted (it is reported at the end of a Cache call) and manually placed in the code, passed in as, say, cacheId = "ad184ce64541972b50afd8e7b75f821b"
.
### cacheId
set.seed(1)
Cache(rnorm, 1, cacheRepo = tmpdir1)
## [1] -0.6264538
## attr(,"tags")
## [1] "cacheId:23dc247384c1b270f0d36de4bca1b138"
## attr(,"newCache")
## [1] TRUE
## attr(,"call")
## [1] ""
# manually look at output attribute which shows cacheId: ad184ce64541972b50afd8e7b75f821b
Cache(rnorm, 1, cacheRepo = tmpdir1, cacheId = "ad184ce64541972b50afd8e7b75f821b") # same value
## cacheId is not same as calculated hash. Manually searching for cacheId:ad184ce64541972b50afd8e7b75f821b
## [1] 0.1836433
## attr(,"tags")
## [1] "cacheId:ad184ce64541972b50afd8e7b75f821b"
## attr(,"newCache")
## [1] TRUE
## attr(,"call")
## [1] ""
# override even with different inputs:
Cache(rnorm, 2, cacheRepo = tmpdir1, cacheId = "ad184ce64541972b50afd8e7b75f821b")
## cacheId is not same as calculated hash. Manually searching for cacheId:ad184ce64541972b50afd8e7b75f821b
## loading cached result from previous rnorm call, adding to memoised copy
## [1] 0.1836433
## attr(,"tags")
## [1] "cacheId:ad184ce64541972b50afd8e7b75f821b"
## attr(,"newCache")
## [1] FALSE
## attr(,"call")
## [1] ""
## cleanup
unlink(c("filename.rda", "filename1.rda"))
Since the cache is simply an archivist
repository, all archivist
functions will work as is. In addition, there are several helpers in the reproducible
package, including showCache
, keepCache
and clearCache
that may be useful. Also, one can access cached items manually (rather than simply rerunning the same Cache
function again).
if (requireNamespace("archivist")) {
# get the RasterLayer that was produced with the gaussMap function:
mapHash <- unique(showCache(tmpDir, userTags = "projectRaster")$artifact)
map <- archivist::loadFromLocalRepo(md5hash = mapHash[1], repoDir = tmpDir, value = TRUE)
plot(map)
}
## Cache size:
## Total (including Rasters): 12.8 Kb
## Selected objects (not including Rasters): 800 bytes
## cleanup
unlink(dirname(tmpDir), recursive = TRUE)
In general, we feel that a liberal use of Cache
will make a re-usable and reproducible work flow. shiny
apps can be made, taking advantage of Cache
. Indeed, much of the difficulty in managing data sets and saving them for future use, can be accommodated by caching.
Cache(<functionName>, <other arguments>)
This will allow fine scale control of individual function calls.