NEWS
levi 1.99.0
Overview
- This is the Bioconductor devel version of the rewritten package; it
will be released as levi 2.0.0. The landscape core was rebuilt, an
inference layer with permutation tests was added, the Shiny interface
now shares every computation with script mode, and adapters bring
data in from the main Bioconductor and single-cell workflows.
Landscape core
- The landscape is computed as a Gaussian-kernel weighted average
(normalised convolution) instead of an unweighted mean over the k
nearest points. Every landscape value is a convex combination of the
input signals and lies within their range; data with no variation
gives the neutral value 0.5 at any smoothing and any network density.
- The previous implementation injected a zero-signal point at every
background grid cell, which divided the landscape amplitude by k
(about 11% of the colour scale at the default smoothing), put
result$scores off the documented [0, 1] scale and made peaks
unreachable on sparse networks. All ten Expected results assertions
of the bundled toy datasets now pass (three passed before).
- The network silhouette is carried by the sum of kernel weights.
Cells outside the network return NA and are drawn in a background
colour, so "no network here" and "network present, minimal
expression" are no longer the same value.
- The kernel is separable, so the convolution runs as two 1-D passes.
medusa.dat at resolution 135 went from 28.5 s to about 12 ms and the
cost no longer grows with network size.
- New argument edge_weighting in levi(): "midpoint" (default) keeps
the historical deposit of one support point per edge midpoint,
"degree" weights the midpoint of edge (i, j) by (1/d_i + 1/d_j)/2 so
the midpoints around any node add up to one, and "none" smooths the
node values alone. The choice and the weights are recorded in the
result metadata and reused by the sample-label null.
- The grid always leaves room for the smoothed silhouette. The margin
used to be the zoom alone, while the kernel carries occupancy about
20% beyond the outermost node, so landscapes were clipped wherever a
node sat near the border. zoomValueInput now adds up to 20% on top
of a frame that fits the silhouette (0 = widest, 100 = tightest).
- contrastValueInput and smoothValueInput keep their 0-100 range but
now set the occupancy threshold of the silhouette and the Gaussian
width in grid cells. The width is scaled to resolutionValueInput, so
changing the resolution no longer changes the apparent smoothing,
and smoothing no longer changes the value scale.
- The same transformed signal feeds the surface, the scores, the peaks
and the tests. Ratio scores keep the absolute Test/(Test+Control)
scale; the old max() normalisation of the per-condition matrices was
dropped.
- SigCoordPiso(), matrix_entrada() and matrix_saida() were replaced by
landscape_gauss(); nearest_node_grid() labels each grid cell with its
closest node.
Signal modes and input guards
- New argument signal_mode: "ratio" (default) for counts, TPM, FPKM
and linear proteomics; "logfc" (sigmoid of the log fold change, with
steepness logfc_k) for RMA microarray, VST/rlog and single-cell
avg_log2FC; "zscore" for multi-condition or heterogeneous-scale
data. Single-column logFC input uses readExpColumn("logFC-logFC")
with signal_mode = "logfc".
- expressionLog back-transforms from log2 only in ratio mode; in the
other modes it is ignored with a message, because log-scale data
must stay in log2 for the fold change.
- Ratio mode warns when the expression data contain negative values,
which invert the landscape; Test + Control == 0 is reported and set
to the neutral score instead of escaping as Inf.
- The [0, 1] range of LandscapeScore is enforced for every signal mode
in one place and fixed by a test.
- Duplicated identifiers are still averaged, but the number collapsed
is reported; a warning is raised when no identifier of the network
matches the expression data, and a message states how many nodes
have no expression value.
- expressionInput accepts a data.frame or matrix as well as a file
path, and the stg branch accepts in-memory node and edge tables.
- The error for a missing expression column now reads "Column not
found in the expression data".
Result object, scores, peaks and regions
- levi() returns an object of class "levi_result" with a print()
method summarising the comparison, the scored nodes, the score
range, the strongest and weakest genes, the peaks and valleys and
whether a permutation test ran. The fields are unchanged, so
result$scores and the rest keep working.
- result$scores ranks every gene by LandscapeScore in [0, 1];
result$peaks lists local maxima and minima with the nearest gene,
matrix position and score.
- Explicit eight-connected landscape regions with direction, area,
mass, peak, centroid and member cells. Without permutations they
remain descriptive; with them they carry regional p-values.
- Result provenance is recorded and leviDiff() rejects incompatible
comparisons.
Inference layer
- Permutation test in levi() with n_perm, sig_level and perm_side
("both", "over", "under"). result$pvalues holds $over and $under
matrices; 2D figures overlay the sig_level contours, dashed for
over-expression and dotted for under-expression, and the 3D surface
traces the same boundary.
- inference_unit = "region" (now the default) runs a maximum
excess-mass permutation test over eight-connected regions in both
directions, keeps the null maxima, reports a spatial p-value per
region and draws regional boundaries. inference_unit = "cell" keeps
the legacy per-cell test, which the Shiny contours still use. Both
directional families are adjusted jointly (BY by default) and the
raw p-values remain available.
- Node-label permutations shuffle only the signals, never the
coordinates, so the silhouette and the denominator are computed
once. p-values use (k+1)/(n+1), so none is reported as exactly 0.
Optional perm_strata shuffles within bins of expression, detection
rate or degree.
- New leviReplicateInference(): regional maximum-mass inference by
permuting biological sample labels, optionally within blocks.
Regional figures label only the regions that pass sig_level.
- New leviAdjustPathways() to correct regional results across a
family of pathway networks, and leviRegionGenes() to rank the genes
that contribute to a region.
- New graph statistics on the network itself: leviGraphMoran()
(global and local autocorrelation from the same permutations, with
BH-adjusted local p-values), leviGraphGetisOrd() (hotspots and
coldspots with PTwoSided and PAdjusted; Significant uses the
adjusted value), leviGraphSpectrum() (Laplacian smoothness and
low-frequency energy; the eigenvector order was corrected and the
decomposition is computed once) and leviGraphWeightedTopology().
- New sample-label graph tests: leviGraphClusterInference() for
connected cluster mass, leviGraphTFCEInference() for threshold-free
cluster enhancement, leviGraphTFCEFreedmanLane() for residual
permutation under covariates, leviGraphRewiringInference() and
leviGraphTFCERewiring() for degree-preserving rewiring nulls, and
leviBulkGraphInference() on DESeq2 counts.
- leviBulkGraphInference() keeps the observed size factors and
dispersions fixed across permutations by default
(refit_dispersions = FALSE) and uses one dispersion estimator for
the observed statistic and every draw, falling back to gene-wise
dispersions when the trend fit fails; the choice is returned as
dispersion_estimator.
- Single-cell pseudobulk tests: leviPseudobulk() aggregates counts per
sample and cell type; leviSingleCellRegionalInference(),
leviSingleCellGraphInference(), leviSingleCellTFCEInference(),
leviSingleCellInteractionTFCE() and
leviSingleCellTopologyInference() permute within donor. They use
TMM-normalised log2 CPM and the limma-trend moderated t
(normalize = "none" restores plain CPM) and drop genes with fewer
than 10 counts in two pseudobulks before the fit, always keeping the
network genes; the interaction coefficient is located by its exact
design column name.
- Graph TFCE builds the igraph object once per call and recomputes the
components only when the set of nodes above the threshold changes,
instead of rebuilding the graph at each of the 100 integration steps
of every permutation. The single-cell TFCE example runs in under a
second instead of about 6 s; results are identical.
- All permutation tests share one engine with a BPPARAM argument.
SerialParam is the default; MulticoreParam() and SnowParam() give
identical results because all randomness stays in the calling
process. Sparse adjacency matrices are used throughout (Matrix in
Imports).
- A warning states when a design cannot reach the significance level
(fewer than 1/alpha arrangements or draws), e.g. 3 vs 3 unblocked
gives a floor of 0.05. Arrangements are counted with choose()
before enumeration, so unblocked designs with more than about 30
samples no longer abort. Blocks holding a single condition
contribute one fixed arrangement instead of an error.
Data adapters and network construction
- leviFromDESeq2(), leviFromEdgeR(), leviFromLimma() and
leviFromSeurat() convert differential expression tables to levi
input; leviFromSE(), leviFromExpressionSet() and leviFromBioc()
read SummarizedExperiment and ExpressionSet objects, selecting
samples by name, index or colData condition.
- leviFromSTRING() builds a network from the STRING database from a
vector of gene symbols, and leviFromEdges() builds one from a plain
edge list (data.frame, matrix or igraph graph), computing a layout
with igraph (fr, kk, lgl, dh or circle) scaled to the range levi
expects. Both share the same layout routine and report interactions
dropped for a missing endpoint.
- Network parsers live in one place, one per format (dat, dyn, net,
stg), and are shared by script mode and the interface.
Comparison, enrichment and export helpers
- leviGrid() arranges several results side by side (patchwork,
cowplot or gridExtra); leviDiff() subtracts two landscapes cell by
cell into a diverging map.
- leviEnrich() runs GO and KEGG enrichment on the top and bottom genes
of result$scores through clusterProfiler; the KEGG background
identifier mapping was fixed.
- plot3d draws an interactive plotly surface next to the 2D map, with
the significance boundary when a test ran. New leviSave3D() writes
it to html, png, jpeg, webp, svg, pdf or tiff from a chosen camera
position.
- Contour layers drop out-of-silhouette cells before stat_contour()
and say when there is no curve to draw instead of warning.
Shiny interface
- The interface no longer compiles its own copy of the C++ core at
startup and no longer keeps private copies of network parsing, the
permutation test, the palettes or the figure. It calls the same
routines as script mode, so no compiler is needed and the two paths
cannot drift (they had: the same palette name gave different
colours and the scale arrows sat at different heights).
- The 2D landscape and the 3D surface are shown in two tabs; the
gene, node score, peak and region tables are tabs of their own
below the figure, each with a CSV download. "Genes" lists what was
brushed on the map. The 3D tab has a "Download 3D (HTML)" button
that keeps the rotated view, and the toolbar PNG export was raised
to 1600 x 1200 at scale 2.
- Settings tab exposes signal_mode, logfc_k, n_perm, perm_side,
sig_level and a "Test unit" selector. The regional test runs by
default and the significant regions are outlined and labelled as in
script mode; a permutation progress bar reports each iteration.
- Gene highlight accepts several genes, draws one circle per gene at
its own grid cell and offers a colour selector; "Label peaks on map"
overlays gene names at the detected peaks.
- The interface could not compute anything under igraph >= 2.0
because a deprecation warning was caught as fatal and reported as
"Incorrect file format". graph_from_edgelist() is used and warnings
are shown as notifications while the computation continues; a file
without a trailing newline is read instead of rejected.
- tests/testthat/test_gui_parity.R drives the server with
shiny::testServer and requires the same scores, surface and, under
a fixed seed, the same p-values as script mode.
Documentation, datasets and validation
- Four vignettes: "levi" (introduction, workflow and every control of
the interface, with new screenshots), "levi_guide" (Bioconductor
data, adapters and STRING), "levi_inference" (which null answers
which question, the p-value floor of small designs, BPPARAM and a
reporting checklist) and "levi_validation" (type I error, power and
layout sensitivity from offline simulations, plus the airway
dexamethasone experiment). All vignettes cite their methods and data
sources from a shared bibliography.
- Twelve self-contained scripts in inst/scripts/ with a README, from
first steps to degree weighting, calibration and the airway
preprocessing; they run on the bundled data alone.
- Toy datasets in inst/extdata/, each documented with expected
results: hub, gradient, bimodal, flat, sparse, logfc and
multi-comparison, beside the original medusa data, the airway
summaries and the validation tables.
- Real-data integration tests replay the analyses of the supplement
(airway, GSE10072 and Kang 2018 on STRING and KEGG networks) and are
skipped unless LEVI_INTEGRATION is set.
- Every argument of the inference functions is documented and the
examples run on the hub dataset. Examples that need objects the
user must supply (a DESeq2, edgeR, limma or Seurat fit, a
SummarizedExperiment, real gene symbols for STRING or
org.Hs.eg.db) are marked \dontrun so R CMD check --as-cran passes.
- Title, Description and biocViews describe the inference layer
(RNASeq, GraphAndNetwork and StatisticalMethod added). NAMESPACE
uses specific importFrom() directives; knitr, rmarkdown and
BiocStyle moved to Suggests. Comments, test names and messages are
all in English.
Breaking changes
- The argument geneSymbolnput of levi() was renamed geneSymbolInput.
No alias is kept; positional calls are unaffected.
- Every landscape changes with the new core, so figures produced with
levi 1.x are not comparable to figures produced with this version.
- inference_unit defaults to "region"; use inference_unit = "cell"
for the former per-cell test.
Bug fixes
- Operator precedence in the DAT parser (3:(delimiter-1)).
- C++ buffer overflow in the former matrix_saida() and matrixOutFun()
when smoothValue >= h.
- colorSet() returned inside an else block; duplicate coord_fixed()
call; deprecated size aesthetic in annotate() segments.
- Internal networkNodesInput renamed networkCoordinatesInput to match
the public API.
- edgeR test-object conversion, medusa validation on the linear ratio
scale with a matched smoking category, and the ".logFC" title on
replicate landscapes.
levi 1.0.0
Overview