| Title: | Bioconductor DuckDB Integration and High-Level I/O |
|---|---|
| Description: | Integration package providing high-level Parquet I/O functions and optimized methods for single-cell analysis workflows using DuckDB-backed data structures. Includes readParquet and writeParquet functions for seamless serialization of SummarizedExperiment, SingleCellExperiment, MultiAssayExperiment, and MultiAssaySpatialExperiment objects, plus SQL-optimized implementations of scran and scuttle methods for variance modeling, marker detection, QC metrics, and normalization. This package brings together DuckDBDataFrame, DuckDBArray, DuckDBGRanges, and DuckDBSpatial (optional) for complete Bioconductor integration. |
| Authors: | Patrick Aboyoun [aut, cre], Genentech, Inc. [cph] |
| Maintainer: | Patrick Aboyoun <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.99.5 |
| Built: | 2026-07-23 11:32:39 UTC |
| Source: | https://github.com/BiocStaging/BiocDuckDB |
DuckDBDataFrame methods for MultiAssaySpatialExperiment spatial generics. Lazy layers delegate to DuckDBSpatial for SQL-backed spatial operations.
spatialOverlaps() returns the pairwise spatial-overlap result between
the lazy layers, spatialMatch() the matched table, and
spatialJoin() a sparse pair table (mapping row indices between layers)
when sparse = TRUE, or the joined table otherwise. All are computed
lazily over the DuckDB-backed layers.
spatialOverlaps(x, y, coords = NULL, geom = "geometry"):Test spatial overlap between lazy spatial layers via
layerSpatialOverlaps.
spatialMatch(x, table, coords, geom = "geometry", join = NULL):Match points or geometries to a table via
layerSpatialMatch.
spatialJoin(x, y, join = st_intersects, sparse = TRUE):Spatial join between lazy layers. When sparse = TRUE, returns a
sparse pair table via st_intersects_table;
otherwise uses st_join.
spatialOverlaps,
spatialMatch, and
spatialJoin for generic
documentation.
# Spatial predicates need DuckDB's optional spatial extension. Probe for it so # the example runs only where the extension can actually load; it is skipped # cleanly when offline or behind a firewall that blocks the extension # repository, or when DuckDBSpatial is not installed. spatial_ok <- requireNamespace("DuckDBSpatial", quietly = TRUE) && tryCatch({ DBI::dbGetQuery(DuckDBDataFrame::acquireDuckDBConn(), "SELECT ST_Area(ST_GeomFromText('POLYGON((0 0,0 1,1 1,1 0,0 0))'))") TRUE }, error = function(e) FALSE) if (spatial_ok) { df <- data.frame(id = 1:3, x = 1:3, y = 1:3) path <- tempfile(fileext = ".parquet") arrow::write_parquet(df, path) ddb <- DuckDBDataFrame(path, datacols = c("x", "y"), keycol = "id") polygon <- "POLYGON((0 0, 6 0, 6 6, 0 6, 0 0))" spatialOverlaps(ddb, polygon, coords = c("x", "y")) unlink(path) }# Spatial predicates need DuckDB's optional spatial extension. Probe for it so # the example runs only where the extension can actually load; it is skipped # cleanly when offline or behind a firewall that blocks the extension # repository, or when DuckDBSpatial is not installed. spatial_ok <- requireNamespace("DuckDBSpatial", quietly = TRUE) && tryCatch({ DBI::dbGetQuery(DuckDBDataFrame::acquireDuckDBConn(), "SELECT ST_Area(ST_GeomFromText('POLYGON((0 0,0 1,1 1,1 0,0 0))'))") TRUE }, error = function(e) FALSE) if (spatial_ok) { df <- data.frame(id = 1:3, x = 1:3, y = 1:3) path <- tempfile(fileext = ".parquet") arrow::write_parquet(df, path) ddb <- DuckDBDataFrame(path, datacols = c("x", "y"), keycol = "id") polygon <- "POLYGON((0 0, 6 0, 6 6, 0 6, 0 0))" spatialOverlaps(ddb, polygon, coords = c("x", "y")) unlink(path) }
The DuckDBDualSubset class conforms to the DualSubset class from SingleCellExperiment to provide lazy node-based subsetting for DuckDBSelfHits objects.
DuckDBDualSubset is a lightweight wrapper around DuckDBSelfHits
that provides node-based subsetting semantics required by SingleCellExperiment's
colPairs/rowPairs framework. Unlike the DualSubset class
which materializes data immediately upon subsetting, DuckDBDualSubset
delegates to the nodes slot in the wrapped DuckDBSelfHits object,
enabling lazy SQL-based edge filtering.
When a DuckDBDualSubset is subset via x[i], it delegates to
extractNODES(x@hits, i), which updates the nodes slot in the
DuckDBSelfHits object. Edge filtering is deferred until materialization
via SQL WHERE clauses (only edges where BOTH from and to are in the node subset
are retained).
The DuckDBDualSubset() constructor returns a DuckDBDualSubset object
wrapping a DuckDBSelfHits. length() returns the number of
nodes (an integer); subsetting ([) returns a DuckDBDualSubset, and the
[<- and c() methods return the updated DuckDBDualSubset.
DuckDBDualSubset(hits):Creates a DuckDBDualSubset object wrapping a DuckDBSelfHits object.
hitsA DuckDBSelfHits object containing the edge data.
length(x):Returns the number of nodes (nnode(x@hits)).
x[i]:Performs node-based subsetting by delegating to
extractNODES(x@hits, i). This updates the nodes slot
in the wrapped DuckDBSelfHits object and applies lazy SQL filtering
to retain only edges where BOTH endpoints are in the subset.
DuckDBDualSubset objects are typically created automatically when assigning
DuckDBSelfHits objects to colPairs/rowPairs:
Patrick Aboyoun
DuckDBSelfHits for the underlying edge storage class.
extractNODES for the node-based subsetting method.
library(SingleCellExperiment) sce <- SingleCellExperiment(assays = list(counts = matrix(1:50, 10, 5))) hits <- S4Vectors::SelfHits(from = 1:3, to = 2:4, nnode = 5L) tmp <- tempfile() writeParquet(hits, tmp) ddb_hits <- DuckDBSelfHits(tmp, from = "from", to = "to", nnode = 5L) colPair(sce, "knn") <- ddb_hits ds <- colPair(sce, "knn") length(ds) ds[1:3] unlink(tmp, recursive = TRUE)library(SingleCellExperiment) sce <- SingleCellExperiment(assays = list(counts = matrix(1:50, 10, 5))) hits <- S4Vectors::SelfHits(from = 1:3, to = 2:4, nnode = 5L) tmp <- tempfile() writeParquet(hits, tmp) ddb_hits <- DuckDBSelfHits(tmp, from = "from", to = "to", nnode = 5L) colPair(sce, "knn") <- ddb_hits ds <- colPair(sce, "knn") length(ds) ds[1:3] unlink(tmp, recursive = TRUE)
scran methods for DuckDBMatrix objects.
The DuckDBMatrix methods compute per-gene statistics using SQL aggregation, which is efficient for large disk-backed matrices:
For pairwiseTTests: per-group means and variances via SQL
For pairwiseBinom: per-group detection counts via SQL
The trend fitting and p-value computations are then performed in R using the standard scran functions.
For findMarkers, both test.type="t" and test.type="binom"
use optimized DuckDBMatrix implementations. The Wilcoxon test
(test.type="wilcox") falls back to the default method because it
requires pairwise cell comparisons that are not efficiently expressed in SQL.
These methods mirror their scran generics. correlatePairs()
returns a DataFrame of gene pairs with correlations and significance.
modelGeneVar(), modelGeneVarByPoisson(), and
modelGeneCV2() return a DataFrame of per-gene variance
statistics, with the fitted mean-variance trend in metadata().
pairwiseTTests() and pairwiseBinom() return a list with
statistics and pairs elements; findMarkers() and
scoreMarkers() return a List of per-group
DataFrames of marker statistics; and summaryMarkerStats()
returns a DataFrame summarizing marker statistics per group.
The following pairwise correlation methods have optimized DuckDBMatrix implementations:
correlatePairs(x, subset.row = NULL, pairings = NULL,
use.names = TRUE, BPPARAM = SerialParam()):Compute pairwise Pearson correlations between genes using sparse-aware
SQL aggregation. Only fill = 0 is supported.
subset.rowrows (genes) to correlate; required for >1000 genes
pairingsoptional matrix specifying specific gene pairs
use.nameswhether to use row names in output
The following variance modelling methods have optimized DuckDBMatrix implementations:
modelGeneVar(x, block = NULL, design = NULL, subset.row = NULL,
subset.fit = NULL, ..., equiweight = TRUE, method = "fisher",
BPPARAM = SerialParam()):Model the variance of log-expression profiles for each gene, decomposing it into technical and biological components. Uses SQL aggregation for computing per-gene means and variances.
blockfactor specifying blocking levels for each cell
designdesign matrix for blocking (not supported with block)
subset.rowrows for which to model the variance
subset.fitrows to be used for trend fitting
equiweightwhether blocks should be weighted equally
methodmethod for combining p-values across blocks
modelGeneVarByPoisson(x, size.factors = NULL, block = NULL,
design = NULL, subset.row = NULL, ..., equiweight = TRUE, method = "fisher",
BPPARAM = SerialParam()):Model the variance of log-expression profiles for each gene, using a Poisson-based trend to estimate technical variance. Uses SQL aggregation for computing per-gene means and variances.
size.factorsnumeric vector of size factors for normalization
blockfactor specifying blocking levels for each cell
designdesign matrix for blocking (not supported with block)
subset.rowrows for which to model the variance
equiweightwhether blocks should be weighted equally
methodmethod for combining p-values across blocks
modelGeneCV2(x, size.factors = NULL, block = NULL,
subset.row = NULL, subset.fit = NULL, ..., equiweight = TRUE, method = "fisher",
BPPARAM = SerialParam()):Model the squared coefficient of variation (CV2) of normalized expression for each gene, fitting a trend to account for the mean-CV2 relationship. Uses SQL aggregation for computing per-gene means and variances on size-factor normalized counts.
size.factorsnumeric vector of size factors for normalization
blockfactor specifying blocking levels for each cell
subset.rowrows for which to model the CV2
subset.fitrows to be used for trend fitting
equiweightwhether blocks should be weighted equally
methodmethod for combining p-values across blocks
The following marker gene detection methods have optimized DuckDBMatrix implementations:
pairwiseTTests(x, groups, block = NULL, design = NULL,
restrict = NULL, exclude = NULL, direction = c("any", "up", "down"),
lfc = 0, std.lfc = FALSE, log.p = FALSE, gene.names = NULL,
subset.row = NULL, BPPARAM = SerialParam()):Perform pairwise Welch t-tests between groups of cells. Uses SQL aggregation for computing per-gene, per-group means and variances.
groupsvector specifying group assignment for each cell
blockfactor specifying blocking levels
restrictcharacter vector of groups to restrict comparisons to
excludecharacter vector of groups to exclude from comparisons
directiondirection of log-fold changes to consider
lfclog-fold change threshold
std.lfcwhether to standardize log-fold changes
log.pwhether to return log-transformed p-values
gene.namescharacter vector of gene names for output
pairwiseBinom(x, groups, block = NULL, restrict = NULL,
exclude = NULL, direction = c("any", "up", "down"), threshold = 1e-8,
lfc = 0, log.p = FALSE, gene.names = NULL, subset.row = NULL,
BPPARAM = SerialParam()):Perform pairwise binomial tests between groups of cells. Uses SQL aggregation for computing per-gene, per-group detection counts.
groupsvector specifying group assignment for each cell
blockfactor specifying blocking levels
restrictcharacter vector of groups to restrict comparisons to
excludecharacter vector of groups to exclude from comparisons
directiondirection of log-fold changes to consider
thresholdexpression threshold for detection
lfclog-fold change threshold for proportions
log.pwhether to return log-transformed p-values
gene.namescharacter vector of gene names for output
findMarkers(x, groups, test.type = c("t", "wilcox", "binom"), ...,
pval.type = c("any", "some", "all"), min.prop = NULL, log.p = FALSE,
full.stats = FALSE, sorted = TRUE, row.data = NULL, add.summary = FALSE,
BPPARAM = SerialParam()):Find candidate marker genes for groups of cells.
groupsvector specifying group assignment for each cell
test.typetype of pairwise test ("t" and "binom" are optimized)
pval.typehow to combine p-values across pairwise tests
sortedwhether to sort results by significance
add.summarywhether to add summary statistics
scoreMarkers(x, groups, block = NULL, pairings = NULL, lfc = 0,
row.data = NULL, full.stats = FALSE, subset.row = NULL,
BPPARAM = SerialParam(), true.auc = FALSE):Compute effect size summary statistics for potential marker genes. Uses SQL aggregation for computing per-gene, per-group means, variances, and detection proportions.
AUC computation options: By default (true.auc = FALSE), AUC is
computed using a normal approximation which is fast (~97% correlation with scran).
Set true.auc = TRUE to compute true rank-based AUC using SQL window functions
(Wilcoxon rank-sum statistic). This is slower (~15-25x) but provides exact AUC
values identical to scran (100% correlation).
groupsvector specifying group assignment for each cell
blockfactor specifying blocking levels
pairingsspecification of pairwise comparisons to compute
lfclog-fold change threshold for effect sizes
row.dataadditional row data to include in output
full.statswhether to return full pairwise statistics
true.auclogical indicating whether to compute true rank-based AUC via SQL window functions (default FALSE uses normal approximation with ~97% correlation; TRUE gives 100% exact match but is ~15-25x slower)
summaryMarkerStats(x, groups, row.data = NULL, average = "mean",
BPPARAM = SerialParam()):Compute summary statistics for marker gene selection. Uses SQL aggregation for per-group means and detection proportions.
groupsvector specifying group assignment for each cell
row.dataadditional row data to include in output
averagetype of average to compute ("mean" or "median")
The scoreMarkers method for DuckDBMatrix provides two AUC computation modes:
Normal approximation (default, true.auc = FALSE):
where is the standard normal CDF. This is fast but may be inaccurate for
sparse/bimodal data.
True rank-based AUC (true.auc = TRUE):
Computes exact AUC via the Wilcoxon rank-sum statistic using SQL window functions:
where is the sum of ranks in group 1. This is ~15-25x slower than the
normal approximation but provides exact results identical to scran (correlation = 1.0).
Benchmarks (5000 genes x 5000 cells, 12 pairwise comparisons):
Default (normal approx): 0.68s, ~97% correlation with scran
true.auc = TRUE: 17.7s, 100% exact match with scran
The true AUC is preferred for:
Genes with bimodal expression (expressed vs. not expressed)
Sparse data with many zeros
Applications where exact effect sizes are important
Alternatively, use findMarkers(x, groups, test.type = "wilcox") for
rank-based p-values.
Patrick Aboyoun
DuckDBMatrix-class for the main class
DuckDBMatrix-scuttle for the scuttle methods
correlatePairs for the scran generic
modelGeneVar for the scran generic
modelGeneVarByPoisson for the scran generic
modelGeneCV2 for the scran generic
pairwiseTTests for the scran generic
pairwiseBinom for the scran generic
findMarkers for the scran generic
scoreMarkers for the scran generic
summaryMarkerStats for the scran generic
mat <- matrix(rpois(500, 5), 25, 20) dimnames(mat) <- list(paste0("G", 1:25), paste0("C", 1:20)) names(dimnames(mat)) <- c("index1", "index2") tmp <- tempfile() writeParquet(mat, tmp) pqmat <- DuckDBMatrix(tmp, datacol = "value", keycols = lapply(dimnames(mat), function(x) setNames(seq_along(x), x))) head(scran::modelGeneVarByPoisson(pqmat)) unlink(tmp, recursive = TRUE)mat <- matrix(rpois(500, 5), 25, 20) dimnames(mat) <- list(paste0("G", 1:25), paste0("C", 1:20)) names(dimnames(mat)) <- c("index1", "index2") tmp <- tempfile() writeParquet(mat, tmp) pqmat <- DuckDBMatrix(tmp, datacol = "value", keycols = lapply(dimnames(mat), function(x) setNames(seq_along(x), x))) head(scran::modelGeneVarByPoisson(pqmat)) unlink(tmp, recursive = TRUE)
scuttle methods for DuckDBMatrix objects.
These methods mirror their scuttle generics.
perCellQCMetrics() and perFeatureQCMetrics() return a
DataFrame of QC metrics. librarySizeFactors(),
geometricSizeFactors(), and calculateAverage() return a numeric
vector. normalizeCounts(), calculateCPM(), and
calculateTPM() return a matrix-like object of transformed values, and
numDetectedAcrossFeatures() and sumCountsAcrossFeatures() a
matrix-like object of per-group summaries. summarizeAssayByGroup()
returns a SummarizedExperiment of per-group assay summaries.
The following QC metrics methods have optimized DuckDBMatrix implementations:
perCellQCMetrics(x, subsets = NULL, percent.top = integer(0),
threshold = 0, ..., flatten = TRUE):Compute per-cell quality control metrics for a count matrix.
subsetsnamed list of vectors specifying feature subsets (e.g., mitochondrial genes). Each vector can be character (feature names), logical, or integer (indices). Returns sum, detected, and percent for each subset.
percent.topinteger vector specifying the sizes of the top
sets of high-abundance genes (e.g., c(50, 100, 200)). For each
size, computes the percentage of total counts assigned to the most
highly expressed genes in each cell. Uses SQL window functions for
efficient computation.
thresholdnumeric scalar specifying the detection threshold
flattenlogical scalar indicating whether nested DataFrames
in the output should be flattened. If TRUE (default), subset
and percent.top statistics are returned as top-level columns
(e.g., subsets_Mito_sum, percent.top_50).
If FALSE, nested DataFrames/matrices are returned.
perFeatureQCMetrics(x, subsets = NULL, threshold = 0, ..., flatten = TRUE):Compute per-feature quality control metrics for a count matrix.
subsetsnamed list of vectors specifying cell subsets (e.g., control wells). Each vector can be character (cell names), logical, or integer (indices). Returns mean, detected, and ratio for each subset.
thresholdnumeric scalar specifying the detection threshold
flattenlogical scalar indicating whether nested DataFrames
in the output should be flattened. If TRUE (default), subset
statistics are returned as top-level columns (e.g., subsets_SetA_mean).
If FALSE, a nested subsets DataFrame is returned.
The following normalization methods have optimized DuckDBMatrix implementations:
librarySizeFactors(x):Define per-cell size factors from the library sizes.
geometricSizeFactors(x, pseudo.count = 1):Define per-cell size factors from the geometric mean of counts per cell.
pseudo.countnumeric scalar specifying the pseudo-count to add during log-transformation
normalizeCounts(x, size.factors = NULL, log = TRUE, transform = c("log", "none", "asinh"),
pseudo.count = 1, center.size.factors = TRUE, subset.row = NULL, ...):Normalizes counts by dividing by size factors.
size.factorsnumeric vector of cell-specific size factors
loglogical scalar indicating whether normalized values should be log2-transformed
transformstring specifying the transformation to apply to the normalized expression values.
pseudo.countnumeric scalar specifying the pseudo-count
to add when transform = "log"
center.size.factorslogical scalar indicating whether size factors should be centered at unity before being used
subset.rowvector specifying the subset of rows of x for which to return normalized values
calculateTPM(x, lengths = NULL, ...):Calculates transcripts per million using SQL-optimized row-wise sweep.
lengthsNumeric vector of gene lengths.
calculateCPM(x, size.factors = NULL, subset.row = NULL, ...):Calculates counts per million using DelayedArray-safe sweep operations.
size.factorsnumeric vector containing size factors to adjust the library sizes. If NULL, library sizes are used directly.
subset.rowvector specifying the subset of rows of x for which to return normalized values
calculateAverage(x, size.factors = NULL, subset.row = NULL, BPPARAM = SerialParam(), ...):Calculates average counts per feature using DelayedArray-safe sweep operations.
size.factorsnumeric vector containing size factors. If NULL, library size factors are computed automatically.
subset.rowvector specifying the subset of rows of x for which to return averages
BPPARAMBiocParallelParam object for parallel computation
The following normalization methods will dispatch to optimized
DuckDBMatrix implementations of normalizeCounts,
librarySizeFactors, calculateCPM, calculateAverage,
and calculateTPM.
The following aggregation methods have optimized DuckDBMatrix implementations that use SQL GROUP BY operations for efficient pseudo-bulk analysis:
numDetectedAcrossFeatures(x, ids, subset.row = NULL, subset.col = NULL,
average = FALSE, threshold = 0, ...):Count detected features across feature sets for each cell.
idsfactor or list specifying feature groupings
averagelogical indicating whether to compute the proportion
thresholdthreshold for detection
sumCountsAcrossFeatures(x, ids, subset.row = NULL, subset.col = NULL,
average = FALSE, ...):Sum counts across feature sets for each cell.
idsfactor or list specifying feature groupings
averagelogical indicating whether to compute the average
summarizeAssayByGroup(x, ids, subset.row = NULL, subset.col = NULL,
statistics = c("mean", "sum", "num.detected", "prop.detected"),
store.number = "ncells", threshold = 0, ...):From an assay matrix, compute summary statistics for groups of cells.
idsfactor specifying the group for each cell
statisticscharacter vector of statistics to compute
thresholdthreshold for detection
Patrick Aboyoun
DuckDBMatrix-class for the main class
DuckDBArray-matrixStats for the matrixStats methods
perCellQCMetrics for the scuttle generic
perFeatureQCMetrics for the scuttle generic
librarySizeFactors for the scuttle generic
normalizeCounts for the scuttle generic
calculateCPM for the scuttle generic
calculateAverage for the scuttle generic
calculateTPM for the scuttle generic
numDetectedAcrossFeatures for the scuttle generic
sumCountsAcrossFeatures for the scuttle generic
summarizeAssayByGroup for the scuttle generic
mat <- matrix(rpois(500, 5), 25, 20) dimnames(mat) <- list(paste0("G", 1:25), paste0("C", 1:20)) names(dimnames(mat)) <- c("index1", "index2") tmp <- tempfile() writeParquet(mat, tmp) pqmat <- DuckDBMatrix(tmp, datacol = "value", keycols = lapply(dimnames(mat), function(x) setNames(seq_along(x), x))) head(librarySizeFactors(pqmat)) unlink(tmp, recursive = TRUE)mat <- matrix(rpois(500, 5), 25, 20) dimnames(mat) <- list(paste0("G", 1:25), paste0("C", 1:20)) names(dimnames(mat)) <- c("index1", "index2") tmp <- tempfile() writeParquet(mat, tmp) pqmat <- DuckDBMatrix(tmp, datacol = "value", keycols = lapply(dimnames(mat), function(x) setNames(seq_along(x), x))) head(librarySizeFactors(pqmat)) unlink(tmp, recursive = TRUE)
Joins the observations recorded in spatialMap to the rows of the
spatial layer they reference, through the full junction key
(assay, colname, element_type, region,
instance_id). Resolves a single (element_type, region) layer
(inferred from the filtered spatialMap when unambiguous, else
specify); the join is pushed to DuckDB and returned as a lazy
DuckDBDataFrame with the assay identity columns plus the
layer's coordinate columns.
linkSpatialMap( mase, assay = NULL, element_type = NULL, region = NULL, conn = acquireDuckDBConn() )linkSpatialMap( mase, assay = NULL, element_type = NULL, region = NULL, conn = acquireDuckDBConn() )
mase |
|
assay, element_type, region
|
Optional filters selecting the spatialMap rows (and thus the single layer) to link. |
conn |
A DuckDB connection (default the shared BiocDuckDB connection). |
A lazy DuckDBDataFrame of linked observations.
mase <- readParquet(system.file("extdata", "spatial_mase", package = "BiocDuckDB")) linkSpatialMap(mase, assay = "assay1")mase <- readParquet(system.file("extdata", "spatial_mase", package = "BiocDuckDB")) linkSpatialMap(mase, assay = "assay1")
Lazy MultiAssaySpatialExperiment methods that keep spatial layers as DuckDBDataFrame objects when possible. In-memory MASE defaults are used when no lazy layers are present.
readParquetForMASE() returns the parsed table — a lazy
DuckDBDataFrame for large files, or an in-memory object for small ones
— and readGeoParquetForMASE() returns a geometry-bearing object (an
sf data frame). annotateWithRegions(),
subsetByBoundingBox(), and subsetByPolygon() return a
MultiAssaySpatialExperiment (annotated or spatially subset).
readParquetForMASE(file_path, col_select = NULL, ...):Files larger than 50 MB are read as lazy DuckDBDataFrame objects;
smaller files use the in-memory MultiAssaySpatialExperiment method.
readGeoParquetForMASE(file_path, ...):Uses readGeoParquet when DuckDBSpatial
is installed; otherwise falls back to sfarrow.
annotateWithRegions(x, points, shapes, ...):Annotate points with shape regions using lazy spatialMatch.
subsetByBoundingBox(x, xmin, xmax, ymin, ymax, ...):Subset by bounding box with lazy layer filtering when applicable.
subsetByPolygon(x, polygon, ...):Subset by polygon with lazy layer filtering when applicable.
readParquetForMASE,
readGeoParquetForMASE,
annotateWithRegions,
subsetByBoundingBox, and
subsetByPolygon.
# subsetByBoundingBox on a DuckDB-backed layer uses DuckDB's optional spatial # extension; probe for it so the example is skipped cleanly where the # extension cannot load (offline / firewalled repository / not installed). spatial_ok <- requireNamespace("MultiAssaySpatialExperiment", quietly = TRUE) && requireNamespace("sf", quietly = TRUE) && requireNamespace("DuckDBSpatial", quietly = TRUE) && tryCatch({ DBI::dbGetQuery(DuckDBDataFrame::acquireDuckDBConn(), "SELECT ST_Area(ST_GeomFromText('POLYGON((0 0,0 1,1 1,1 0,0 0))'))") TRUE }, error = function(e) FALSE) if (spatial_ok) { pts <- S4Vectors::DataFrame( x = 1:3, y = 1:3, instance_id = c("A", "B", "C")) mat <- matrix(1:9, 3, 3, dimnames = list(paste0("G", 1:3), c("A", "B", "C"))) mase <- MultiAssaySpatialExperiment::MultiAssaySpatialExperiment( experiments = MultiAssayExperiment::ExperimentList(rna = mat), colData = S4Vectors::DataFrame(row.names = "s1"), sampleMap = S4Vectors::DataFrame( assay = "rna", primary = "s1", colname = c("A", "B", "C")), points = MultiAssaySpatialExperiment::PointsLayerList(centroids = pts)) tmp <- tempfile() writeParquet(mase, tmp) mase2 <- readParquet(tmp) subsetByBoundingBox(mase2, xmin = 1, xmax = 3, ymin = 1, ymax = 3) unlink(tmp, recursive = TRUE) }# subsetByBoundingBox on a DuckDB-backed layer uses DuckDB's optional spatial # extension; probe for it so the example is skipped cleanly where the # extension cannot load (offline / firewalled repository / not installed). spatial_ok <- requireNamespace("MultiAssaySpatialExperiment", quietly = TRUE) && requireNamespace("sf", quietly = TRUE) && requireNamespace("DuckDBSpatial", quietly = TRUE) && tryCatch({ DBI::dbGetQuery(DuckDBDataFrame::acquireDuckDBConn(), "SELECT ST_Area(ST_GeomFromText('POLYGON((0 0,0 1,1 1,1 0,0 0))'))") TRUE }, error = function(e) FALSE) if (spatial_ok) { pts <- S4Vectors::DataFrame( x = 1:3, y = 1:3, instance_id = c("A", "B", "C")) mat <- matrix(1:9, 3, 3, dimnames = list(paste0("G", 1:3), c("A", "B", "C"))) mase <- MultiAssaySpatialExperiment::MultiAssaySpatialExperiment( experiments = MultiAssayExperiment::ExperimentList(rna = mat), colData = S4Vectors::DataFrame(row.names = "s1"), sampleMap = S4Vectors::DataFrame( assay = "rna", primary = "s1", colname = c("A", "B", "C")), points = MultiAssaySpatialExperiment::PointsLayerList(centroids = pts)) tmp <- tempfile() writeParquet(mase, tmp) mase2 <- readParquet(tmp) subsetByBoundingBox(mase2, xmin = 1, xmax = 3, ymin = 1, ymax = 3) unlink(tmp, recursive = TRUE) }
Reads a parquet representation of various Bioconductor objects from disk
using the Frictionless Data Package metadata model, reconstructing them as
DuckDB-backed objects. This function is the counterpart to
writeParquet and supports reading SummarizedExperiment,
RangedSummarizedExperiment, SingleCellExperiment, and
MultiAssayExperiment objects that have been written to parquet format
with comprehensive metadata.
readParquet( path, package = .readDatapackage(path), model = package[["model"]], ... )readParquet( path, package = .readDatapackage(path), model = package[["model"]], ... )
path |
A character string specifying the path to the directory
containing the parquet files. The directory must contain a
|
package |
A list containing the parsed |
model |
A character string identifying the package schema — which
container class to reconstruct. Defaults to |
... |
Additional arguments passed to internal helper functions. |
This function reads parquet files that were created by
writeParquet using the Frictionless Data Package metadata model
and reconstructs the original Bioconductor object structure. This function
supports different object types through a switch statement that routes to
appropriate helper functions:
SummarizedExperiment objects: Reads feature data, sample data,
and assay data from separate parquet files, reconstructing the object with
DuckDB-backed components. For ranged experiments, genomic coordinates are
reconstructed as DuckDBGRanges objects.
SingleCellExperiment objects: Extends SummarizedExperiment
functionality by also reading reduced dimensions, loadings, alternative
experiments, row / column tables, and row / column pairings from their
respective parquet files.
MultiAssayExperiment objects: Reads multiple experiments
along with sample data and sample mapping information, reconstructing the
multi-experiment structure.
This function uses the Frictionless Data Package metadata to understand the
structure of the data, including resource schemas with field definitions,
primary keys, and semantic meanings (sequence_name, start, end, strand for
genomic data) encoded in the datapackage.json file.
A Bioconductor object of the appropriate class, reconstructed using DuckDB backend classes:
DuckDBMatrix objects for assay data
DuckDBDataFrame objects for row and column metadata
DuckDBGRanges objects for genomic ranges
DuckDBGRangesList objects for grouped genomic ranges
DuckDBSelfHits objects for graph edge lists
DuckDBDualSubset objects for pairwise graphs (wrapping DuckDBSelfHits)
GenomicRangesRead with genomic coordinates (seqnames, start,
end, strand) and optional metadata columns.
GenomicRangesListRead with genomic coordinates and metadata columns.
DuckDBSelfHitsGraph edge lists with from, to columns and optional
metadata. Node count (nnode) is stored in the schema
graphEdges property.
SummarizedExperimentFeature metadata from features/, sample metadata from
samples/, and assays from flat assay_<name>/ directories.
Any complex objects stored in metadata() are written to
unbound_<name>/ directories and restored on read.
RangedSummarizedExperimentAs SummarizedExperiment, with rowRanges reconstructed as a
DuckDBGRanges from features/.
SingleCellExperimentExtends SummarizedExperiment with: reduced dimensions from
sample_embeddings/; row loadings from feature_embeddings/;
alternative experiments from experiment_<name>/ directories;
row/column tables from flat feature_table_<name>/ and
sample_table_<name>/ directories; row/column pairwise graphs from
flat feature_graph_<name>/ and sample_graph_<name>/
directories.
MultiAssayExperimentExperiments written directly to root as experiment_<name>/
directories (flattened). Each SummarizedExperiment has its own
datapackage.json (layout = "nested_experiment").
Also includes subjects (subject metadata) and sample_map
(subject-to-sample mapping) as unbound resources.
MultiAssaySpatialExperimentExtends MultiAssayExperiment with spatial points from flat
sample_points_<name>/ directories, shapes from flat
sample_shapes_<name>/ directories, image metadata from
img_data/, and spatial mapping from spatial_map/.
Requires the MultiAssaySpatialExperiment package.
This function reads data packages that follow the Frictionless Data Package
specification (extended by the BiocDuckDB profile), parsing the
datapackage.json metadata file. Dispatch operates at two levels:
Package level (root datapackage.json):
model — which container class to reconstruct; absent means
SimpleList fallback.
annotations — non-relational metadata from metadata(x)
(scalars, vectors, 1-D arrays, JSON-safe nested lists). Tabular items
are referenced via parquet_ref stubs pointing at unbound
Parquet sidecars; mixed nested lists use a nested_mapping wrapper.
Resource level (entries in resources array):
dimension — biological axis ("feature",
"sample", "crossed", "unbound").
layout — physical storage pattern (e.g.,
"data_frame", "coord_array", "graph_edges",
"nested_experiment"); routes each resource to the
appropriate low-level reader via .readParquetResource.
schema — field definitions, primary keys, and BiocDuckDB
annotations (genomicCoords, graphEdges, arrayItem).
Patrick Aboyoun
writeParquet for writing Bioconductor objects to parquet
DuckDBMatrix for matrix storage
DuckDBDataFrame for metadata storage
DuckDBGRanges for genomic ranges
DuckDBGRangesList for grouped genomic ranges
DuckDBSelfHits for graph edge lists
se <- SummarizedExperiment::SummarizedExperiment( assays = list(counts = matrix(rpois(500, 5), 25, 20)), colData = S4Vectors::DataFrame(sample = rep(c("A", "B"), each = 10))) tmpdir <- tempfile() writeParquet(se, tmpdir) se2 <- readParquet(tmpdir) class(se2) dim(se2) unlink(tmpdir, recursive = TRUE)se <- SummarizedExperiment::SummarizedExperiment( assays = list(counts = matrix(rpois(500, 5), 25, 20)), colData = S4Vectors::DataFrame(sample = rep(c("A", "B"), each = 10))) tmpdir <- tempfile() writeParquet(se, tmpdir) se2 <- readParquet(tmpdir) class(se2) dim(se2) unlink(tmpdir, recursive = TRUE)
Methods to get or set nested sample-level tables in a
SingleCellExperiment object. These tables enable storage of
multi-column relational data associated with samples (cells/perturbations),
which would otherwise require nested DataFrames in colData(). By
extracting nested tables into a separate slot, they can be serialized to
independent Parquet tables for efficient querying.
x |
A SingleCellExperiment object. |
type |
String or integer scalar specifying the name or index of the table to get or set. |
withDimnames |
Logical scalar indicating whether row names should be
extracted from (getters) or set to (setters) the column names of |
... |
Additional arguments, currently ignored. |
value |
For the getter, a DataFrame-like object with number of rows
equal to |
Sample tables (colTables) are stored in
int_colData(x)$colTables and provide a mechanism to unnest complex
relational data from colData(). This is particularly useful when
sample metadata contains multi-valued properties or hierarchical structures
that are difficult to query when embedded as nested DataFrames.
Common use cases include:
Patient metadata: multiple disease diagnoses, treatment histories, or demographic details per sample
Experimental metadata: replicate-level measurements, batch details, or quality control metrics
Cell lineage: developmental trajectories or cell state transitions
Each table is a DataFrame with ncol(x) rows, maintaining
alignment with samples. Tables are automatically subset when the parent
SingleCellExperiment is subset by columns.
For colTable, a DataFrame containing sample-level relational data.
For colTables, a SimpleList of such DataFrames.
For colTableNames, a character vector of table names.
For all setters, x is returned with the modified tables or names.
In the following examples, x is a SingleCellExperiment
object.
colTable(x, type, withDimnames=TRUE):Retrieves a DataFrame containing sample-level relational data. type
is either a string specifying the name of the table in x to retrieve,
or a numeric scalar specifying the index of the desired table.
If withDimnames=TRUE, row names of the output DataFrame are replaced
with the column names of x.
colTableNames(x):Returns a character vector containing the names of all tables in x.
This is guaranteed to be of the same length as the number of tables, though
the names may not be unique.
colTables(x, withDimnames=TRUE):Returns a named SimpleList of DataFrames containing one or more
tables. Each table has the same number of rows as ncol(x).
If withDimnames=TRUE, row names of each DataFrame are replaced with
the column names of x.
colTable(x, type, withDimnames=TRUE) <- value will add or replace a
table in a SingleCellExperiment object x.
The value of type determines how the table is added or replaced:
If type is a numeric scalar, it must be within the range of
existing tables, and value will be assigned to the table at that
index.
If type is a string and a table exists with this name,
value is assigned to that table. Otherwise a new table with this
name is appended to the existing list of tables.
value is expected to be a DataFrame or data.frame-like object with
number of rows equal to ncol(x).
If withDimnames=TRUE, row names of value are set to
colnames(x).
In the following examples, x is a SingleCellExperiment
object.
colTables(x, withDimnames=TRUE) <- value:Replaces all tables in x with those in value.
The latter should be a list-like object containing any number of DataFrames
or data.frame-like objects with number of rows equal to ncol(x).
If value is named, those names will be used to name the tables in
x.
If value is a Annotated object, any
metadata will be retained in colTables(x).
If value is a Vector object, any mcols
will also be retained.
If withDimnames=TRUE, row names in each entry of value are set
to colnames(x).
colTableNames(x) <- value:Replaces all names for tables in x with a character vector
value. This should be of length equal to the number of tables
currently in x.
Patrick Aboyoun
colData for simple sample metadata.
rowTables for feature-level nested tables.
int_colData for the internal column
metadata storage.
library(SingleCellExperiment) # Create example SCE sce <- SingleCellExperiment( assays = list(counts = matrix(rpois(1000, 5), 100, 10)) ) rownames(sce) <- paste0("Gene", 1:100) colnames(sce) <- paste0("Cell", 1:10) # Add a table of patient disease history (multiple diseases per patient) diseases <- DataFrame( disease_id = paste0("MONDO:", sample(100000:999999, 10)), disease_label = paste0("Disease_", 1:10), onset_age = sample(20:80, 10, replace = TRUE) ) colTable(sce, "diseases") <- diseases # Add a table of per-sample QC metrics qc_metrics <- DataFrame( metric_name = rep(c("n_genes", "n_counts", "pct_mito"), length.out = 10), value = runif(10, 1000, 5000), passed = sample(c(TRUE, FALSE), 10, replace = TRUE) ) colTable(sce, "qc") <- qc_metrics # Retrieve all tables all_tables <- colTables(sce) names(all_tables) # Retrieve specific table by name dis <- colTable(sce, "diseases") dim(dis) # 10 samples × 3 columns # Retrieve by index qc <- colTable(sce, 2) # Get table names colTableNames(sce) # Subset SCE - tables are automatically subset sce_sub <- sce[, 1:5] dim(colTable(sce_sub, "diseases")) # 5 samples × 3 columnslibrary(SingleCellExperiment) # Create example SCE sce <- SingleCellExperiment( assays = list(counts = matrix(rpois(1000, 5), 100, 10)) ) rownames(sce) <- paste0("Gene", 1:100) colnames(sce) <- paste0("Cell", 1:10) # Add a table of patient disease history (multiple diseases per patient) diseases <- DataFrame( disease_id = paste0("MONDO:", sample(100000:999999, 10)), disease_label = paste0("Disease_", 1:10), onset_age = sample(20:80, 10, replace = TRUE) ) colTable(sce, "diseases") <- diseases # Add a table of per-sample QC metrics qc_metrics <- DataFrame( metric_name = rep(c("n_genes", "n_counts", "pct_mito"), length.out = 10), value = runif(10, 1000, 5000), passed = sample(c(TRUE, FALSE), 10, replace = TRUE) ) colTable(sce, "qc") <- qc_metrics # Retrieve all tables all_tables <- colTables(sce) names(all_tables) # Retrieve specific table by name dis <- colTable(sce, "diseases") dim(dis) # 10 samples × 3 columns # Retrieve by index qc <- colTable(sce, 2) # Get table names colTableNames(sce) # Subset SCE - tables are automatically subset sce_sub <- sce[, 1:5] dim(colTable(sce_sub, "diseases")) # 5 samples × 3 columns
Methods to get or set feature-level loading matrices in a SingleCellExperiment object. These matrices represent per-feature contributions to latent factors, embeddings, or statistical models, where feature identity (e.g., gene names) carries semantic meaning. Each row of a loading matrix corresponds to a row (feature) of the SingleCellExperiment object.
x |
A SingleCellExperiment object. |
type |
String or integer scalar specifying the name or index of the loading result to get or set. |
withDimnames |
Logical scalar indicating whether row names should be
extracted from (getters) or set to (setters) the row names of |
... |
Additional arguments, currently ignored. |
value |
For the getter, a matrix-like object with number of rows equal
to |
Feature loadings (rowLoadings) are stored in
int_elementMetadata(x)$rowLoadings and represent the AnnData
.varm equivalent in R. Unlike sample embeddings (reducedDims)
where dimension labels are arbitrary coordinates, loading matrices have
meaningful row names that identify which features contribute to each
latent dimension.
Common use cases include:
PCA loadings: which genes load on each principal component
Factor analysis: gene weights for latent factors
Statistical summaries: per-gene effect sizes or log fold changes across conditions
Loading matrices are automatically subset when the parent SingleCellExperiment is subset by rows, maintaining alignment between features and their loadings.
For rowLoading, a matrix containing loading values for features (rows)
and dimensions (columns).
For rowLoadings, a SimpleList of such matrices.
For rowLoadingNames, a character vector of loading names.
For all setters, x is returned with the modified loading results or
names.
In the following examples, x is a SingleCellExperiment
object.
rowLoading(x, type, withDimnames=TRUE):Retrieves a matrix containing feature loadings (rows) for each latent
dimension (columns). type is either a string specifying the name of
the loading result in x to retrieve, or a numeric scalar specifying
the index of the desired result.
If withDimnames=TRUE, row names of the output matrix are replaced with
the row names of x.
rowLoadingNames(x):Returns a character vector containing the names of all loading results in
x. This is guaranteed to be of the same length as the number of
results, though the names may not be unique.
rowLoadings(x, withDimnames=TRUE):Returns a named SimpleList of matrices containing one or more
loading results. Each result is a matrix with the same number of rows as
nrow(x).
If withDimnames=TRUE, row names of each matrix are replaced with the
row names of x.
rowLoading(x, type, withDimnames=TRUE) <- value will add or replace a
loading result in a SingleCellExperiment object x.
The value of type determines how the result is added or replaced:
If type is a numeric scalar, it must be within the range of
existing results, and value will be assigned to the result at that
index.
If type is a string and a result exists with this name,
value is assigned to that result. Otherwise a new result with this
name is appended to the existing list of results.
value is expected to be a matrix or matrix-like object with number of
rows equal to nrow(x).
If withDimnames=TRUE, row names of value are set to
rownames(x).
In the following examples, x is a SingleCellExperiment
object.
rowLoadings(x, withDimnames=TRUE) <- value:Replaces all loading results in x with those in value.
The latter should be a list-like object containing any number of matrices or
matrix-like objects with number of rows equal to nrow(x).
If value is named, those names will be used to name the loading
results in x.
If value is a Annotated object, any
metadata will be retained in rowLoadings(x).
If value is a Vector object, any mcols
will also be retained.
If withDimnames=TRUE, row names in each entry of value are set
to rownames(x).
rowLoadingNames(x) <- value:Replaces all names for loading results in x with a character vector
value. This should be of length equal to the number of results
currently in x.
Patrick Aboyoun
reducedDims for sample-level embeddings
(observation matrices).
int_elementMetadata for the internal row
metadata storage.
library(SingleCellExperiment) # Create example SCE with perturbation data sce <- SingleCellExperiment( assays = list(beta_Z = matrix(rnorm(1000), 100, 10)) ) rownames(sce) <- paste0("Gene", 1:100) colnames(sce) <- paste0("Pert", 1:10) # Add PCA loadings (genes × components) pca_loadings <- matrix(rnorm(100 * 5), 100, 5) rownames(pca_loadings) <- rownames(sce) colnames(pca_loadings) <- paste0("PC", 1:5) rowLoading(sce, "PCA") <- pca_loadings # Add factor analysis loadings (genes × factors) factor_loadings <- matrix(rnorm(100 * 20), 100, 20) rownames(factor_loadings) <- rownames(sce) colnames(factor_loadings) <- paste0("Factor", 1:20) rowLoading(sce, "FactorAnalysis") <- factor_loadings # Retrieve all loadings all_loadings <- rowLoadings(sce) names(all_loadings) # Retrieve specific loading by name pca <- rowLoading(sce, "PCA") dim(pca) # 100 genes × 5 components # Retrieve by index factors <- rowLoading(sce, 2) # Get loading names rowLoadingNames(sce) # Subset SCE - loadings are automatically subset sce_sub <- sce[1:50, ] dim(rowLoading(sce_sub, "PCA")) # 50 genes × 5 componentslibrary(SingleCellExperiment) # Create example SCE with perturbation data sce <- SingleCellExperiment( assays = list(beta_Z = matrix(rnorm(1000), 100, 10)) ) rownames(sce) <- paste0("Gene", 1:100) colnames(sce) <- paste0("Pert", 1:10) # Add PCA loadings (genes × components) pca_loadings <- matrix(rnorm(100 * 5), 100, 5) rownames(pca_loadings) <- rownames(sce) colnames(pca_loadings) <- paste0("PC", 1:5) rowLoading(sce, "PCA") <- pca_loadings # Add factor analysis loadings (genes × factors) factor_loadings <- matrix(rnorm(100 * 20), 100, 20) rownames(factor_loadings) <- rownames(sce) colnames(factor_loadings) <- paste0("Factor", 1:20) rowLoading(sce, "FactorAnalysis") <- factor_loadings # Retrieve all loadings all_loadings <- rowLoadings(sce) names(all_loadings) # Retrieve specific loading by name pca <- rowLoading(sce, "PCA") dim(pca) # 100 genes × 5 components # Retrieve by index factors <- rowLoading(sce, 2) # Get loading names rowLoadingNames(sce) # Subset SCE - loadings are automatically subset sce_sub <- sce[1:50, ] dim(rowLoading(sce_sub, "PCA")) # 50 genes × 5 components
Specialized setter methods for colPairs and rowPairs that
automatically wrap DuckDBSelfHits objects in
DuckDBDualSubset containers. This enables lazy node-based
subsetting when a SingleCellExperiment is subset.
x |
A SingleCellExperiment object. |
type |
String or integer scalar specifying the name or index of the pairing to replace. |
... |
Additional arguments passed to the default setter. |
value |
A DuckDBSelfHits object to be stored as a column/row pairing. This will be automatically wrapped in a DuckDBDualSubset. |
These methods extend the default colPairs<- and rowPairs<-
setters from SingleCellExperiment to recognize DuckDBSelfHits objects
and wrap them in DuckDBDualSubset containers. This ensures that when
a SingleCellExperiment is subset (e.g., sce[, 1:100]), the wrapped
DuckDBSelfHits objects are lazily filtered via SQL rather than
materialized in memory.
For the setter methods, x is returned with the modified pairings.
# Create SCE with lazy KNN graph
library(SingleCellExperiment)
library(BiocDuckDB)
sce <- SingleCellExperiment(assays = list(counts = matrix(1:100, 10, 10)))
# Create DuckDBSelfHits from parquet
knn_hits <- DuckDBSelfHits("knn_edges.parquet",
from = "from",
to = "to",
nnode = ncol(sce))
# Assign to colPairs (auto-wraps in DuckDBDualSubset)
colPairs(sce, "knn") <- knn_hits
# Subsetting SCE automatically subsets the graph lazily
sce_sub <- sce[, 1:5]
pairs_sub <- colPairs(sce_sub, "knn") # Lazy filtered to nodes 1:5
Patrick Aboyoun
DuckDBSelfHits for the lazy edge storage class.
DuckDBDualSubset for the wrapper that enables node-based subsetting.
colPairs and
rowPairs for the base SingleCellExperiment methods.
library(SingleCellExperiment) sce <- SingleCellExperiment(assays = list(counts = matrix(1:50, 10, 5))) hits <- S4Vectors::SelfHits( from = rep(1:5, each = 2), to = sample(1:5, 10, replace = TRUE), nnode = 5L) tmp <- tempfile() writeParquet(hits, tmp) ddb_hits <- DuckDBSelfHits(tmp, from = "from", to = "to", nnode = 5L) colPair(sce, "knn") <- ddb_hits class(colPair(sce, "knn")) unlink(tmp, recursive = TRUE)library(SingleCellExperiment) sce <- SingleCellExperiment(assays = list(counts = matrix(1:50, 10, 5))) hits <- S4Vectors::SelfHits( from = rep(1:5, each = 2), to = sample(1:5, 10, replace = TRUE), nnode = 5L) tmp <- tempfile() writeParquet(hits, tmp) ddb_hits <- DuckDBSelfHits(tmp, from = "from", to = "to", nnode = 5L) colPair(sce, "knn") <- ddb_hits class(colPair(sce, "knn")) unlink(tmp, recursive = TRUE)
Methods to get or set nested feature-level tables in a
SingleCellExperiment object. These tables enable storage of
multi-column relational data associated with features (genes), which would
otherwise require nested DataFrames in rowData(). By extracting nested
tables into a separate slot, they can be serialized to independent Parquet
tables for efficient querying.
x |
A SingleCellExperiment object. |
type |
String or integer scalar specifying the name or index of the table to get or set. |
withDimnames |
Logical scalar indicating whether row names should be
extracted from (getters) or set to (setters) the row names of |
... |
Additional arguments, currently ignored. |
value |
For the getter, a DataFrame-like object with number of rows
equal to |
Feature tables (rowTables) are stored in
int_elementMetadata(x)$rowTables and provide a mechanism to unnest
complex relational data from rowData(). This is particularly useful
when feature metadata contains multi-valued properties or hierarchical
structures that are difficult to query when embedded as nested DataFrames.
Common use cases include:
Gene annotation tables: transcript isoforms, protein domains, or GO terms
Experimental design tables: per-gene treatment conditions or batch effects
Statistical results: detailed per-gene statistics across multiple contrasts
Each table is a DataFrame with nrow(x) rows, maintaining
alignment with features. Tables are automatically subset when the parent
SingleCellExperiment is subset by rows.
For rowTable, a DataFrame containing feature-level relational data.
For rowTables, a SimpleList of such DataFrames.
For rowTableNames, a character vector of table names.
For all setters, x is returned with the modified tables or names.
In the following examples, x is a SingleCellExperiment
object.
rowTable(x, type, withDimnames=TRUE):Retrieves a DataFrame containing feature-level relational data. type
is either a string specifying the name of the table in x to retrieve,
or a numeric scalar specifying the index of the desired table.
If withDimnames=TRUE, row names of the output DataFrame are replaced
with the row names of x.
rowTableNames(x):Returns a character vector containing the names of all tables in x.
This is guaranteed to be of the same length as the number of tables, though
the names may not be unique.
rowTables(x, withDimnames=TRUE):Returns a named SimpleList of DataFrames containing one or more
tables. Each table has the same number of rows as nrow(x).
If withDimnames=TRUE, row names of each DataFrame are replaced with
the row names of x.
rowTable(x, type, withDimnames=TRUE) <- value will add or replace a
table in a SingleCellExperiment object x.
The value of type determines how the table is added or replaced:
If type is a numeric scalar, it must be within the range of
existing tables, and value will be assigned to the table at that
index.
If type is a string and a table exists with this name,
value is assigned to that table. Otherwise a new table with this
name is appended to the existing list of tables.
value is expected to be a DataFrame or data.frame-like object with
number of rows equal to nrow(x).
If withDimnames=TRUE, row names of value are set to
rownames(x).
In the following examples, x is a SingleCellExperiment
object.
rowTables(x, withDimnames=TRUE) <- value:Replaces all tables in x with those in value.
The latter should be a list-like object containing any number of DataFrames
or data.frame-like objects with number of rows equal to nrow(x).
If value is named, those names will be used to name the tables in
x.
If value is a Annotated object, any
metadata will be retained in rowTables(x).
If value is a Vector object, any mcols
will also be retained.
If withDimnames=TRUE, row names in each entry of value are set
to rownames(x).
rowTableNames(x) <- value:Replaces all names for tables in x with a character vector
value. This should be of length equal to the number of tables
currently in x.
Patrick Aboyoun
rowData for simple feature metadata.
colTables for sample-level nested tables.
int_elementMetadata for the internal row
metadata storage.
library(SingleCellExperiment) # Create example SCE sce <- SingleCellExperiment( assays = list(counts = matrix(rpois(1000, 5), 100, 10)) ) rownames(sce) <- paste0("Gene", 1:100) colnames(sce) <- paste0("Cell", 1:10) # Add a table of gene isoforms (multiple isoforms per gene) isoforms <- DataFrame( isoform_id = paste0("ISO", 1:100), length = sample(500:5000, 100), is_canonical = sample(c(TRUE, FALSE), 100, replace = TRUE) ) rowTable(sce, "isoforms") <- isoforms # Add a table of differential expression results across conditions de_results <- DataFrame( condition = rep(c("treated", "control"), each = 50), log2fc = rnorm(100), pvalue = runif(100) ) rowTable(sce, "de_stats") <- de_results # Retrieve all tables all_tables <- rowTables(sce) names(all_tables) # Retrieve specific table by name iso <- rowTable(sce, "isoforms") dim(iso) # 100 genes × 3 columns # Retrieve by index de <- rowTable(sce, 2) # Get table names rowTableNames(sce) # Subset SCE - tables are automatically subset sce_sub <- sce[1:50, ] dim(rowTable(sce_sub, "isoforms")) # 50 genes × 3 columnslibrary(SingleCellExperiment) # Create example SCE sce <- SingleCellExperiment( assays = list(counts = matrix(rpois(1000, 5), 100, 10)) ) rownames(sce) <- paste0("Gene", 1:100) colnames(sce) <- paste0("Cell", 1:10) # Add a table of gene isoforms (multiple isoforms per gene) isoforms <- DataFrame( isoform_id = paste0("ISO", 1:100), length = sample(500:5000, 100), is_canonical = sample(c(TRUE, FALSE), 100, replace = TRUE) ) rowTable(sce, "isoforms") <- isoforms # Add a table of differential expression results across conditions de_results <- DataFrame( condition = rep(c("treated", "control"), each = 50), log2fc = rnorm(100), pvalue = runif(100) ) rowTable(sce, "de_stats") <- de_results # Retrieve all tables all_tables <- rowTables(sce) names(all_tables) # Retrieve specific table by name iso <- rowTable(sce, "isoforms") dim(iso) # 100 genes × 3 columns # Retrieve by index de <- rowTable(sce, 2) # Get table names rowTableNames(sce) # Subset SCE - tables are automatically subset sce_sub <- sce[1:50, ] dim(rowTable(sce_sub, "isoforms")) # 50 genes × 3 columns
Returns the names of the coordinate systems referenced by the per-element
transforms stored in metadata(x)$transforms (the targets each element
is mapped into). Empty when no transforms are recorded.
spatialCoordinateSystems(x)spatialCoordinateSystems(x)
x |
A character vector of coordinate-system names.
mase <- readParquet(system.file("extdata", "spatial_mase", package = "BiocDuckDB")) spatialCoordinateSystems(mase)mase <- readParquet(system.file("extdata", "spatial_mase", package = "BiocDuckDB")) spatialCoordinateSystems(mase)
Joins two spatial elements (points or shapes, each named
"<element_type>/<region>") by a spatial predicate, pushed to DuckDB
via DuckDBSpatial. When coordinate_system is given and the MASE
carries transforms, each element is first aligned into that coordinate system
through the coordinate-transform graph
(spatialCoordinateSystems), so elements defined in different
intrinsic frames are compared correctly. This generalizes
annotateWithRegions (point-in-polygon only) to arbitrary
element pairs and predicates.
spatialElementJoin( mase, x, y, join = sf::st_intersects, coordinate_system = NULL, x_col = "x", y_col = "y" )spatialElementJoin( mase, x, y, join = sf::st_intersects, coordinate_system = NULL, x_col = "x", y_col = "y" )
mase |
|
x, y
|
Element specs |
join |
A spatial predicate function (default
|
coordinate_system |
Optional target coordinate-system name to align both elements into before joining. |
x_col, y_col
|
Coordinate column names for point elements. |
The spatial-join result from DuckDBSpatial
(spatialMatch indices for a point/geometry query).
# spatialElementJoin(mase, "points/centroids", "shapes/cells") # spatialElementJoin(mase, "points/centroids", "shapes/cells", # coordinate_system = "global")# spatialElementJoin(mase, "points/centroids", "shapes/cells") # spatialElementJoin(mase, "points/centroids", "shapes/cells", # coordinate_system = "global")
Registers each spatial layer (points, shapes) and the spatialMap
junction of a MultiAssaySpatialExperiment as DuckDB temp views
on a shared connection, so cross-element queries can be expressed as SQL.
Lazy (DuckDBDataFrame) layers are registered as views over
their rendered SQL (no materialization); in-memory layers and the
materialized spatialMap are registered as temp tables. This is the
substrate used by linkSpatialMap /
validateSpatialMap and a handle for power-user raw SQL.
spatialViews(mase, conn = acquireDuckDBConn(), prefix = "mase_")spatialViews(mase, conn = acquireDuckDBConn(), prefix = "mase_")
mase |
|
conn |
A DuckDB connection (default the shared BiocDuckDB connection). |
prefix |
View-name prefix. |
A MASESpatialViews registry (a list of view names + conn).
mase <- readParquet(system.file("extdata", "spatial_mase", package = "BiocDuckDB")) v <- spatialViews(mase) v$spatial_mapmase <- readParquet(system.file("extdata", "spatial_mase", package = "BiocDuckDB")) v <- spatialViews(mase) v$spatial_map
Checks the spatialMap foreign keys against the spatial layers and the
assays, out-of-core via DuckDB anti-joins on lazy layers. Reports four
violation classes: unknown_layer (a (element_type, region) with
no matching layer), orphan_instance (a spatialMap row whose
instance_id is absent from its layer), orphan_colname (an
(assay, colname) that is not a column of that experiment), and
duplicate_instance (an instance_id occurring more than once
within a layer).
validateSpatialMap(mase, strict = FALSE, conn = acquireDuckDBConn())validateSpatialMap(mase, strict = FALSE, conn = acquireDuckDBConn())
mase |
|
strict |
If |
conn |
A DuckDB connection (default the shared BiocDuckDB connection). |
A DataFrame of violations (type, assay,
colname, element_type, region, instance_id,
detail); empty when the spatialMap is valid.
mase <- readParquet(system.file("extdata", "spatial_mase", package = "BiocDuckDB")) validateSpatialMap(mase) # empty report: the spatialMap is validmase <- readParquet(system.file("extdata", "spatial_mase", package = "BiocDuckDB")) validateSpatialMap(mase) # empty report: the spatialMap is valid
Assembles and writes the top-level datapackage.json manifest from a
list of already-written Frictionless resource descriptors. This is the
envelope step shared by the experiment-level writeParquet
methods, exposed so that producers who accumulate resources incrementally
(streaming large datasets, or promoting from another store) can emit a
conformant manifest without reconstructing an in-memory Bioconductor object.
writeDatapackage( model, resources, path, main_exp_name = NULL, annotations = NULL )writeDatapackage( model, resources, path, main_exp_name = NULL, annotations = NULL )
model |
Character(1) package-level schema identifier that selects the
|
resources |
A list of Frictionless resource descriptors (each a list),
as returned/accumulated from |
path |
Character(1) directory to write |
main_exp_name |
Optional character(1) naming the main experiment (used by
the |
annotations |
Optional list of non-relational metadata elements (as
produced during metadata serialization). Omitted when |
Each entry of resources is a Frictionless resource descriptor – a list
with name, path, dimension, layout, format,
mediatype, and schema – exactly as returned by the primitive
writeParquet methods (array, data.frame, DataFrame,
SelfHits). NULL entries are dropped, so the NULL returned
by append/streaming parts (see writeParquet) can be accumulated
and passed straight through. Descriptors are written verbatim otherwise; strip
any private, non-Frictionless keys before calling.
Invisibly, the assembled package list that was written.
Patrick Aboyoun
writeParquet for writing resources, and
readParquet for reading a written package (the
DuckDBMatrix/DuckDBArray/DuckDBTable constructors attach
an existing coord-array in place).
# Assemble a manifest from a hand-built resource descriptor. tf <- tempfile() resources <- list(list( name = "features", path = "features", dimension = "feature", layout = "data_frame", format = "parquet", mediatype = "application/vnd.apache.parquet", schema = list(fields = list(list(name = "id", type = "integer"))))) writeDatapackage("summarized_experiment", resources, tf) cat(readLines(file.path(tf, "datapackage.json")), sep = "\n")# Assemble a manifest from a hand-built resource descriptor. tf <- tempfile() resources <- list(list( name = "features", path = "features", dimension = "feature", layout = "data_frame", format = "parquet", mediatype = "application/vnd.apache.parquet", schema = list(fields = list(list(name = "id", type = "integer"))))) writeDatapackage("summarized_experiment", resources, tf) cat(readLines(file.path(tf, "datapackage.json")), sep = "\n")
An arrow::write_dataset wrapper function to write the Parquet
representation of various R objects using the Frictionless
[Data Package](https://datapackage.org) metadata model. This function
supports multiple object types including arrays, data frames, genomic
ranges, and more, converting them into efficient Parquet format for
analysis and storage with comprehensive metadata descriptions.
writeParquet(x, path, ...) ## S4 method for signature 'ANY' writeParquet( x, path, indexcols = names(dimnames(x)) %||% sprintf("index%d", seq_along(dim(x))), indexrefs = NULL, datacol = "value", grid = defaultAutoGrid(COO_SparseArray(dim(x))), grid_suffix = "_group", BPPARAM = getAutoBPPARAM(), name = basename(path), dimension = c("crossed", "unbound"), layout = "coord_array", ... ) ## S4 method for signature 'data.frame' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, refs = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = c("unbound", "sample", "feature", "crossed"), layout = "data_frame", ... ) ## S4 method for signature 'DataFrame' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, refs = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = c("unbound", "sample", "feature", "crossed"), layout = "data_frame", ... ) ## S4 method for signature 'DuckDBTable' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = c("unbound", "sample", "feature", "crossed"), layout = "data_frame", ... ) ## S4 method for signature 'DuckDBDataFrame' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = c("unbound", "sample", "feature", "crossed"), layout = "data_frame", ... ) ## S4 method for signature 'DuckDBTransposedDataFrame' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = "crossed", layout = "transposed_data_frame", ... ) ## S4 method for signature 'DuckDBSelfHits' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = c("unbound", "sample", "feature"), layout = "graph_edges", ... ) ## S4 method for signature 'DuckDBGRanges' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = c("feature", "unbound"), layout = "genomic_ranges", ... ) ## S4 method for signature 'DuckDBGRangesList' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = c("feature", "unbound"), layout = "genomic_ranges_list", ... ) ## S4 method for signature 'TransposedDataFrame' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, name = basename(path), dimension = "crossed", layout = "transposed_data_frame", ... ) ## S4 method for signature 'list' writeParquet( x, path, dimension = c("unbound", "sample", "feature", "crossed"), ... ) ## S4 method for signature 'List' writeParquet( x, path, dimension = c("unbound", "sample", "feature", "crossed"), ... ) ## S4 method for signature 'GenomicRanges' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, name = basename(path), dimension = c("feature", "unbound"), layout = "genomic_ranges", ... ) ## S4 method for signature 'GenomicRangesList' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, name = basename(path), dimension = c("feature", "unbound"), layout = "genomic_ranges_list", ... ) ## S4 method for signature 'SelfHits' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, name = basename(path), dimension = c("unbound", "sample", "feature"), layout = "graph_edges", ... ) ## S4 method for signature 'Assays' writeParquet( x, path, indexcols = c("__feature__", "__sample__"), indexrefs = NULL, datacol = "value", grid = defaultAutoGrid(COO_SparseArray(dim(x[[1L]]))), grid_suffix = "group__", dimension = "crossed", ... ) ## S4 method for signature 'SummarizedExperiment' writeParquet( x, path, indexcols = c("__feature__", "__sample__"), package = list(model = ifelse(is(x, "RangedSummarizedExperiment"), "ranged_summarized_experiment", "summarized_experiment"), resources = list()), ... ) ## S4 method for signature 'SingleCellExperiment' writeParquet( x, path, indexcols = c("__feature__", "__sample__"), package = list(model = "single_cell_experiment", resources = list()), ... ) ## S4 method for signature 'ExperimentList' writeParquet(x, path, indexcols = c("__feature__", "__sample__"), ...) ## S4 method for signature 'MultiAssayExperiment' writeParquet( x, path, indexcols = c("__feature__", "__sample__"), package = list(model = "multi_assay_experiment", resources = list()), ... ) ## S4 method for signature 'PointsLayerList' writeParquet(x, path, ...) ## S4 method for signature 'ShapesLayerList' writeParquet(x, path, ...) ## S4 method for signature 'MultiAssaySpatialExperiment' writeParquet( x, path, indexcols = c("__feature__", "__sample__"), package = list(model = "multi_assay_spatial_experiment", resources = list()), ... )writeParquet(x, path, ...) ## S4 method for signature 'ANY' writeParquet( x, path, indexcols = names(dimnames(x)) %||% sprintf("index%d", seq_along(dim(x))), indexrefs = NULL, datacol = "value", grid = defaultAutoGrid(COO_SparseArray(dim(x))), grid_suffix = "_group", BPPARAM = getAutoBPPARAM(), name = basename(path), dimension = c("crossed", "unbound"), layout = "coord_array", ... ) ## S4 method for signature 'data.frame' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, refs = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = c("unbound", "sample", "feature", "crossed"), layout = "data_frame", ... ) ## S4 method for signature 'DataFrame' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, refs = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = c("unbound", "sample", "feature", "crossed"), layout = "data_frame", ... ) ## S4 method for signature 'DuckDBTable' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = c("unbound", "sample", "feature", "crossed"), layout = "data_frame", ... ) ## S4 method for signature 'DuckDBDataFrame' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = c("unbound", "sample", "feature", "crossed"), layout = "data_frame", ... ) ## S4 method for signature 'DuckDBTransposedDataFrame' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = "crossed", layout = "transposed_data_frame", ... ) ## S4 method for signature 'DuckDBSelfHits' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = c("unbound", "sample", "feature"), layout = "graph_edges", ... ) ## S4 method for signature 'DuckDBGRanges' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = c("feature", "unbound"), layout = "genomic_ranges", ... ) ## S4 method for signature 'DuckDBGRangesList' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, append = FALSE, offset = 0L, part = NULL, part_digits = 0L, name = basename(path), dimension = c("feature", "unbound"), layout = "genomic_ranges_list", ... ) ## S4 method for signature 'TransposedDataFrame' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, name = basename(path), dimension = "crossed", layout = "transposed_data_frame", ... ) ## S4 method for signature 'list' writeParquet( x, path, dimension = c("unbound", "sample", "feature", "crossed"), ... ) ## S4 method for signature 'List' writeParquet( x, path, dimension = c("unbound", "sample", "feature", "crossed"), ... ) ## S4 method for signature 'GenomicRanges' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, name = basename(path), dimension = c("feature", "unbound"), layout = "genomic_ranges", ... ) ## S4 method for signature 'GenomicRangesList' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, name = basename(path), dimension = c("feature", "unbound"), layout = "genomic_ranges_list", ... ) ## S4 method for signature 'SelfHits' writeParquet( x, path, indexcol = "__index__", keycol = "__name__", dimtbl = NULL, name = basename(path), dimension = c("unbound", "sample", "feature"), layout = "graph_edges", ... ) ## S4 method for signature 'Assays' writeParquet( x, path, indexcols = c("__feature__", "__sample__"), indexrefs = NULL, datacol = "value", grid = defaultAutoGrid(COO_SparseArray(dim(x[[1L]]))), grid_suffix = "group__", dimension = "crossed", ... ) ## S4 method for signature 'SummarizedExperiment' writeParquet( x, path, indexcols = c("__feature__", "__sample__"), package = list(model = ifelse(is(x, "RangedSummarizedExperiment"), "ranged_summarized_experiment", "summarized_experiment"), resources = list()), ... ) ## S4 method for signature 'SingleCellExperiment' writeParquet( x, path, indexcols = c("__feature__", "__sample__"), package = list(model = "single_cell_experiment", resources = list()), ... ) ## S4 method for signature 'ExperimentList' writeParquet(x, path, indexcols = c("__feature__", "__sample__"), ...) ## S4 method for signature 'MultiAssayExperiment' writeParquet( x, path, indexcols = c("__feature__", "__sample__"), package = list(model = "multi_assay_experiment", resources = list()), ... ) ## S4 method for signature 'PointsLayerList' writeParquet(x, path, ...) ## S4 method for signature 'ShapesLayerList' writeParquet(x, path, ...) ## S4 method for signature 'MultiAssaySpatialExperiment' writeParquet( x, path, indexcols = c("__feature__", "__sample__"), package = list(model = "multi_assay_spatial_experiment", resources = list()), ... )
x |
The object to write. Supported types include:
|
path |
A character string specifying the path where the data will be written. The path will be created if it doesn't exist. |
... |
Additional arguments. For
Note: |
indexcols |
For array-like objects, a character vector of column names
for the integer index columns in the resulting table. Defaults to the
|
indexrefs |
For array-like objects, an optional list of foreign key
references for the index columns in the schema. Defaults to |
datacol |
For array-like objects, a character string specifying the column name containing the array values in the resulting table. Defaults to "value". |
grid |
For array-like objects, an optional ArrayGrid
to use for partitioning array-like objects. If provided, the array will be
split into chunks and written as separate files. Defaults to
|
grid_suffix |
For array-like objects, a character string to append to the partitioning directories if the grid contains more than one cell. Defaults to "_group". |
BPPARAM |
For array-like objects, a
BiocParallelParam object to use for parallel processing
when |
name |
A character string specifying the name of the resource. Defaults
to the basename of the |
dimension |
A character string describing which biological axis the
resource is indexed by. One of |
layout |
A character string identifying the physical storage layout of
the resource. Recorded in |
indexcol |
For |
keycol |
For |
dimtbl |
For |
refs |
For flat ( |
append |
Logical. For |
offset |
Non-negative integer. For |
part |
Optional non-negative integer; |
part_digits |
Zero-padding width for |
package |
For experiment-level methods (
Callers should not normally set this; the default is supplied by each
experiment method. Experiment methods accumulate |
This function provides specialized handling for different object types:
Array-like objects: Converts multi-dimensional arrays into a
coordinate (long) format where each non-zero element is represented as a
row with columns for each dimension and the value. For sparse arrays, only
non-zero elements are written, making it efficient for sparse data. When a
grid is provided with multiple cells, the array is partitioned and each
partition is written to a separate subdirectory. Array writes delegate to
writeCoordArray; arguments
append, along, offset, group_offset,
arrowtype, and max_dim are forwarded via ... and
documented on the writeCoordArray help page.
Flat table append (data.frame / DataFrame): Use
part, part_digits, append, and offset to stream
chunked sample or feature tables as part-0.parquet, part-1.parquet,
... without temporary directories. The global index column (when
indexcol is set) uses offset + seq_len(nrow(x)) on each chunk.
The first call returns a Frictionless resource list (one element); later
append parts return NULL. Build datapackage.json from the first
return, for example:
res <- writeParquet(chunk1, samples_dir, indexcol = "__sample__",
part = 0L, name = "samples", dimension = "sample")
pkg <- list(resources = res)
writeParquet(chunk2, samples_dir, indexcol = "__sample__",
offset = nrow(chunk1), part = 1L, append = TRUE, ...)
data.frame objects: Writes data frames directly with optional
row names and dimension lookup tables for partitioning information.
DataFrame objects: Writes data frames with schema properties
that describe column types and semantics. Column metadata (mcols) is
not preserved; use object-level metadata() for annotations.
TransposedDataFrame objects: Writes transposed data frames
with optional row names and dimension lookup tables for partitioning
information.
GenomicRanges objects: Writes genomic coordinates with proper
column naming and schema properties (genomicCoords) that describe the mapping
of genomic coordinate columns. Range metadata columns are written as regular
columns in the output.
GenomicRangesList objects: Separates list element metadata
from ranges data, writing them to different paths with schema properties
that describe how to split the ranges into list elements.
SelfHits objects: Writes graph edge lists as a data frame
with from, to, and optional metadata columns. The node count
(nnode) is stored in the schema properties to enable reconstruction as
a DuckDBSelfHits object.
Automatic unnesting of nested DataFrames: When writing
SingleCellExperiment objects, any DataFrame-class columns found in
rowData() or colData() are automatically extracted and moved to
rowTables() or colTables() respectively. This unnesting enables
independent Parquet serialization of multi-valued properties (e.g., multiple
diseases per patient, multiple isoforms per gene) while keeping the main
metadata tables flat and SQL-queryable. The original nested columns are
removed from rowData()/colData() after extraction. This
behavior is automatic and requires no user intervention.
SummarizedExperiment objects: Writes multi-assay experiments
with separate paths for feature data, sample data, and assay data.
RangedSummarizedExperiment objects: Extends
SummarizedExperiment functionality with genomic ranges for features.
SingleCellExperiment objects: Extends
SummarizedExperiment with single-cell specific data. All collections
are written as flat resource directories directly under path. The
directory name encodes both axis and type: feature_embeddings/,
sample_embeddings/, feature_table_<name>/,
sample_table_<name>/, feature_graph_<name>/,
sample_graph_<name>/, experiment_<name>/. The path prefix is
derived automatically from layout. Alternative experiments are written
directly to root (flattened, same pattern as MultiAssayExperiment
experiments). See the automatic unnesting section for details about
DataFrame columns in rowData()/colData().
MultiAssayExperiment objects: Writes multi-experiment studies
with separate paths for sample data, sample mapping, and experiment data.
For primitive methods (ANY, data.frame, DataFrame,
TransposedDataFrame, GenomicRanges, GenomicRangesList,
SelfHits, Assays, list, List,
ExperimentList, PointsLayerList, ShapesLayerList):
invisibly returns a list of Frictionless resource entries suitable for
inclusion in a datapackage.json resources array.
For experiment methods (SummarizedExperiment,
SingleCellExperiment, MultiAssayExperiment,
MultiAssaySpatialExperiment): invisibly returns NULL; the
datapackage.json is written to path as a side effect.
For flat multi-part table writes (data.frame with append = TRUE
and part > 0): invisibly returns NULL.
This function writes a datapackage.json file (Frictionless Data
Package v2.0) alongside the Parquet files. The file contains two levels of
metadata:
Package-level fields (top of datapackage.json):
model - Overall schema identifier. Determines which
readParquet reader is used to reconstruct the container object
(e.g. "single_cell_experiment", "multi_assay_experiment").
When absent, readParquet returns a SimpleList of resources
with no imposed schema.
annotations - Non-relational elements from metadata(x)
(scalars, vectors, 1-D arrays, JSON-safe nested lists). Tabular /
relational items (DataFrame, matrix, 2-D arrays) are
written as unbound Parquet sidecars and referenced here via
parquet_ref stubs; mixed nested lists use a
nested_mapping wrapper.
Per-resource fields (each entry in the resources array):
name - Resource identifier (typically the object name)
path - Relative directory path to the Parquet dataset
dimension - Biological axis: "feature", "sample",
"crossed", or "unbound"
layout - Physical storage layout (BiocDuckDB extension);
together with dimension, uniquely determines the R accessor and
AnnData slot used during reconstruction
format - File format ("parquet")
mediatype - MIME type ("application/vnd.apache.parquet")
schema - Field definitions including types, primary key,
sort order, and foreign key references
See the BiocDuckDB storage patterns documentation for the full
model table and dimension + layout dispatch table.
Patrick Aboyoun
readParquet for reading Bioconductor objects from parquet
writeCoordArray for coord-array layout,
hive partitioning, and array append (append, along,
offset, group_offset)
parquet-io for shared Parquet I/O:
flat append validation (setupFlatParquetWrite, checkAppendPart,
validateAppendOffset), SQL COPY TO helpers
(buildParquetCopySQL), and lazy table export
(writeDuckDBTableParquet)
createDimTables for creating dimension lookup tables
ArrayGrid for grid partitioning
BiocParallelParam for parallel processing options
# Write the Titanic dataset to a single Parquet file tf1 <- tempfile() writeParquet(Titanic, tf1) list.files(tf1, full.names = TRUE, recursive = TRUE) # Write the state.x77 matrix to multiple Parquet files using grid partitioning tf3 <- tempfile() state_grid <- RegularArrayGrid(dim(state.x77), c(10, 4)) writeParquet(state.x77, file.path(tf3, "state"), grid = state_grid) dimtbls <- createDimTables(state.x77, grid = state_grid) list.files(tf3, full.names = TRUE, recursive = TRUE)# Write the Titanic dataset to a single Parquet file tf1 <- tempfile() writeParquet(Titanic, tf1) list.files(tf1, full.names = TRUE, recursive = TRUE) # Write the state.x77 matrix to multiple Parquet files using grid partitioning tf3 <- tempfile() state_grid <- RegularArrayGrid(dim(state.x77), c(10, 4)) writeParquet(state.x77, file.path(tf3, "state"), grid = state_grid) dimtbls <- createDimTables(state.x77, grid = state_grid) list.files(tf3, full.names = TRUE, recursive = TRUE)