Changes in version 1.99.0

Overview

    o 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
    o 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.
    o 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).
    o 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.
    o 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.
    o 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.
    o 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).
    o 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.
    o 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.
    o 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
    o 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".
    o 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.
    o 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.
    o The [0, 1] range of LandscapeScore is enforced for every signal mode
      in one place and fixed by a test.
    o 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.
    o expressionInput accepts a data.frame or matrix as well as a file
      path, and the stg branch accepts in-memory node and edge tables.
    o The error for a missing expression column now reads "Column not
      found in the expression data".

Result object, scores, peaks and regions
    o 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.
    o 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.
    o 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.
    o Result provenance is recorded and leviDiff() rejects incompatible
      comparisons.

Inference layer
    o 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.
    o 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.
    o 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.
    o New leviReplicateInference(): regional maximum-mass inference by
      permuting biological sample labels, optionally within blocks.
      Regional figures label only the regions that pass sig_level.
    o New leviAdjustPathways() to correct regional results across a
      family of pathway networks, and leviRegionGenes() to rank the genes
      that contribute to a region.
    o 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().
    o 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.
    o 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.
    o 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.
    o 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.
    o 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).
    o 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
    o 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.
    o 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.
    o 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
    o leviGrid() arranges several results side by side (patchwork,
      cowplot or gridExtra); leviDiff() subtracts two landscapes cell by
      cell into a diverging map.
    o leviEnrich() runs GO and KEGG enrichment on the top and bottom genes
      of result$scores through clusterProfiler; the KEGG background
      identifier mapping was fixed.
    o 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.
    o Contour layers drop out-of-silhouette cells before stat_contour()
      and say when there is no curve to draw instead of warning.

Shiny interface
    o 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).
    o 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.
    o 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.
    o 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.
    o 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.
    o 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
    o 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.
    o 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.
    o 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.
    o 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.
    o 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.
    o 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
    o The argument geneSymbolnput of levi() was renamed geneSymbolInput.
      No alias is kept; positional calls are unaffected.
    o Every landscape changes with the new core, so figures produced with
      levi 1.x are not comparable to figures produced with this version.
    o inference_unit defaults to "region"; use inference_unit = "cell"
      for the former per-cell test.

Bug fixes
    o Operator precedence in the DAT parser (3:(delimiter-1)).
    o C++ buffer overflow in the former matrix_saida() and matrixOutFun()
      when smoothValue >= h.
    o colorSet() returned inside an else block; duplicate coord_fixed()
      call; deprecated size aesthetic in annotate() segments.
    o Internal networkNodesInput renamed networkCoordinatesInput to match
      the public API.
    o edgeR test-object conversion, medusa validation on the linear ratio
      scale with a matched smoking category, and the ".logFC" title on
      replicate landscapes.

Changes in version 1.0.0

Overview

    o First version of levi.
