Changes in version 1.4.2.9002 - Fix "Risk Differerence" typo in risk.times output - Report follow-up per treatment arm in the @info slot as info$followup.unique and info$followup.nonunique (per subgroup, mirroring info$outcome.unique / info$outcome.nonunique). Both are grouped by baseline treatment over expanded-data rows with an observed (non-NA) outcome - the person-time the outcome model is fit on. The non-unique table counts follow-up intervals (so the non-unique outcome counts divided by these give per-arm event rates); the unique table counts the distinct subjects contributing follow-up to each arm. Also shown in the printed diagnostic tables. - Speed up the hazard ratio calculation by fitting the Cox model with the survival C fitters directly on a prebuilt design matrix instead of coxph(formula, data), avoiding the model.frame/model.matrix rebuild on every bootstrap iteration: survival::coxph.fit() for the non-competing-event model and survival::agreg.fit() for the competing-event Fine-Gray (counting-process) model. The hazard ratio and CIs are unchanged. - Fix the competing-event Fine-Gray hazard fit to use the finegray() case weights (fgwt), which are required for a valid subdistribution-hazard estimate and were previously omitted. This is a no-op for the current hazard simulation (which has only administrative censoring, so all fgwt are 1) but corrects the estimate should the simulated data ever carry random censoring. - Report competing events per treatment arm in the @info slot as info$compevent.unique and info$compevent.nonunique, mirroring the structure of info$outcome.unique / info$outcome.nonunique. Both are grouped by baseline treatment; the non-unique table counts all competing event occurrences in the expanded data and the unique table counts distinct subjects who experienced the competing event. Both are NA when no compevent is specified. - From a SEQuential() fit, populate weight.statistics and outcome.model when hazard = TRUE. - Warn when the numerator and denominator weight models are given identical covariates. In that case the stabilized weights all equal 1 (i.e., no weighting), which is usually a typo in the denominator argument. - Improve the SEQuential() helpfile by adding a per-protocol example. - Add behavioural tests that selection.random = TRUE retains all treated trial-starts, subsamples control trial-starts to the requested selection.prob fraction, and is reproducible under a fixed seed; rename the previous smoke test that did not actually exercise the feature. - Add behavioural tests for; weight.lower/weight.upper truncation, weight.p99 truncation, followup.include/trial.include, followup.class, weight.lag_condition, followup.min/followup.max, and weight.eligible_cols. - Fix numerator() and denominator() returning NULL for every weighted model; they now return the fitted per-arm numerator/denominator weight models. - Document that weight truncation applies only to the outcome-model fit. - Pass the formula cache to inline.pred() in the weight models - Fix SEQOpts() argument ordering. Changes in version 1.4.2 (2026-05-21) - Remove mention of units from time in docs. - Improve memory usage in the bootstrapping. - Fix off-by-one labeling in survival output so that followup = k correctly represents survival after k intervals, adding a row at followup = survival.max + 1 for the final interval's estimate. - Fix expansion bug where subjects experiencing the outcome early were incorrectly carried forward with outcome=0 rows from subsequent periods by truncating each trial at the first event row (thanks, @francescazaccagnino) - Add expand.only option to SEQopts(). When TRUE, SEQuential() returns the expanded data.table directly and skips the analysis steps, for users who want to inspect or store the expanded dataset on its own. - Fix followup.spline = TRUE so the basis is genuinely non-linear. Splines are now built into the model formula via splines::ns() instead of being applied as a single-column transform of followup, and the new followup.spline.df option (default 4) controls the number of basis functions. The treatment-by-followup interaction now uses the same spline basis. Knots are baked from the full expanded followup once at fit time so the basis is identical at fit and prediction time across bootstraps and survival grids. Internally, formula column extraction now uses all.vars(), so user-supplied covariates may include ns(), bs(), I(), factor(), poly() etc. without breaking expansion. - Rename format.time() to format_time() because it wasn't an S3 method and hence was causing roxygen2 to write incorrect information in its helpfile. - Add package level helpfile and bump roxygen2 to 8.0.0. - Add parglm as an alternative GLM fitting backend. - Add warm starts for bootstrap GLM fits. - Add dataset size summary to verbose output. - Fix selection.random not being propagated from SEQopts() to internal parameters. - Cap data.table to 2 threads during tests and vignette builds, and skip the multisession parallel test on CRAN, to comply with CRAN's 2-core policy for checks. - Apply the SEQopts(nthreads = ...) setting to data.table during SEQuential(). Previously it was only used by the parglm backend and ignored in the default serial fastglm path, so data.table ran at its global default thread count. The previous global setting is restored when the call finishes. - Add risk.times option to SEQopts(). When km.curves = TRUE, risk difference and risk ratio (with CIs) are reported at each requested follow-up time, not just at the end of follow-up. Requested times are snapped to the latest available follow-up at or before them, and the final time is always included. The risk.comparison and risk.data tables gain a Followup column. - Fix factorize() to also coerce categorical (character) time-varying covariates - and their baseline (_bas) counterparts - to factors with levels fixed from the full data. Previously only fixed and treatment columns were factorized, so a character time-varying covariate could realise different level sets across bootstrap resamples and raise "newdata provided does not match fitted model" (most often in bootstrapped hazard analyses on larger samples or with a smaller bootstrap.sample). Numeric time-varying covariates are left unchanged. Changes in version 1.4.1 (2026-03-31) - Strip row-level vectors from fastglm objects to reduce weight.statistics memory usage and use a new internal function to print the coefficient table. - Strip row-level vectors from outcome models before storing in @outcome.model - Fix clean_fastglm to strip row-level vectors from nested multinomial weight models - No longer store survival.curve ggplot object; regenerate on demand via km_curve() - Removed several local() wrappers and made several code optimizations. - Improved documentation of the datasets in the package. - Implement check for perfect separation when fitting logistic regression models. - Fixed a bug in and make some improvements to internal.weights(). - Removed three unused slots in SEQopts(). - Add alt text to figures in vignettes. - Fixed SEQuential() time.col validation detecting and repairing non-zero-indexed time. - Add validation for eligible.col values - Add Paul Madley-Dowd as a co-author - Add check for overlapping time_varying.cols and fixed.cols - Add bounds validation for numeric and integer options in SEQopts() - Add check for duplicate id/time combinations in input data - Add check that treat.level values exist in the treatment column - Add validation for excused.cols flags - Add validation for followup.min/max ordering - Add binary check for outcome.col in non-hazard analyses - Add treat.level length validation for multinomial and non-multinomial analyses - Add binary validation for cense.eligible and weight.eligible_cols - Remove additional eligibility rows if not needed - Amend defaults for followup.min and weight.lower from -Inf to 0 - Fix bootstrapping for risk difference and risk ratio estimates to use paired per-iteration estimates - Optimizations to use less RAM - Fix duplicate scale_color_manual warning and plot.subtitle label bug in internal.plot() - Run doseresponse and ITT vignette chunks on GitHub Actions - Fix km_curve() returning list instead of ggplot for non-subgroup case - Fix km_curve() subtitle condition - Fix risk.comparison() CIs being NA with competing events - Move selection.random before expansion to reduce peak memory usage - Replace cbind() with := in expansion chain to avoid intermediate copy - Replace merge() with data.table native join in expansion data_list combine step - Replace rbind weight construction with copy+in-place to reduce peak memory - Drop wt and tmp columns immediately after weight is computed in all code paths - Remove redundant setDF calls in fast_model_matrix - Free WDT before bootstrap loop when data.return is FALSE - Use match(TRUE, ...) instead of which(...)[1] to find first switch/event per group - Replace sapply loop with single matrix multiply in multinomial prediction - Vectorise survival curve predictions into a single inline.pred call per treatment level - Free result list after extraction in internal_survival.R to reduce peak memory during bootstrap - Free analytic list after subgroup loop in SEQuential.R to reduce peak memory during survival curve computation - Avoid copy() in data_all construction and free data list in internal_survival.R to reduce peak memory during bootstrap - Filter to followup==0 before adding trialID in internal.survival to avoid copying entire expanded dataset - Trim base_DT to only prediction-needed columns before replication in internal.survival to reduce peak memory - Remove unnecessary copy(weight) for model.data in internal.weights since it is never modified in-place - Free baseDT after bootstrap loop in internal.survival to reduce peak memory during survival curve computation - Fix multinomial.summary: replace vcov() with fastglm $se field and add missing Coefficient column to prevent rbind mismatch - Add test_coverage.R with tests targeting uncovered code paths to increase coverage - Remove some no longer used variables and dead code - Further memory reduction optimizations Changes in version 1.3.6 (2026-02-16) - Added a set.seed() call in internal.hazard() to make main estimate reproducible. And also implement fix to ensure the bootstrapping, including both standard error and percentiles, is deterministic given the seed. Changes in version 1.3.5 (2026-02-05) - The hazard_ratio() function now correctly describes the estimate as "Hazard ratio" - The bootstrapping now collects the log hazard ratio instead of the hazard ratio because the log hazard ratio has better normality properties. - The covariates() function now returns more nicely formatted output (with spaces around ~ and + symbols in the model formulae) Changes in version 1.3.4 (2026-01-23) - Implemented some code optimizations - Replace a table() call with data.table's .N - Remove all gc() calls - Use a keyed index in bootstrapping - Remove some uses of copy() Changes in version 1.3.3 (2026-01-08) - Found and fixed a bug which caused excused switches to be overwritten. - Fix excusing override (#115) - Added visit option (#116)