knitr::opts_chunk$set( collapse = TRUE, comment = "#>", message = FALSE, warning = FALSE ) has_hdf5 <- requireNamespace("hdf5r", quietly = TRUE)
library(delarr)
delarr solve?delarr lets you write matrix pipelines as if everything were already in
memory while deferring the actual work until collect(). That matters when the
source matrix lives on disk, when you want to avoid intermediate allocations,
or when you need to stream the result directly into another backend.
This vignette covers one small lazy pipeline, one streaming write to HDF5, and
one custom backend. For chunk planning, profiling, and optional shared-memory
workers, see vignette("advanced", package = "delarr").
set.seed(1) mat <- matrix( rnorm(24), nrow = 6, ncol = 4, dimnames = list(paste0("sample_", 1:6), paste0("feature_", 1:4)) ) lazy_mean <- delarr(mat) |> d_center(dim = "rows") |> d_map(~ .x * 0.5) |> d_reduce(mean, dim = "rows") lazy_mean
Nothing has been materialized yet. The object is still a delarr, and the work
is only a recorded plan.
row_summary <- collect(lazy_mean, chunk_size = 2L) max(abs(row_summary))
After row-centering, every row has mean zero. The printed value is the largest absolute row mean after collection.
stopifnot( all(is.finite(row_summary)), all(abs(row_summary) < 1e-10) )
Scalars, row-sized vectors, and column-sized vectors stay lazy too. delarr
infers whether a vector should broadcast across rows or columns from its
length.
row_bias <- c(-1, 0, 1, 2, 3, 4) col_scale <- c(1, 0.5, 2, 1.5) broadcasted <- collect((delarr(mat) + row_bias) * col_scale, chunk_size = 2L) broadcasted[1:3, , drop = FALSE]
expected <- sweep(sweep(mat, 1L, row_bias, "+"), 2L, col_scale, "*") stopifnot(isTRUE(all.equal(broadcasted, expected)))
This length-based inference only works when the matrix is non-square, so that
nrow and ncol are distinct. For a square n-by-n matrix a bare
length-n vector is ambiguous --- its length matches both dimensions --- and
delarr resolves the tie to row-aligned (one value per row, broadcast
across the columns). This matches base R, where matrix + vector recycles the
vector down the columns and so also aligns element i to row i. delarr
emits a one-time warning at this point to flag the ambiguity:
sq <- matrix(1:9, 3, 3) biased <- delarr(sq) + c(10, 20, 30) #> Warning: Ambiguous broadcast: a length-3 vector against a square 3x3 #> matrix is interpreted as row-aligned (one value per row) ... collect(biased)
If you actually want column alignment on a square matrix you cannot express it with a bare vector; pass an explicit conformable matrix instead:
collect(delarr(sq) + matrix(c(10, 20, 30), 3, 3, byrow = TRUE))
Silence the warning with options(delarr.warn_ambiguous_broadcast = FALSE) once
you have confirmed the row-aligned default is what you intend.
delarr_hdf5() reads a dataset lazily, and hdf5_writer() lets you stream the
transformed result back to disk without materializing the full output matrix in
R. The HDF5 backend is optional: install the hdf5r package to enable it. The
code below runs only when hdf5r is available.
tf_in <- tempfile(fileext = ".h5") tf_out <- tempfile(fileext = ".h5") input <- matrix(runif(30), 5, 6) write_hdf5(input, tf_in, "X")
X <- delarr_hdf5(tf_in, "X") writer <- hdf5_writer(tf_out, "X_z", ncol = ncol(X), chunk = c(5L, 3L)) collect(X |> d_zscore(dim = "cols"), into = writer, chunk_size = 3L)
z <- read_hdf5(tf_out, "X_z") rbind( mean = round(colMeans(z), 6), sd = round(apply(z, 2L, stats::sd), 6) )
stopifnot( all(is.finite(z)), all(abs(colMeans(z)) < 1e-8), all(abs(apply(z, 2L, stats::sd) - 1) < 1e-8) ) unlink(c(tf_in, tf_out))
A custom backend only needs matrix dimensions and a pull() function that can
return arbitrary row and column slices. Here the backing store is just another
matrix, but the same pattern works for databases, APIs, or memory-mapped files.
source_mat <- matrix( seq_len(60), nrow = 10, ncol = 6, dimnames = list(paste0("row_", 1:10), paste0("col_", 1:6)) )
custom <- delarr_backend( nrow = nrow(source_mat), ncol = ncol(source_mat), pull = function(rows = NULL, cols = NULL) { if (is.null(rows)) rows <- seq_len(nrow(source_mat)) if (is.null(cols)) cols <- seq_len(ncol(source_mat)) source_mat[rows, cols, drop = FALSE] }, dimnames = dimnames(source_mat) ) custom_result <- custom[1:4, 2:5] |> d_map(~ .x^2) |> collect(chunk_size = 2L) custom_result
stopifnot(isTRUE(all.equal(custom_result, source_mat[1:4, 2:5]^2)))
Use collect() when you want to control chunk size or stream into a writer,
delarr_backend() when you need a custom backend, and
vignette("advanced", package = "delarr") for execution plans, streamed
multi-reducer summaries, block-wise workflows, delayed matrix products, and
optional shared-memory workers.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.