R-CMD-check DOI

linf: group samples by their most abundant features

linf works with nonnegative measurements, such as microbial counts or proportions. Supply a numeric matrix with samples in rows and features in columns. A feature is a measured quantity, such as the abundance of a type of bacterium.

The package can rescale each sample by its largest value, group samples by which features are most abundant, and divide those groups into smaller groups. It can also assign new samples to groups you have already defined. The groups are called dominant community state types (dCSTs) in the package and methods paper.

A first example

After installation, try this example. n0 = 4 means a group must initially contain at least four samples to be kept. set.seed(1) makes the randomly generated example data repeatable.

library(linf)
set.seed(1)
counts <- matrix(rpois(30, 5), nrow = 10,
                 dimnames = list(paste0("s", 1:10), c("A", "B", "C")))
M <- normalize.linf(counts)
dominant <- linf.dominant.features(M)
fit <- linf.csts(M, n0 = 4, low.freq.policy = "absorb")
Feature Samples before reassignment Samples after reassignment
A 4 6
B 4 4
C 2 0

Each sample is first assigned to its largest feature. When largest values are equal, the first column is used. This gives four samples to A, four to B, and two to C. Groups A and B meet the minimum size of four. Group C is too small, so each of its samples is reassigned to A or B, whichever has the larger value in that sample. Here, both go to A: the final groups contain six and four samples. The option "absorb" selects this reassignment rule. A was not the largest feature in the two reassigned samples; C was.

M contains the rescaled values. Each sample’s largest value is now one, and zeros stay zero. Use M directly if you only need rescaling, without groups.

Group by leading features, regardless of their order

Use linf.udcsts() when samples should belong to the same group if they have the same leading features. For example, A followed by B and B followed by A both count towards one {A, B} group. The minimum group size is checked after combining them:

U <- rbind(c(A = 9, B = 5, C = 1), c(A = 8, B = 4, C = 1),
           c(A = 4, B = 9, C = 1), c(A = 3, B = 8, C = 1),
           c(A = 1, B = 3, C = 9))
unordered <- linf.udcsts(U, level = 2, n0 = 3, low.freq.policy = "filter")
unordered$frequency.table
#>    ID freq cumm.pct
#> 1 1,2    4       80
#> 2 2,3    1      100
U[unordered$keep, , drop = FALSE]
#>      A B C
#> [1,] 9 5 1
#> [2,] 8 4 1
#> [3,] 4 9 1
#> [4,] 3 8 1

The four samples led by A and B remain. The single sample led by B and C is excluded because its group is smaller than three. It is not reassigned.

The result includes:

The "filter" option excludes samples from small groups, as above. The "pure" option keeps them but marks them as rare. The "absorb" option reassigns them to the kept group whose features have the largest combined abundance in that sample. This differs from linf.csts(), which uses one feature at a time and restricts each subdivision to its current group. See ?linf.udcsts for details.

Connect groups in a network

linf.udcst.graph() connects groups defined by their two leading features. By default, pairs such as {A, B} and {A, C} are linked only if both occur among samples whose three leading features are {A, B, C}. Sharing A alone is not enough.

The result records the supporting sample counts, measures of how often each pair and its three-feature group occur together, and which group each sample belongs to. Counts use all input samples, even if you keep only some pairs in the network. It also reports triangles supported by the same three-feature group. Every link has length one; a larger sample count does not shorten it. Unconnected pairs and separate parts of the network are kept. See ?linf.udcst.graph for details.

Installation

Install the released package from CRAN:

install.packages("linf")

linf.udcsts() and linf.udcst.graph() require linf 0.4.0 or later. Check your installed version with packageVersion("linf"). Version 0.4.0 includes four guides, called vignettes in R. Open the function and example-data guides with:

help("linf", package = "linf")
vignette("function-guide", package = "linf")
vignette("example-datasets", package = "linf")

The published website is updated separately and may describe an older version. Use the installed help for the version you run.

Choose the next step

Scaling by the largest value is called L-infinity normalization. The values are not required to sum to one, and no logarithms are taken. Zeros stay zero. Grouping results depend on which features you include, the minimum group size, how small groups are handled, and how choices between equal values are made. Adding more levels gives finer groups, but does not necessarily make them more useful. The function guide source explains these choices with runnable examples.

Gut microbiome example

The figure below shows the first level of grouping for 766 gut microbiome samples provided with the package, from the American Gut Project (AGP). Filtering leaves 763 samples and 307 microbial features. Groups initially containing fewer than 30 samples are too small to keep. Their samples are reassigned to the kept groups, using the largest value among the remaining defining features in each sample. The figure shows the resulting groups, without a separate rare-sample category.

This subset includes every available sample assigned to four selected uncommon groups. The remaining places were filled by random selection from other eligible samples, using seed 42. Selection did not use participants’ recorded traits. Because groups had different chances of being selected, do not use the observed trait frequencies, differences between groups, or statistical test results to draw conclusions about the broader population. These data are provided to demonstrate how to use the package.

Number of samples in each group

Numbers of samples in seven groups after reassigning samples from small groups, using 763 filtered AGP gut samples

The 763 samples are assigned to seven groups. The largest are Bacteroides (239 samples), Escherichia-Shigella (134), and Staphylococcus (102). In total, 159 samples were reassigned from groups below the minimum size.

See vignette("linf-intro", package = "linf") for a reproducible example using these data.

Guides and worked examples

Version 0.4.0 includes four installed guides. The first two links below open their source files on GitHub; the R commands open the formatted guides locally.

The interactive 3D visualization example is available on the website only.

browseVignettes("linf")

Citation

If you use this package, please cite:

Gajer, P. & Ravel, J. (2025). A New Approach to Compositional Data Analysis using L∞-normalization with Applications to Vaginal Microbiome. arXiv preprint arXiv:2503.21543 [stat.CO]. doi: 10.48550/arXiv.2503.21543

BibTeX

@article{gajer2025linf,
  title   = {A New Approach to Compositional Data Analysis using
             {$L^{\infty}$}-normalization with Applications to
             Vaginal Microbiome},
  author  = {Gajer, Pawel and Ravel, Jacques},
  year    = {2025},
  eprint  = {2503.21543},
  archivePrefix = {arXiv},
  primaryClass  = {stat.CO},
  journal = {arXiv preprint arXiv:2503.21543},
  doi     = {10.48550/arXiv.2503.21543},
  url     = {https://arxiv.org/abs/2503.21543}
}

License

MIT © 2025 Pawel Gajer. See LICENSE. Sources and reuse conditions for the example data are recorded in the data-source notes.