library(growkar)
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
library(knitr)
data(yeast_growth_data)High-throughput microbial growth assays are widely used to characterize phenotypic responses to genetic perturbations, environmental conditions, and drug treatments. These assays generate time-resolved measurements across large numbers of samples, analogous to other high-throughput experimental platforms in functional genomics.
While tools exist for transcriptomic and epigenomic data analysis within the Bioconductor ecosystem, there is a relative lack of standardized infrastructure for analyzing growth-based phenotypic data and integrating it with omics datasets.
The growkar package addresses this gap by providing a
scalable and reproducible framework for high-throughput microbial
phenotyping from growth assays. It uses Bioconductor data structures to
represent assay measurements and derived phenotypes, while accepting
tidy and wide plate-reader exports as import adapters.
The primary workflow is:
SummarizedExperiment.growkar uses two S4 classes and no S3 classes:
GrowthExperiment is the data
container. It is an S4 class that extends
SummarizedExperiment, adding a validity method that
enforces the canonical growth-assay layout (an od assay,
numeric time in rowData()). Because it
is a SummarizedExperiment, every Bioconductor
method works on it unchanged, and
as(x, "SummarizedExperiment") is always available for
handing objects to other packages.GrowthFit represents a model
result — a logistic or Gompertz fit for one sample. It is not a
data container; instances are stored in metadata(ge) beside
the experiment they were derived from.The package defines no S3 classes and registers no S3 methods.
Build a GrowthExperiment with the constructor or with
standard S4 coercion:
# Constructor (control over column-name resolution):
ge <- GrowthExperiment(yeast_growth_data)
ge
#> class: GrowthExperiment
#> dim: 49 9
#> metadata(1): growkar_schema
#> assays(1): od
#> rownames(49): 0 0.5 ... 23.5 24
#> rowData names(1): time
#> colnames(9): Cg_R1 Cg_R2 ... YPD_R2 YPD_R3
#> colData names(3): sample condition replicate
# Equivalent standard coercion:
ge2 <- as(yeast_growth_data, "GrowthExperiment")
identical(ge, ge2)
#> [1] TRUE
# It is a SummarizedExperiment, so it hands off cleanly to other packages:
se <- as(ge, "SummarizedExperiment")
is(ge, "SummarizedExperiment")
#> [1] TRUETidy tables and wide plate-reader exports are handled purely as
import adapters by as_tidy_growth_data(), which
resolves vendor column labels and normalizes them to
sample, time, and od before the
container is built. Once data are in the container, tidy manipulation is
delegated to the tidyomics stack rather than reimplemented — see Interoperability with
tidyomics below.
Derived summaries are stored in metadata() of the same
object, so growth phenotypes travel with the container:
ge <- growth_metrics(ge, method = "rolling_window", average_replicates = TRUE)
S4Vectors::metadata(ge)$growth_metrics
#> # A tibble: 3 × 10
#> sample mu start_time end_time r_squared method n_points degraded note
#> <chr> <dbl> <dbl> <dbl> <dbl> <chr> <int> <lgl> <chr>
#> 1 Cg 0.569 4.5 6.5 1.000 rolling_… 5 FALSE roll…
#> 2 CgFlu 0.404 4.5 6.5 1.000 rolling_… 5 FALSE roll…
#> 3 YPD 0.00128 12.5 14.5 0.500 rolling_… 5 FALSE roll…
#> # ℹ 1 more variable: doubling_time <dbl>This workflow is useful when growth phenotypes need to be carried forward into other Bioconductor analyses or linked to omics-derived sample annotations.
The canonical layout in growkar is:
assay(ge, "od"): OD matrix with timepoints in rows and
samples in columnsrowData(ge): timepoint metadatacolData(ge): sample metadatametadata(ge): derived results such as growth metrics,
phase windows, and fitsvalidate_growth_experiment() checks that an object
follows this layout:
validate_growth_experiment(ge)
#> class: GrowthExperiment
#> dim: 49 9
#> metadata(4): growkar_schema growth_metrics analysis_params
#> growth_metrics_parameters
#> assays(1): od
#> rownames(49): 0 0.5 ... 23.5 24
#> rowData names(1): time
#> colnames(9): Cg_R1 Cg_R2 ... YPD_R2 YPD_R3
#> colData names(3): sample condition replicateHelper accessors make these components easier to inspect:
growth_assay(ge)[1:3, 1:3]
#> Cg_R1 Cg_R2 Cg_R3
#> 0 0.115 0.116 0.117
#> 0.5 0.116 0.118 0.117
#> 1 0.118 0.119 0.118
timepoints(ge)
#> # A tibble: 49 × 1
#> time
#> <dbl>
#> 1 0
#> 2 0.5
#> 3 1
#> 4 1.5
#> 5 2
#> 6 2.5
#> 7 3
#> 8 3.5
#> 9 4
#> 10 4.5
#> # ℹ 39 more rows
sample_data(ge)
#> # A tibble: 9 × 3
#> sample condition replicate
#> <chr> <fct> <fct>
#> 1 Cg_R1 Cg R1
#> 2 Cg_R2 Cg R2
#> 3 Cg_R3 Cg R3
#> 4 CgFlu_R1 CgFlu R1
#> 5 CgFlu_R2 CgFlu R2
#> 6 CgFlu_R3 CgFlu R3
#> 7 YPD_R1 YPD R1
#> 8 YPD_R2 YPD R2
#> 9 YPD_R3 YPD R3as_tidy_growth_data() is the import adapter. It accepts
wide plate-reader exports (time in the first column, samples in the
remaining columns) as well as long tables, resolves common instrument
column labels such as Time [h] and OD600, and
infers condition/replicate from suffixed
sample names.
tidy_growth <- as_tidy_growth_data(yeast_growth_data)
head(tidy_growth)
#> # A tibble: 6 × 5
#> time sample od condition replicate
#> <dbl> <chr> <dbl> <chr> <chr>
#> 1 0 Cg_R1 0.115 Cg R1
#> 2 0 Cg_R2 0.116 Cg R2
#> 3 0 Cg_R3 0.117 Cg R3
#> 4 0 CgFlu_R1 0.131 CgFlu R1
#> 5 0 CgFlu_R2 0.133 CgFlu R2
#> 6 0 CgFlu_R3 0.132 CgFlu R3The canonical columns are sample, time, and
od. Additional metadata such as condition and
replicate are carried alongside them.
validate_growth_data(tidy_growth)
#> # A tibble: 441 × 5
#> time sample od condition replicate
#> <dbl> <chr> <dbl> <chr> <chr>
#> 1 0 Cg_R1 0.115 Cg R1
#> 2 0 Cg_R2 0.116 Cg R2
#> 3 0 Cg_R3 0.117 Cg R3
#> 4 0 CgFlu_R1 0.131 CgFlu R1
#> 5 0 CgFlu_R2 0.133 CgFlu R2
#> 6 0 CgFlu_R3 0.132 CgFlu R3
#> 7 0 YPD_R1 0.105 YPD R1
#> 8 0 YPD_R2 0.105 YPD R2
#> 9 0 YPD_R3 0.104 YPD R3
#> 10 0.5 Cg_R1 0.116 Cg R1
#> # ℹ 431 more rowsImported data are then converted into the canonical container:
growkar does not reimplement tidy verbs or a tidy
display layer for SummarizedExperiment. Install the
optional tidySummarizedExperiment
package to filter, mutate, summarize, and plot growkar
objects with the standard tidyverse grammar, while the underlying object
remains a SummarizedExperiment usable by any other
Bioconductor package.
library(tidySummarizedExperiment)
#> Loading required package: SummarizedExperiment
#> Loading required package: MatrixGenerics
#> Loading required package: matrixStats
#>
#> Attaching package: 'matrixStats'
#> The following object is masked from 'package:dplyr':
#>
#> count
#>
#> Attaching package: 'MatrixGenerics'
#> The following objects are masked from 'package:matrixStats':
#>
#> colAlls, colAnyNAs, colAnys, colAvgsPerRowSet, colCollapse,
#> colCounts, colCummaxs, colCummins, colCumprods, colCumsums,
#> colDiffs, colIQRDiffs, colIQRs, colLogSumExps, colMadDiffs,
#> colMads, colMaxs, colMeans2, colMedians, colMins, colOrderStats,
#> colProds, colQuantiles, colRanges, colRanks, colSdDiffs, colSds,
#> colSums2, colTabulates, colVarDiffs, colVars, colWeightedMads,
#> colWeightedMeans, colWeightedMedians, colWeightedSds,
#> colWeightedVars, rowAlls, rowAnyNAs, rowAnys, rowAvgsPerColSet,
#> rowCollapse, rowCounts, rowCummaxs, rowCummins, rowCumprods,
#> rowCumsums, rowDiffs, rowIQRDiffs, rowIQRs, rowLogSumExps,
#> rowMadDiffs, rowMads, rowMaxs, rowMeans2, rowMedians, rowMins,
#> rowOrderStats, rowProds, rowQuantiles, rowRanges, rowRanks,
#> rowSdDiffs, rowSds, rowSums2, rowTabulates, rowVarDiffs, rowVars,
#> rowWeightedMads, rowWeightedMeans, rowWeightedMedians,
#> rowWeightedSds, rowWeightedVars
#> Loading required package: GenomicRanges
#> Loading required package: stats4
#> Loading required package: BiocGenerics
#> Loading required package: generics
#>
#> Attaching package: 'generics'
#> The following object is masked from 'package:dplyr':
#>
#> explain
#> The following objects are masked from 'package:base':
#>
#> as.difftime, as.factor, as.ordered, intersect, is.element, setdiff,
#> setequal, union
#>
#> Attaching package: 'BiocGenerics'
#> The following object is masked from 'package:dplyr':
#>
#> combine
#> The following objects are masked from 'package:stats':
#>
#> IQR, mad, sd, var, xtabs
#> The following object is masked from 'package:utils':
#>
#> data
#> The following objects are masked from 'package:base':
#>
#> anyDuplicated, aperm, append, as.data.frame, basename, cbind,
#> colnames, dirname, do.call, duplicated, eval, evalq, Filter, Find,
#> get, grep, grepl, is.unsorted, lapply, Map, mapply, match, mget,
#> order, paste, pmax, pmax.int, pmin, pmin.int, Position, rank,
#> rbind, Reduce, rownames, sapply, saveRDS, scale, sequence, table,
#> tapply, transform, unique, unsplit, which.max, which.min
#> Loading required package: S4Vectors
#>
#> Attaching package: 'S4Vectors'
#> The following objects are masked from 'package:dplyr':
#>
#> first, rename
#> The following object is masked from 'package:utils':
#>
#> findMatches
#> The following objects are masked from 'package:base':
#>
#> expand.grid, I, unname
#> Loading required package: IRanges
#>
#> Attaching package: 'IRanges'
#> The following objects are masked from 'package:dplyr':
#>
#> collapse, desc, slice
#> Loading required package: Seqinfo
#> Loading required package: Biobase
#> Welcome to Bioconductor
#>
#> Vignettes contain introductory material; view with
#> 'browseVignettes()'. To cite Bioconductor, see
#> 'citation("Biobase")', and for packages 'citation("pkgname")'.
#>
#> Attaching package: 'Biobase'
#> The following object is masked from 'package:MatrixGenerics':
#>
#> rowMedians
#> The following objects are masked from 'package:matrixStats':
#>
#> anyMissing, rowMedians
#> Loading required package: ttservice
#>
#> Attaching package: 'ttservice'
#> The following objects are masked from 'package:dplyr':
#>
#> bind_cols, bind_rows
#> ℹ tidySummarizedExperiment says: By default SummarizedExperiment uses the standard display. For a tidy tibble-style display, run tidy_print_on(remember = TRUE).
#>
#> Attaching package: 'tidySummarizedExperiment'
#> The following object is masked _by_ '.GlobalEnv':
#>
#> se
# The SE prints and behaves as a tibble abstraction, without being converted.
se
#> class: SummarizedExperiment
#> dim: 49 9
#> metadata(1): growkar_schema
#> assays(1): od
#> rownames(49): 0 0.5 ... 23.5 24
#> rowData names(1): time
#> colnames(9): Cg_R1 Cg_R2 ... YPD_R2 YPD_R3
#> colData names(3): sample condition replicate
se |>
filter(condition == "Cg") |>
filter(time <= 6) |>
select(.feature, .sample, od, condition, replicate)
#> class: SummarizedExperiment
#> dim: 13 3
#> metadata(3): growkar_schema latest_filter_scope_report
#> latest_select_scope_report
#> assays(1): od
#> rownames(13): 0 0.5 ... 5.5 6
#> rowData names(0):
#> colnames(3): Cg_R1 Cg_R2 Cg_R3
#> colData names(2): condition replicate
se |>
group_by(condition) |>
summarise(max_od = max(od), .groups = "drop")
#> ℹ tidySummarizedExperiment says: A data frame is returned for independent data analysis.
#> # A tibble: 3 × 2
#> condition max_od
#> <fct> <dbl>
#> 1 Cg 2.02
#> 2 CgFlu 1.97
#> 3 YPD 0.105growkar results stored in metadata(se) are
ordinary tibbles, so they slot directly into the same downstream
workflow.
Graphing is optional in growkar: ggplot2
and RColorBrewer are declared in Suggests, so
the data-structure and analysis layers install without a graphics stack.
The plot_*() functions check for these packages at call
time and return standard ggplot objects that can be
customized further.
plot_growth_curve(
se,
average_replicates = TRUE,
colour_col = "condition",
palette_name = "Dark2"
)Replicate-level faceting remains available when averaging is disabled:
plot_growth_curve(
se,
average_replicates = FALSE,
colour_col = "condition",
facet_col = "replicate",
palette_name = "Dark2"
)metrics <- summarize_growth_metrics(
se,
method = "rolling_window",
average_replicates = TRUE
)
knitr::kable(metrics, digits = 3)| sample | mu | start_time | end_time | r_squared | method | n_points | degraded | note | doubling_time |
|---|---|---|---|---|---|---|---|---|---|
| Cg | 0.569 | 4.5 | 6.5 | 1.0 | rolling_window | 5 | FALSE | rolling_window_ranked | 1.219 |
| CgFlu | 0.404 | 4.5 | 6.5 | 1.0 | rolling_window | 5 | FALSE | rolling_window_ranked | 1.714 |
| YPD | 0.001 | 12.5 | 14.5 | 0.5 | rolling_window | 5 | FALSE | rolling_window_ranked | 539.788 |
For a single sample, the empirical growth-rate estimate can be inspected in more detail:
gr <- compute_growth_rate(se, method = "rolling_window")
#> Warning: Sample `YPD_R1`: Exponential phase detection did not yield a positive
#> growth slope (rolling_window_ranked).
#> Warning: Sample `YPD_R2`: Exponential phase detection did not yield a positive
#> growth slope (rolling_window_ranked).
knitr::kable(head(gr), digits = 3)| sample | mu | start_time | end_time | r_squared | method | n_points | degraded | note |
|---|---|---|---|---|---|---|---|---|
| Cg_R1 | 0.576 | 4.5 | 6.5 | 1.000 | rolling_window | 5 | FALSE | rolling_window_ranked |
| Cg_R2 | 0.560 | 4.5 | 6.5 | 1.000 | rolling_window | 5 | FALSE | rolling_window_ranked |
| Cg_R3 | 0.571 | 4.5 | 6.5 | 0.999 | rolling_window | 5 | FALSE | rolling_window_ranked |
| CgFlu_R1 | 0.403 | 4.5 | 6.5 | 1.000 | rolling_window | 5 | FALSE | rolling_window_ranked |
| CgFlu_R2 | 0.407 | 4.5 | 6.5 | 1.000 | rolling_window | 5 | FALSE | rolling_window_ranked |
| CgFlu_R3 | 0.403 | 4.5 | 6.5 | 1.000 | rolling_window | 5 | FALSE | rolling_window_ranked |
The doubling time helper is useful when you already have growth-rate values:
knitr::kable(
tibble(
sample = gr$sample,
growth_rate = gr$mu,
doubling_time = compute_doubling_time(gr$mu)
),
digits = 3
)| sample | growth_rate | doubling_time |
|---|---|---|
| Cg_R1 | 0.576 | 1.204 |
| Cg_R2 | 0.560 | 1.238 |
| Cg_R3 | 0.571 | 1.214 |
| CgFlu_R1 | 0.403 | 1.719 |
| CgFlu_R2 | 0.407 | 1.702 |
| CgFlu_R3 | 0.403 | 1.721 |
| YPD_R1 | NA | NA |
| YPD_R2 | NA | NA |
| YPD_R3 | 0.004 | 179.350 |
detect_exponential_phase() returns the ranked candidate
windows and metadata describing whether the chosen interval required any
degraded fallback.
| sample | rank | start_time | end_time | slope | r_squared | n_points | selection_reason | degraded |
|---|---|---|---|---|---|---|---|---|
| Cg_R1 | 1 | 4.5 | 6.5 | 0.576 | 1.000 | 5 | rolling_window_ranked | FALSE |
| Cg_R1 | 2 | 4.0 | 6.0 | 0.551 | 0.997 | 5 | rolling_window_ranked | FALSE |
| Cg_R1 | 3 | 5.0 | 7.0 | 0.551 | 0.997 | 5 | rolling_window_ranked | FALSE |
| Cg_R1 | 4 | 3.5 | 5.5 | 0.490 | 0.990 | 5 | rolling_window_ranked | FALSE |
| Cg_R1 | 5 | 5.5 | 7.5 | 0.490 | 0.991 | 5 | rolling_window_ranked | FALSE |
| Cg_R1 | 6 | 6.0 | 8.0 | 0.414 | 0.987 | 5 | rolling_window_ranked | FALSE |
For a full-curve model-based summary, fit one of the supported
parametric models. fit_growth_curve() returns a
GrowthFit S4 object.
sample_id <- unique(gr$sample)[1]
fit_input <- as_tidy_growth_data(se) |>
filter(sample == sample_id)
cg_fit <- fit_growth_curve(fit_input, model = "logistic")
cg_fit
#> <GrowthFit> sample=Cg_R1, model=logistic, status=converged, n_points=49
isVirtualClass("GrowthFit")
#> [1] FALSE
extract_params(cg_fit)
#> # A tibble: 1 × 6
#> sample model asymptote r t0 doubling_time_model
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 Cg_R1 logistic 2.03 0.777 6.97 0.892
summary(cg_fit)
#> # A tibble: 1 × 9
#> sample model converged status message n_points rss aic bic
#> <chr> <chr> <lgl> <chr> <chr> <int> <dbl> <dbl> <dbl>
#> 1 Cg_R1 logistic TRUE converged <NA> 49 0.0688 -175. -167.GrowthFit supports the standard modelling generics and a
small set of accessors, so slots never need to be touched directly:
fit_sample(cg_fit)
#> [1] "Cg_R1"
fit_model(cg_fit)
#> [1] "logistic"
fit_status(cg_fit)
#> [1] "converged"
fit_converged(cg_fit)
#> [1] TRUE
coef(cg_fit)
#> K r t0
#> 2.0271408 0.7772492 6.9688283
head(fitted(cg_fit))
#> [1] 0.008965938 0.013196596 0.019404332 0.028490904 0.041744026 0.060974028
nobs(cg_fit)
#> [1] 49Failed fits remain machine-readable and do not crash downstream helpers:
flat_fit <- fit_growth_curve(
tibble(
sample = "flat",
time = 0:4,
od = rep(0.2, 5)
),
model = "logistic"
)
summary(flat_fit)
#> # A tibble: 1 × 9
#> sample model converged status message n_points rss aic bic
#> <chr> <chr> <lgl> <chr> <chr> <int> <dbl> <dbl> <dbl>
#> 1 flat logistic FALSE flat_curve Model fitting… 5 NA NA NAAcross a plate, fits are stored in metadata() of the
same SummarizedExperiment:
se <- fit_growth_models(se, model = "logistic")
S4Vectors::metadata(se)$growth_model_parameters
#> # A tibble: 9 × 6
#> sample model asymptote r t0 doubling_time_model
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 Cg_R1 logistic 2.03 0.777 6.97 0.892
#> 2 Cg_R2 logistic 2.01 0.784 6.95 0.884
#> 3 Cg_R3 logistic 2.02 0.784 7.03 0.884
#> 4 CgFlu_R1 logistic 1.98 0.563 7.51 1.23
#> 5 CgFlu_R2 logistic 1.96 0.569 7.40 1.22
#> 6 CgFlu_R3 logistic 1.90 0.589 7.29 1.18
#> 7 YPD_R1 logistic 0.208 0.00000001 0.00132 69314718.
#> 8 YPD_R2 logistic 0.208 0.00000001 0.00132 69314718.
#> 9 YPD_R3 logistic 0.208 0.00000001 812. 69314718.Observed and fitted values can be visualized together:
The supported SE-native interface includes:
as_tidy_growth_data()GrowthExperiment()validate_growth_experiment()compute_growth_rate()summarize_growth_metrics()detect_exponential_phase()fit_growth_curve()GrowthFit object
with status and diagnostics rather than throwing a cryptic
nls error.sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats4 stats graphics grDevices utils datasets methods
#> [8] base
#>
#> other attached packages:
#> [1] ggplot2_4.0.3 tidyr_1.3.2
#> [3] tidySummarizedExperiment_1.23.2 ttservice_0.5.3
#> [5] SummarizedExperiment_1.43.0 Biobase_2.73.2
#> [7] GenomicRanges_1.65.3 Seqinfo_1.3.2
#> [9] IRanges_2.47.5 S4Vectors_0.51.9
#> [11] BiocGenerics_0.59.12 generics_0.1.4
#> [13] MatrixGenerics_1.25.0 matrixStats_1.5.0
#> [15] knitr_1.51 dplyr_1.2.1
#> [17] growkar_0.99.2 rmarkdown_2.32
#>
#> loaded via a namespace (and not attached):
#> [1] gtable_0.3.6 xfun_0.60 bslib_0.12.0
#> [4] htmlwidgets_1.6.4 lattice_0.23-1 vctrs_0.7.3
#> [7] tools_4.6.1 tidyprint_1.1.0 tibble_3.3.1
#> [10] fansi_1.0.7 pkgconfig_2.0.3 Matrix_1.7-6
#> [13] data.table_1.18.6.1 RColorBrewer_1.1-3 S7_0.2.2
#> [16] lifecycle_1.0.5 compiler_4.6.1 farver_2.1.2
#> [19] stringr_1.6.0 htmltools_0.5.9 sys_3.4.3
#> [22] buildtools_1.0.0 sass_0.4.10 yaml_2.3.12
#> [25] plotly_4.12.1 pillar_1.11.1 jquerylib_0.1.4
#> [28] ellipsis_0.3.3 DelayedArray_0.39.6 cachem_1.1.0
#> [31] abind_1.4-8 tidyselect_1.2.1 digest_0.6.39
#> [34] stringi_1.8.9 purrr_1.2.2 maketools_1.3.2
#> [37] labeling_0.4.3 fastmap_1.2.0 grid_4.6.1
#> [40] cli_3.6.6 SparseArray_1.13.2 magrittr_2.0.5
#> [43] S4Arrays_1.13.0 utf8_1.2.6 withr_3.0.3
#> [46] scales_1.4.0 XVector_0.53.0 httr_1.4.9
#> [49] otel_0.2.0 evaluate_1.0.5 viridisLite_0.4.3
#> [52] rlang_1.3.0 glue_1.8.1 jsonlite_2.0.0
#> [55] R6_2.6.1