| Title: | Clinical and Data Science Utilities |
|---|---|
| Description: | Utility functions for clinical trials and data science: descriptive statistics (summaries with optional custom stats, quartiles, median CI, geometric mean), SDTM/ADaM helpers (date conversion, supplemental merge, XPT batch load), table builders, batch data loading, and utilities (z-score, CV, SWOE, AUC, EAIR, Newcombe CIs, AR(1) simulation). |
| Authors: | Vladimir Larchenko [aut, cre], Igor Aleschenkov [aut] |
| Maintainer: | Vladimir Larchenko <[email protected]> |
| License: | GPL-3 |
| Version: | 0.6.1 |
| Built: | 2026-10-25 16:40:52 UTC |
| Source: | https://github.com/crow16384/kstools |
Aligns strings so that the decimal point or first space after digits is vertically aligned.
NA values are replaced by na.rep.
ks_aligndec(var, na.rep = "")ks_aligndec(var, na.rep = "")
var |
Character vector (or coercible to character). |
na.rep |
String to use for NA values. |
Character vector of the same length, with leading spaces so decimals align.
Computes AUC from concentration–time data using the linear or log-linear trapezoidal method. Handles grouped data via a character vector of grouping column names.
ks_auc(data, by = NULL, val, time, type = c("trapez", "log-trapez"))ks_auc(data, by = NULL, val, time, type = c("trapez", "log-trapez"))
data |
A data frame or tibble. |
by |
Character vector of grouping column names, e.g.
|
val |
Unquoted name of the concentration (value) column. |
time |
Unquoted name of the time column (numeric). |
type |
AUC method: |
Linear trapezoid (type = "trapez"):
Log-linear trapezoid (type = "log-trapez"):
The log-linear method is preferred during elimination phases where
concentrations decline exponentially. It requires strictly positive
concentrations; when two consecutive values are equal or one is
the interval automatically falls back to the linear rule.
Rows are sorted by c(by, time) before computing so the caller
does not need to pre-sort the data.
When by = NULL: a single numeric value.
When by is provided: a data.frame with one row
per group and an AUC column appended to the grouping columns.
ks_auc for the vectorised helper
## Single subject — linear trapezoid pk1 <- data.frame(conc = c(0, 4, 2, 1), time = c(0, 1, 2, 4)) ks_auc(pk1, val = conc, time = time) ## Two subjects — grouped pk2 <- data.frame( id = c("A","A","A","B","B","B"), conc = c(0, 4, 1, 0, 6, 2), time = c(0, 1, 2, 0, 1, 2) ) ks_auc(pk2, by = "id", val = conc, time = time)## Single subject — linear trapezoid pk1 <- data.frame(conc = c(0, 4, 2, 1), time = c(0, 1, 2, 4)) ks_auc(pk1, val = conc, time = time) ## Two subjects — grouped pk2 <- data.frame( id = c("A","A","A","B","B","B"), conc = c(0, 4, 1, 0, 6, 2), time = c(0, 1, 2, 0, 1, 2) ) ks_auc(pk2, by = "id", val = conc, time = time)
Loads all files of one type from a directory (or .zip) in batch.
Supported types are csv, xlsx, xpt, and rds.
Results can be returned as a named list, assigned to .GlobalEnv,
saved as individual .rds, and/or saved as one .RData.
ks_batch_load( data, type = c("csv", "xlsx", "xpt", "rds"), save.path = NULL, keep = FALSE, save.rdata = NULL, with_length = FALSE, recursive = FALSE )ks_batch_load( data, type = c("csv", "xlsx", "xpt", "rds"), save.path = NULL, keep = FALSE, save.rdata = NULL, with_length = FALSE, recursive = FALSE )
data |
Path to a directory or a |
type |
One of |
save.path |
Optional directory where loaded objects are saved as |
keep |
If |
save.rdata |
Optional path to a |
with_length |
Passed to |
recursive |
If |
Named list of loaded objects.
Iterates all members found by ks_xpt_content and either
assigns them into .GlobalEnv or writes them to disk.
ks_batch_load_from_single_xpt( file, action = "load", save_type = NULL, save_path = NULL )ks_batch_load_from_single_xpt( file, action = "load", save_type = NULL, save_path = NULL )
file |
Path to the XPORT file. |
action |
|
save_type |
For |
save_path |
For
|
action = "load"Each dataset is assigned into
.GlobalEnv using its lower-case member name.
action = "save", save_type = "rdata"All datasets are
saved together into a single .RData file at save_path
(the .RData extension is appended automatically). The global
environment is not modified.
action = "save", save_type = "rds"Each dataset is saved
as a separate .rds file inside the save_path directory.
action = "save", save_type = "parquet"Each dataset is saved as a separate Parquet file (requires the arrow package).
Members are extracted in parallel on non-Windows platforms
(mclapply); lapply is used
on Windows.
Invisibly returns a named list of all extracted datasets.
ks_xpt_content, ks_extract_single_xpt
## Not run: ## Load all members into .GlobalEnv ks_batch_load_from_single_xpt("study.xpt", action = "load") ## Save each member as .rds ks_batch_load_from_single_xpt("study.xpt", action = "save", save_type = "rds", save_path = "data/") ## End(Not run)## Not run: ## Load all members into .GlobalEnv ks_batch_load_from_single_xpt("study.xpt", action = "load") ## Save each member as .rds ks_batch_load_from_single_xpt("study.xpt", action = "save", save_type = "rds", save_path = "data/") ## End(Not run)
ks_batch_load with type = "xlsx"
Calls ks_batch_load with type = "xlsx". Provided for
compatibility with older scripts that used the xlsx-specific function name.
ks_batch_load_xlsx( data, save.path = NULL, keep = FALSE, save.rdata = NULL, recursive = FALSE )ks_batch_load_xlsx( data, save.path = NULL, keep = FALSE, save.rdata = NULL, recursive = FALSE )
data |
Path to a directory or a |
save.path |
Optional directory where loaded objects are saved as |
keep |
If |
save.rdata |
Optional path to a |
recursive |
If |
Named list of loaded objects (see ks_batch_load).
Fits the standard two-period, two-sequence crossover model 'response ~ sequence + treatment + period' (subject nested in sequence in the random term, 'random = ~ seq | subject') and returns the test-minus-reference treatment difference with its confidence interval. The sequence main effect absorbs carryover. This is the R twin of the SAS 'PROC MIXED' bioequivalence analysis in the R tricks article (verified: estimate 0.0145, SE 0.0142, df 7, matching SAS 'CL=DF=7').
ks_be_2x2( data, response, trt, test, reference, seq, period, subject, log = TRUE, conf.level = 0.9 )ks_be_2x2( data, response, trt, test, reference, seq, period, subject, log = TRUE, conf.level = 0.9 )
data |
Data frame with one row per subject-period. |
response |
Unquoted name of the numeric response column. |
trt |
Unquoted name of the treatment column (factor with exactly the two levels named in 'test' and 'reference'). |
test, reference
|
Character labels of the test and reference treatment levels (e.g. '"T"', '"R"'). |
seq, period, subject
|
Unquoted names of sequence, period, and subject columns. |
log |
Apply 'log()' to the response before fitting (default 'TRUE', standard for BE on concentration data). Requires all responses positive. |
conf.level |
Confidence level; BE conventionally uses '0.90'. |
A list with 'estimate', 'se', 'df', 't', 'p.value', 'lcl', 'ucl' (log scale when 'log = TRUE'), plus 'ratio', 'ratio.lcl', 'ratio.ucl' on the original scale when 'log = TRUE'.
cross <- data.frame( per = as.factor(rep(c(1, 2), 9)), trt = as.factor(c(rep(c("T", "R"), 4), rep(c("R", "T"), 5))), seq = as.factor(c(rep(1, 8), rep(2, 10))), cow = as.factor(rep(1:9, each = 2)), my = c(29.9, 27.8, 54.0, 49.7, 41.6, 38.4, 28.5, 26.5, 22.2, 21.4, 55.5, 49.1, 43.5, 41.3, 33.2, 34.3, 18.2, 17.1)) fit <- ks_be_2x2(cross, response = my, trt = trt, test = "T", reference = "R", seq = seq, period = per, subject = cow) c(TvR_log = fit$estimate, ratio = fit$ratio, lcl = fit$ratio.lcl, ucl = fit$ratio.ucl)cross <- data.frame( per = as.factor(rep(c(1, 2), 9)), trt = as.factor(c(rep(c("T", "R"), 4), rep(c("R", "T"), 5))), seq = as.factor(c(rep(1, 8), rep(2, 10))), cow = as.factor(rep(1:9, each = 2)), my = c(29.9, 27.8, 54.0, 49.7, 41.6, 38.4, 28.5, 26.5, 22.2, 21.4, 55.5, 49.1, 43.5, 41.3, 33.2, 34.3, 18.2, 17.1)) fit <- ks_be_2x2(cross, response = my, trt = trt, test = "T", reference = "R", seq = seq, period = per, subject = cow) c(TvR_log = fit$estimate, ratio = fit$ratio, lcl = fit$ratio.lcl, ucl = fit$ratio.ucl)
Convenience wrapper around boot: resamples, computes a user statistic, and returns a percentile (BCa when defined) confidence interval plus the point estimate — one line instead of the usual 'boot()' / 'boot.ci()' plumbing in report code.
ks_boot_ci( x, statistic, R = 2000L, conf = 0.95, seed = NULL, type = c("perc", "basic", "norm", "bca") )ks_boot_ci( x, statistic, R = 2000L, conf = 0.95, seed = NULL, type = c("perc", "basic", "norm", "bca") )
x |
Data resampled by row: a vector, matrix, or data frame. |
statistic |
Function with signature 'function(data, indices)' returning a **single numeric value** (scalar). 'indices' select rows of 'x'. |
R |
Number of bootstrap replicates (default '2000'). |
conf |
Confidence level (default '0.95'). |
seed |
Integer seed for reproducibility (default 'NULL'). |
type |
Interval type: '"perc"' (percentile, default), '"basic"', '"norm"', or '"bca"'. |
The statistic must be scalar-valued; 'ks_boot_ci()' checks the shape of the replicate matrix and aborts otherwise. BCa can be undefined (e.g. degenerate statistics on small samples) — the function then falls back to the percentile interval with a warning.
A list with 'estimate', 'std.error', 'lcl', 'ucl', 'type', 'conf.level', the full 'boot.ci' list in '$ci', and the raw 'boot' object in '$replicates'.
set.seed(1) x <- rnorm(50, 5, 2) ks_boot_ci(x, function(d, i) mean(d[i])) # ratio of two columns (paired resampling) d <- data.frame(a = rnorm(60, 5, 1), b = rnorm(60, 3, 1)) ks_boot_ci(d, function(dd, i) mean(dd$a[i]) / mean(dd$b[i]), R = 999, seed = 42)set.seed(1) x <- rnorm(50, 5, 2) ks_boot_ci(x, function(d, i) mean(d[i])) # ratio of two columns (paired resampling) d <- data.frame(a = rnorm(60, 5, 1), b = rnorm(60, 3, 1)) ks_boot_ci(d, function(dd, i) mean(dd$a[i]) / mean(dd$b[i]), R = 999, seed = 42)
Reads a column that contains "code=label" entries, splits them on
=, and passes the resulting lookup to ksformat::fnew() as a
numeric format.
ks_code_to_fmt(dt, col, name = NULL)ks_code_to_fmt(dt, col, name = NULL)
dt |
A data frame containing the coded column. |
col |
|
name |
Character scalar. Name to register the format under via
|
Invisibly returns the result of ksformat::fnew() (called
for its side-effect of registering the format).
## Not run: # dt has a column DECODE with entries like "1=Male", "2=Female" ks_code_to_fmt(dm, DECODE) ## End(Not run)## Not run: # dt has a column DECODE with entries like "1=Male", "2=Female" ks_code_to_fmt(dm, DECODE) ## End(Not run)
Element-wise '==' returns 'NA' whenever either side is missing, which makes it unusable for comparing vectors that legitimately contain 'NA'. 'ks_compare_na()' treats 'NA' as an ordinary value, so two missing entries at the same position are considered **equal** — the R twin of SAS's 'NULL=' comparison operators.
ks_compare_na(x, y)ks_compare_na(x, y)
x, y
|
Vectors compared element by element; the shorter is recycled. Works for numeric, character, and factor vectors (levels need not match). |
A logical vector with the common recycled length, never 'NA'.
ks_sum_na for NA-aware arithmetic.
ks_compare_na(c(NA, "A", "B"), c(NA, "A", "C")) ks_compare_na(c(1, NA, 3), c(1, 2, NA))ks_compare_na(c(NA, "A", "B"), c(NA, "A", "C")) ks_compare_na(c(1, NA, 3), c(1, 2, NA))
Determines the filename of the currently executing R script by checking two sources in order:
ks_current_filename()ks_current_filename()
source() call — inspects the call stack for the closest
source() frame and reads its filename environment variable.
CLI argument — inspects the command-line arguments for a
-f or --file= flag (as used by Rscript foo.R or
R --slave -f foo.R).
Only the basename of the resolved path is returned.
A character string with the base name of the current script.
Returns an empty string ("") if the filename cannot be determined
from either source (e.g. when running interactively in the R console).
## Not run: # Inside a sourced file or a script run with Rscript: ks_current_filename() #> "my_script.R" # In an interactive session where no script name is available: ks_current_filename() #> "" ## End(Not run)## Not run: # Inside a sourced file or a script run with Rscript: ks_current_filename() #> "my_script.R" # In an interactive session where no script name is available: ks_current_filename() #> "" ## End(Not run)
Calculates a coefficient of variation for given vector
ks_cv(x, na.rm = TRUE)ks_cv(x, na.rm = TRUE)
x |
Numeric vector |
na.rm |
Flag for ignoring NAs. Default = TRUE |
ks_cv(c(1,2,3,NA,5,NA,7,2,3,2))ks_cv(c(1,2,3,NA,5,NA,7,2,3,2))
Removes objects whose names start with t. or o. (if o=TRUE), or only t. (if o=FALSE).
ks_del_tmp(o = TRUE)ks_del_tmp(o = TRUE)
o |
If |
Produces a tidy long-format count summary for data coded with a two-level hierarchy, such as MedDRA System Organ Class / Preferred Term (adverse events, medical history) or ATC group / drug (medications).
The function counts distinct subjects (n) and total events (e)
at three levels:
Overall total — subjects with at least one record.
Level-1 total (e.g. SOC) — subjects in that top-level group.
Level-2 detail (e.g. PT) — subjects with that specific lower-level code.
The function returns raw counts only; apply ks_n_pct to
the n column afterwards to produce formatted "n (pct%)"
strings. Rows where <l2> is NA are header / SOC-total rows
(i.e. they would be rendered bold in a report).
The function is intentionally single-arm: call it once per analysis
population or treatment arm and join the results on the <l1>,
<l2>, ord0, and ord key columns.
ks_desc_coded( data, id = usubjid, l1 = meddra_soc, l2 = meddra_pt, tot_label = "Total subjects" )ks_desc_coded( data, id = usubjid, l1 = meddra_soc, l2 = meddra_pt, tot_label = "Total subjects" )
data |
A data frame or tibble containing the coded event records (one row per event, not one row per subject). |
id |
<['data-masking'][dplyr::dplyr_data_masking]> Bare name of the column holding the unique subject identifier. |
l1 |
<['data-masking'][dplyr::dplyr_data_masking]> Bare name of the
top-level code column (e.g. |
l2 |
<['data-masking'][dplyr::dplyr_data_masking]> Bare name of the
lower-level code column (e.g. |
tot_label |
Character string used as the label in the |
A tibble with one row per display line,
ordered by top-level code then lower-level code:
ord0, ord
Integer sort keys.
ord0 = 1 / ord = 1: overall total;
ord0 = 2 / ord = 2: level-1 (SOC) total;
ord0 = 2 / ord = 3: level-2 (PT) detail row.
<l1 name>Top-level code value. Contains tot_label
for the overall-total row; the SOC name for all other rows.
<l2 name>Lower-level code value. NA for
overall-total and level-1-total rows (these are the bold / header
rows); PT name for detail rows.
nDistinct subject count for the group.
eEvent count (number of source rows for the group).
ks_n_pct for formatting n as
"n (pct%)" once the population size is known.
ae <- data.frame( usubjid = c("01","01","02","03","03","04"), soc = c("Cardiac","Cardiac","Nervous","Cardiac","Nervous","Nervous"), pt = c("Palp","Tachy","Head","Palp","Dizzy","Head"), stringsAsFactors = FALSE ) ## Raw counts (N unknown at this stage) res <- ks_desc_coded(ae, id = usubjid, l1 = soc, l2 = pt) res ## Add formatted value column once pop is known dplyr::mutate(res, value = ks_n_pct(n, totals = 10)) ## Multi-arm via group_modify(); pop looked up per arm pop <- c(A = 5, B = 5) ae$trt <- rep(c("A","B"), each = 3) ae |> dplyr::group_by(trt) |> dplyr::group_modify( ~ ks_desc_coded(.x, id = usubjid, l1 = soc, l2 = pt) |> dplyr::mutate(value = ks_n_pct(n, pop[.y$trt])) ) |> dplyr::ungroup()ae <- data.frame( usubjid = c("01","01","02","03","03","04"), soc = c("Cardiac","Cardiac","Nervous","Cardiac","Nervous","Nervous"), pt = c("Palp","Tachy","Head","Palp","Dizzy","Head"), stringsAsFactors = FALSE ) ## Raw counts (N unknown at this stage) res <- ks_desc_coded(ae, id = usubjid, l1 = soc, l2 = pt) res ## Add formatted value column once pop is known dplyr::mutate(res, value = ks_n_pct(n, totals = 10)) ## Multi-arm via group_modify(); pop looked up per arm pop <- c(A = 5, B = 5) ae$trt <- rep(c("A","B"), each = 3) ae |> dplyr::group_by(trt) |> dplyr::group_modify( ~ ks_desc_coded(.x, id = usubjid, l1 = soc, l2 = pt) |> dplyr::mutate(value = ks_n_pct(n, pop[.y$trt])) ) |> dplyr::ungroup()
Parses ISO-like date strings (YYYY, YYYY-MM, YYYY-MM-DD) with optional
NA/UN for month or day, and converts to Date. Missing month/day become 1.
ks_dtc2dtn(x)ks_dtc2dtn(x)
x |
Character vector of date strings (e.g. |
A Date vector.
Computes the two- or one-sided Dunnett critical value for 'k'
comparisons against a common control by inverting the equicorrelated
multivariate Student-t CDF ('mnormt::sadmvt' / 'mnormt::pmt' via
uniroot). For balanced designs the control-correlation is
'rho = 1/2'. Numerically equivalent to 'nCDunnett::qNCDun' (2.5067 vs
2.506845 at 'p = 0.95, df = 24, k = 3').
ks_dunnett_crit(p, df, k, rho = 0.5, type = c("two-sided", "one-sided"))ks_dunnett_crit(p, df, k, rho = 0.5, type = c("two-sided", "one-sided"))
p |
Coverage probability (e.g. '0.95'). |
df |
Error degrees of freedom. |
k |
Number of treatments compared to the control ('k >= 2'). |
rho |
Correlation between pairs of contrasts; '1/2' for balanced Dunnett designs. |
type |
'"two-sided"' (default) or '"one-sided"'. |
A single numeric critical value.
ks_dunnett_crit(p = 0.95, df = 24, k = 3, rho = 0.5) ks_dunnett_crit(p = 0.95, df = 24, k = 3, rho = 0.5, type = "one-sided")ks_dunnett_crit(p = 0.95, df = 24, k = 3, rho = 0.5) ks_dunnett_crit(p = 0.95, df = 24, k = 3, rho = 0.5, type = "one-sided")
Computes the difference (Test Control) of two exposure-adjusted
incidence rates (EAIRs) and its confidence interval using the score-based
method of Miettinen & Nurminen (1985).
ks_eairdiff_mn(n1, n2, T1, T2, alpha = 0.05)ks_eairdiff_mn(n1, n2, T1, T2, alpha = 0.05)
n1, n2
|
Non-negative integer event counts for Test and Control. |
T1, T2
|
Positive person-time at risk for Test and Control. |
alpha |
Significance level for the two-sided CI. Default |
The CI is found by inverting the score statistic. For each candidate
rate difference , the pooled estimate
is obtained by solving the MLE equations under
the constraint , and the score
chi-squared is compared to . The two roots of
this equation (found via uniroot.all) are the
lower and upper confidence bounds.
The search interval for roots is fixed at c(-1, 1), appropriate
when incidence rates are measured in events per person-time unit and are
well below 1. If rates exceed 1, expand the interval manually by
calling uniroot.all directly.
The function is vectorised: when n1, n2, T1,
T2 are vectors of equal length, one row per element is returned.
A matrix with one row per input pair and three named columns:
delta (point estimate), lcl (lower bound),
ucl (upper bound). Bounds are NA when the score
equation has fewer than two roots in (-1, 1).
Miettinen O, Nurminen M (1985). Comparative analysis of two rates. Statistics in Medicine, 4(2), 213–226.
ks_eairdiff_strata_mn,
uniroot.all
## Single comparison: Test rate = 0.10, Control rate = 0.05 ks_eairdiff_mn(n1 = 10, n2 = 5, T1 = 100, T2 = 100) ## Equal rates → delta = 0, CI straddles zero ks_eairdiff_mn(n1 = 5, n2 = 5, T1 = 100, T2 = 100) ## Vectorised over multiple time-point comparisons ks_eairdiff_mn( n1 = c(10, 20, 8), n2 = c( 5, 15, 6), T1 = c(100, 150, 90), T2 = c(100, 150, 90) ) ## 90 %% CI ks_eairdiff_mn(n1 = 10, n2 = 5, T1 = 100, T2 = 100, alpha = 0.10)## Single comparison: Test rate = 0.10, Control rate = 0.05 ks_eairdiff_mn(n1 = 10, n2 = 5, T1 = 100, T2 = 100) ## Equal rates → delta = 0, CI straddles zero ks_eairdiff_mn(n1 = 5, n2 = 5, T1 = 100, T2 = 100) ## Vectorised over multiple time-point comparisons ks_eairdiff_mn( n1 = c(10, 20, 8), n2 = c( 5, 15, 6), T1 = c(100, 150, 90), T2 = c(100, 150, 90) ) ## 90 %% CI ks_eairdiff_mn(n1 = 10, n2 = 5, T1 = 100, T2 = 100, alpha = 0.10)
Stratified version of ks_eairdiff_mn: pools evidence
across strata using inverse-variance-like weights and returns a single
CI for the common rate difference.
ks_eairdiff_strata_mn(n1, n2, T1, T2, alpha = 0.05)ks_eairdiff_strata_mn(n1, n2, T1, T2, alpha = 0.05)
n1, n2
|
Numeric vectors of event counts per stratum (Test, Control). |
T1, T2
|
Numeric vectors of person-time per stratum (Test, Control). All four vectors must have the same length. |
alpha |
Significance level for the two-sided CI. Default |
Each stratum contributes a weight
and the combined score statistic is
.
The point estimate delta is the unweighted arithmetic mean of the
stratum-specific rate differences.
Strata with NA in any of their inputs are silently dropped via
na.rm = TRUE in the stratum sums.
A named numeric vector of length 3:
delta (point estimate), lcl (lower bound),
ucl (upper bound).
Miettinen O, Nurminen M (1985). Comparative analysis of two rates. Statistics in Medicine, 4(2), 213–226.
## Two strata ks_eairdiff_strata_mn( n1 = c(10, 5), n2 = c( 5, 3), T1 = c(100, 80), T2 = c(100, 70) ) ## Single stratum — equivalent to ks_eairdiff_mn() up to point estimate ks_eairdiff_strata_mn(n1 = 10, n2 = 5, T1 = 100, T2 = 100) ## 90 %% CI ks_eairdiff_strata_mn( n1 = c(10, 5), n2 = c(5, 3), T1 = c(100, 80), T2 = c(100, 70), alpha = 0.10 )## Two strata ks_eairdiff_strata_mn( n1 = c(10, 5), n2 = c( 5, 3), T1 = c(100, 80), T2 = c(100, 70) ) ## Single stratum — equivalent to ks_eairdiff_mn() up to point estimate ks_eairdiff_strata_mn(n1 = 10, n2 = 5, T1 = 100, T2 = 100) ## 90 %% CI ks_eairdiff_strata_mn( n1 = c(10, 5), n2 = c(5, 3), T1 = c(100, 80), T2 = c(100, 70), alpha = 0.10 )
Reads a single member from the file by splicing the file-level header
bytes onto the member's raw bytes, then passing the combined buffer to
read_xpt.
ks_extract_single_xpt(name, file, content)ks_extract_single_xpt(name, file, content)
name |
Character name of the member to read (must match a name in
|
file |
Path to the XPORT file. |
content |
Named list returned by |
A valid XPORT stream requires both the 240-byte file header (three 80-byte
LIBV8 records) and the member data. This function prepends the file header
so that haven::read_xpt can parse the member in isolation.
A tibble (as returned by
read_xpt).
ks_xpt_content,
ks_batch_load_from_single_xpt
## Not run: content <- ks_xpt_content("study.xpt") ae <- ks_extract_single_xpt("ae", "study.xpt", content) ## End(Not run)## Not run: content <- ks_xpt_content("study.xpt") ae <- ks_extract_single_xpt("ae", "study.xpt", content) ## End(Not run)
Computes the geometric mean of a numeric vector and an approximate
confidence interval based on the Student -distribution on the
log scale (assuming log-transformed values are approximately normal).
ks_geom_mean(x, na.rm = TRUE, zero.propagate = FALSE, alpha = 0.05)ks_geom_mean(x, na.rm = TRUE, zero.propagate = FALSE, alpha = 0.05)
x |
Numeric vector (non-negative values; see Details). |
na.rm |
Logical. If |
zero.propagate |
Logical. If |
alpha |
Significance level for the two-sided CI (coverage nominal
|
The geometric mean of positive values is
.
If zero.propagate = FALSE, only are used. If there are
no positive values, the geometric mean is 0 (all zeros) or
NA (empty or invalid input after removing NAs).
Negative values are not allowed: the function returns NaN for
geom.mean (and CI components) because the geometric mean is not
defined in the reals.
The confidence interval is
where for the positive observations used, is the
sample SD of , is their count, and . For
the limits are NA.
A list with components:
geom.meanGeometric mean (numeric scalar).
geom.sdGeometric standard deviation,
on the positive data used (same as
common PK notation; NA if not defined).
geom.mean.lclLower limit of the % CI.
geom.mean.uclUpper limit of the % CI.
Kirkwood, T. B. (1979). Geometric means and measures of dispersion. Biometrics, 35(4), 908–909.
x <- abs(rnorm(100,10,5)) ks_geom_mean(x)x <- abs(rnorm(100,10,5)) ks_geom_mean(x)
Reads one or more data files from path and returns them as
tibbles. A single-domain call returns a tibble; a
multi-domain call returns a named list of tibbles (names are the
lowercased domain names).
ks_get_data( ..., path = getOption("path.data", "."), type = c("auto", "rds", "xpt", "xlsx", "csv", "csvks"), xpt_reader = getOption("kstools.xpt.reader", "haven") )ks_get_data( ..., path = getOption("path.data", "."), type = c("auto", "rds", "xpt", "xlsx", "csv", "csvks"), xpt_reader = getOption("kstools.xpt.reader", "haven") )
... |
Unquoted (or quoted) domain names, e.g. |
path |
Directory containing the data files.
Defaults to |
type |
File type: |
xpt_reader |
Reader to use for |
Supported formats: .rds (R native), .xpt (SAS XPORT),
.xlsx (Microsoft Excel), .csv (plain CSV),
.csv with KeyStat comments (type = "csvks").
Domain names can be passed as bare names (dm, ae) or as quoted
strings ("dm", "ae"); both forms are accepted. Lookup against the
directory is case-insensitive for both stem and extension, so
ks_get_data(dm) and ks_get_data(DM) match any of
dm.xpt, DM.XPT, Dm.Xpt, etc.
When type = "auto" (default), the function scans the directory for
files matching the requested domains and determines the format from the
extension of the first match. All requested domains must use the same
format.
The default search path is read from
getOption("path.data", "."). Set this option in
.Rprofile to avoid repeating the path in every call:
options(path.data = "/my/project/data")
For .xpt (SAS XPORT) files, the reader is selected by the
xpt_reader argument (default from option
kstools.xpt.reader, fallback "haven"):
"haven"(default) use haven::read_xpt().
"internal"use the package-internal
ks_read_xpt() parser. This avoids the
“ignored N trailing byte(s) after row parsing” warning emitted
by haven on some XPORT files. For multi-member XPORT files,
the domain slot will contain a named list of tibbles (one per
member).
When two or more files in path share the same stem but differ only
in case (e.g. both dm.xpt and DM.xpt exist), the resolution
strategy is controlled by the global option kstools.get_data.case:
"error"(default) abort with a message listing the candidates.
"first"pick the first match in alphabetical order.
"lower"require a candidate whose file stem
file_path_sans_ext equals the domain in full lowercase
(extension is always matched case-insensitively); otherwise abort.
"upper"require a candidate whose file stem equals the domain in full uppercase (extension case-insensitive); otherwise abort.
For a single domain, a tibble. For two or more domains, a named list of tibbles keyed by the lowercased domain name.
## Not run: ## Load a single domain options(path.data = "/project/data") dm <- ks_get_data(dm) ## Case-insensitive lookup: works whether folder has dm.xpt or DM.XPT dm <- ks_get_data(DM) ## Load multiple domains: returns a named list dat <- ks_get_data(dm, ae, lb, path = "/project/data/xpt") head(dat$dm) ## Force a specific file type dm <- ks_get_data(dm, path = "/project/data", type = "xpt") ## Resolve case-duplicate filenames options(kstools.get_data.case = "lower") # prefer dm.xpt over DM.xpt dm <- ks_get_data(dm, path = "/project/data") ## Quoted strings are also accepted dat <- ks_get_data("dm", "ae", path = "/project/data") ## Use the internal XPT reader to avoid haven's trailing-byte warning options(kstools.xpt.reader = "internal") dm <- ks_get_data(dm, type = "xpt") ## Or override per call without changing the global option dm <- ks_get_data(dm, type = "xpt", xpt_reader = "internal") ## End(Not run)## Not run: ## Load a single domain options(path.data = "/project/data") dm <- ks_get_data(dm) ## Case-insensitive lookup: works whether folder has dm.xpt or DM.XPT dm <- ks_get_data(DM) ## Load multiple domains: returns a named list dat <- ks_get_data(dm, ae, lb, path = "/project/data/xpt") head(dat$dm) ## Force a specific file type dm <- ks_get_data(dm, path = "/project/data", type = "xpt") ## Resolve case-duplicate filenames options(kstools.get_data.case = "lower") # prefer dm.xpt over DM.xpt dm <- ks_get_data(dm, path = "/project/data") ## Quoted strings are also accepted dat <- ks_get_data("dm", "ae", path = "/project/data") ## Use the internal XPT reader to avoid haven's trailing-byte warning options(kstools.xpt.reader = "internal") dm <- ks_get_data(dm, type = "xpt") ## Or override per call without changing the global option dm <- ks_get_data(dm, type = "xpt", xpt_reader = "internal") ## End(Not run)
Returns a two-column tibble with variable names and their "label"
attributes (as set by labelled, haven, or manual
attr(x, "label") <- ... calls). Columns without a label attribute
are returned as NA_character_.
ks_get_labels(x)ks_get_labels(x)
x |
A data frame (or tibble) whose column labels you want. |
A tibble with columns:
Character. Column name taken from names(x).
Character. Value of the "label" attribute, or
NA if absent.
df <- data.frame(a = 1:3, b = letters[1:3]) attr(df$a, "label") <- "Numeric variable" ks_get_labels(df)df <- data.frame(a = 1:3, b = letters[1:3]) attr(df$a, "label") <- "Numeric variable" ks_get_labels(df)
For each group defined by by, determines the maximum number of
decimal digits observed in var (after rounding to 4 decimal places)
and joins the result back as a new column NUMDEC.
ks_get_numdec(data, var, by)ks_get_numdec(data, var, by)
data |
A data frame or tibble. |
var |
Character string naming the numeric column to inspect. |
by |
Character vector of grouping column names. |
The decimal-digit count is derived by formatting the rounded value with
format(scientific = FALSE), stripping trailing zeros, and counting
characters after the decimal point. NA values in var are
ignored when computing the group maximum.
data with an additional integer column NUMDEC
giving the group-level maximum decimal count.
d <- data.frame( grp = c("A", "A", "B", "B"), val = c(1.1, 2.34, 3.0, 4.567) ) ks_get_numdec(d, "val", "grp")d <- data.frame( grp = c("A", "A", "B", "B"), val = c(1.1, 2.34, 3.0, 4.567) ) ks_get_numdec(d, "val", "grp")
Groups fitted probabilities into quantile-based bins, cross-tabulates observed versus expected successes/failures, and returns the chi-squared statistic with 'g - 2' degrees of freedom (Hosmer & Lemeshow, 1980). A high p-value indicates no evidence of miscalibration.
ks_hosmer_lemeshow(y, yhat, g = 10)ks_hosmer_lemeshow(y, yhat, g = 10)
y |
Binary response (0/1), same length as 'yhat'. |
yhat |
Fitted probabilities from a logistic model. |
g |
Number of groups (default 10 = deciles). |
A list with components chisq and p.value.
Hosmer, D. and Lemeshow, S. (1980). Goodness of fit tests for the multiple logistic regression model. Biometrics, 36, 1045–1054.
set.seed(1) d <- data.frame(x = rnorm(200)) d$y <- rbinom(200, 1, plogis(0.8 * d$x)) m <- glm(y ~ x, binomial, data = d) ks_hosmer_lemeshow(d$y, fitted(m))set.seed(1) d <- data.frame(x = rnorm(200)) d$y <- rbinom(200, 1, plogis(0.8 * d$x)) m <- glm(y ~ x, binomial, data = d) ks_hosmer_lemeshow(d$y, fitted(m))
Parses truncated ISO 8601 date or datetime character strings and imputes
missing components according to the chosen direction, returning a
tibble with the resulting POSIXct and an imputation flag.
ks_impute_dtc(x, direction = "no", tz = "UTC", ref = NULL)ks_impute_dtc(x, direction = "no", tz = "UTC", ref = NULL)
x |
Character vector of ISO 8601 truncated date/datetime strings.
Accepted formats: |
direction |
One of |
tz |
IANA timezone string applied to the output |
ref |
|
## Parsing
Each element of x is matched against a regular expression that
captures year, month, day, hour, minute, and second independently.
The date-time separator may be T or a single space. Trailing
timezone designators (Z, +HH:MM, -HHMM) are
silently stripped; all output is placed in the timezone given by
tz. Elements that do not match the pattern produce
dt = NA and imputation_flag = NA.
## Completeness and imputation flags
A string is considered complete when year, month, and day are all
present, and — if a time separator (T or space) is present —
hour and minute are also present. Complete strings are assembled
directly without imputation (flag NA), except for
date-only strings under direction = "end" (Case D, see below).
The imputation_flag records the most significant component that
was imputed:
| Flag | Meaning |
"M" |
Month was imputed (year was the only component present). |
"D" |
Day was imputed (year and month were present). |
"H" |
Hour (and minute) were imputed. |
"S" |
Minute was imputed (hour was already present). |
NA |
No imputation was performed. |
## Direction
"start"Each missing component is filled with the
earliest plausible value: month 1, day 1,
hour 0, minute 0.
"end"Each missing component is filled with the
latest plausible value: month 12, day last
day of that month (correctly handling leap years), hour 23,
minute 59.
"no"No imputation is performed. Incomplete strings
return dt = NA and imputation_flag = NA.
## Date-only strings with a directional imputation (Cases D and E)
A date-only string such as "2020-04-06" contains no time
separator and is otherwise complete. Because the time was not recorded,
it is always considered imputed when direction != "no":
direction = "end")Time is imputed as
23:59:00. Flag: "H".
direction = "start")Time is imputed as
00:00:00. Flag: "H".
A reference-date match (Case C) overrides both Case D and Case E.
## Reference-guided imputation
When ref is provided and direction != "no", the function
first checks whether the parsed year (and optionally month and day)
match the corresponding reference value before applying the directional
fallback. Cases are evaluated in priority order:
When the day (and time) are absent but year and month equal the
reference, day, hour, and minute are taken from ref.
Directional start/end values are not used.
Typical flag: "D".
When month (and therefore day and time) are absent but the year
equals the reference year, month, day, hour, and minute are all
taken from ref.
Typical flag: "M".
A date-only string whose year, month, and day all equal the
reference has its time filled from ref. This takes priority
over the Case D / Case E directional time fallback.
Flag: "H".
If none of Cases A–C applies — because ref is NULL,
the year does not match, or the month does not match — the
directional start/end values are used as described above.
ref is recycled to length(x) via rep_len().
A Date ref is coerced to POSIXct in timezone tz
before its components are extracted.
A tibble with one row per element of
x and two columns:
dtPOSIXct in timezone tz, or NA
when the year is absent or imputation could not be completed.
imputation_flagcharacter; the highest-priority
component that was imputed: "M" (month), "D" (day),
"H" (hour), "S" (minute). NA when no imputation
was performed.
# --- direction = "no" ------------------------------------------------------- # Complete datetime — no imputation, flag NA ks_impute_dtc("2020-03-15T10:30:00", "no") # Truncated with direction = "no" — returns NA (nothing to impute) ks_impute_dtc("2020-03", "no") # Invalid string ks_impute_dtc("not-a-date", "no") # --- direction = "start" ---------------------------------------------------- # Year only → 2020-01-01 00:00:00, flag "M" ks_impute_dtc("2020", "start") # Year-month → first day of month, flag "D" ks_impute_dtc("2020-03", "start") # Date-only + end → end-of-day 23:59, flag "H" (Case D) ks_impute_dtc("2020-03-15", "end") # Date-only + start → midnight 00:00, flag "H" (Case E) ks_impute_dtc("2020-03-15", "start") # --- direction = "end" ------------------------------------------------------ # End of February in a leap year → 2020-02-29 23:59:00, flag "D" ks_impute_dtc("2020-02", "end") # End of March → 2020-03-31 23:59:00, flag "D" ks_impute_dtc("2020-03", "end") # --- ref: same-period guided imputation ------------------------------------ ref <- as.POSIXct("2020-03-15 10:30:00", tz = "UTC") # Case A — day missing, year+month match ref → take day/time from ref ks_impute_dtc("2020-03", "start", ref = ref) # Case C overrides D — date matches ref → take time from ref, not 23:59 ks_impute_dtc("2020-03-15", "end", ref = ref) # ref year/month mismatch → directional fallback used instead ks_impute_dtc("2020-04", "start", ref = ref) # --- Vectorised with dplyr::bind_cols() ------------------------------------ ## Not run: library(dplyr) df <- tibble::tibble( dtc = c("2020-03-15T08:00", "2020-03-15", "2020-03", "2020", "bad"), ref = as.POSIXct(c(NA, "2020-03-15 14:30:00", "2020-03-10 08:00:00", "2020-05-01 00:00:00", NA), tz = "UTC") ) dplyr::bind_cols(df, ks_impute_dtc(df$dtc, "start", ref = df$ref)) ## End(Not run)# --- direction = "no" ------------------------------------------------------- # Complete datetime — no imputation, flag NA ks_impute_dtc("2020-03-15T10:30:00", "no") # Truncated with direction = "no" — returns NA (nothing to impute) ks_impute_dtc("2020-03", "no") # Invalid string ks_impute_dtc("not-a-date", "no") # --- direction = "start" ---------------------------------------------------- # Year only → 2020-01-01 00:00:00, flag "M" ks_impute_dtc("2020", "start") # Year-month → first day of month, flag "D" ks_impute_dtc("2020-03", "start") # Date-only + end → end-of-day 23:59, flag "H" (Case D) ks_impute_dtc("2020-03-15", "end") # Date-only + start → midnight 00:00, flag "H" (Case E) ks_impute_dtc("2020-03-15", "start") # --- direction = "end" ------------------------------------------------------ # End of February in a leap year → 2020-02-29 23:59:00, flag "D" ks_impute_dtc("2020-02", "end") # End of March → 2020-03-31 23:59:00, flag "D" ks_impute_dtc("2020-03", "end") # --- ref: same-period guided imputation ------------------------------------ ref <- as.POSIXct("2020-03-15 10:30:00", tz = "UTC") # Case A — day missing, year+month match ref → take day/time from ref ks_impute_dtc("2020-03", "start", ref = ref) # Case C overrides D — date matches ref → take time from ref, not 23:59 ks_impute_dtc("2020-03-15", "end", ref = ref) # ref year/month mismatch → directional fallback used instead ks_impute_dtc("2020-04", "start", ref = ref) # --- Vectorised with dplyr::bind_cols() ------------------------------------ ## Not run: library(dplyr) df <- tibble::tibble( dtc = c("2020-03-15T08:00", "2020-03-15", "2020-03", "2020", "bad"), ref = as.POSIXct(c(NA, "2020-03-15 14:30:00", "2020-03-10 08:00:00", "2020-05-01 00:00:00", NA), tz = "UTC") ) dplyr::bind_cols(df, ks_impute_dtc(df$dtc, "start", ref = df$ref)) ## End(Not run)
Computes the sample median and a confidence interval using one of three
approaches: bootstrap (boot), Olive's simple median interval, or a
normal approximation based on the binomial distribution of ranks (the
"z" method).
ks_median_ci( x, na.rm = TRUE, alpha = 0.05, method = c("boot", "olive", "z"), R = 10000L, seed = 0L, type = 2L )ks_median_ci( x, na.rm = TRUE, alpha = 0.05, method = c("boot", "olive", "z"), R = 10000L, seed = 0L, type = 2L )
x |
Numeric vector. |
na.rm |
Logical. If |
alpha |
Significance level; the interval has nominal coverage
|
method |
Character: |
R |
Number of bootstrap replicates when |
seed |
Integer passed to |
type |
Integer |
bootUses boot with the sample median as
the statistic and boot.ci to produce Basic, Normal,
percentile, and BCa intervals (when BCa is defined).
oliveImplements David Olive's simple median CI (order
statistics with a -based multiplier). Requires at least two
non-missing observations.
zApproximate CI from the normal approximation to the
binomial number of observations below the population median: bounds are
the order statistics with ranks
(rounded inward). The reported Median uses quantile(...,
type).
A named numeric vector. The first element is always Median.
Remaining elements depend on method:
bootBasic.LCI, Basic.UCI, Normal.LCI,
Normal.UCI, Percent.LCI, Percent.UCI,
BCa.LCI, BCa.UCI (BCa may be NA in small samples).
oliveOlive.LCI, Olive.UCI.
zz.LCI, z.UCI (limits are order statistics
of the sorted data).
Olive, D. J. (2005). Applied robust statistics. Preprint. (median CI procedure.)
Davison, A. C. and Hinkley, D. V. (1997). Bootstrap Methods and Their Applications. Cambridge University Press.
x <- rnorm(100, 56, 2.5) ks_median_ci(x, method = "olive") vv <- c(8, 11, 12, 13, 15, 17, 19, 20, 21, 21, 22, 23, 25, 26, 28) ks_median_ci(vv, method = "boot", R = 1000, seed = 1L) ks_median_ci(vv, method = "olive") ks_median_ci(vv, method = "olive", alpha = 0.1) ks_median_ci(vv, method = "z") ks_median_ci(vv, method = "z", alpha = 0.1)x <- rnorm(100, 56, 2.5) ks_median_ci(x, method = "olive") vv <- c(8, 11, 12, 13, 15, 17, 19, 20, 21, 21, 22, 23, 25, 26, 28) ks_median_ci(vv, method = "boot", R = 1000, seed = 1L) ks_median_ci(vv, method = "olive") ks_median_ci(vv, method = "olive", alpha = 0.1) ks_median_ci(vv, method = "z") ks_median_ci(vv, method = "z", alpha = 0.1)
Pools point estimates and standard errors across multiple imputations
according to Rubin's rules. Degrees of freedom follow the Barnard-Rubin
(1999) small-sample adjustment when complete-data df are supplied via
edf (equivalent to the SAS EDF= option).
ks_mianalyze(est, err, alpha = 0.05, theta0 = 0, edf = Inf, v0) mianalyze(est, err, alpha = 0.05, theta0 = 0, edf = Inf, v0) ## S3 method for class 'ks_mianalyze' summary(object, ...) ## S3 method for class 'ks_mianalyze' knit_print(x, ...)ks_mianalyze(est, err, alpha = 0.05, theta0 = 0, edf = Inf, v0) mianalyze(est, err, alpha = 0.05, theta0 = 0, edf = Inf, v0) ## S3 method for class 'ks_mianalyze' summary(object, ...) ## S3 method for class 'ks_mianalyze' knit_print(x, ...)
est |
Numeric vector of point estimates from each imputation. |
err |
Numeric vector of corresponding standard errors. |
alpha |
Significance level for confidence intervals. Default |
theta0 |
Null value for hypothesis test |
edf |
Complete-data degrees of freedom (positive numeric scalar or
|
v0 |
Deprecated alias for |
object |
A |
... |
Additional arguments, currently ignored. |
x |
A |
Let and be estimate and variance from imputation .
The pooled estimate is ;
within variance ;
between variance ;
total variance .
Rubin's (1987) large-sample degrees of freedom are
. When the complete-data
degrees of freedom are small and the fraction of missing
information is modest, can greatly exceed , which is
inappropriate. The Barnard-Rubin (1999) adjustment computes
where
and
.
When the within-imputation variance is exactly zero,
df, r, lambda, re, the confidence limits and
the -test are reported as NA and a warning is issued.
This mirrors PROC MIANALYZE, which warns per zero standard error
and prints "." for those quantities, computing no inference in
that case - with or without EDF= - even when the
between-imputation variance is positive. Validated against a live SAS
PROC MIANALYZE run; the probe tooling is deliberately kept outside the
package.
An object of class ks_mianalyze with components:
Pooled estimate.
Pooled standard error.
Number of imputations.
Between-imputation variance.
Within-imputation variance.
Total variance.
Degrees of freedom (adjusted when edf is finite).
Relative increase in variance due to nonresponse.
Fraction of missing information.
Relative efficiency.
Two-sided confidence limits for Estimate.
Alpha used for CI.
Null value used in hypothesis test.
Complete-data degrees of freedom used.
Min/max of input est.
Test statistic for .
Two-sided p-value.
Barnard, J. and Rubin, D. B. (1999). Small-sample degrees of freedom with multiple imputation. Biometrika, 86, 948–955.
Rubin, D. B. (1987). Multiple Imputation for Nonresponse in Surveys. Wiley.
SAS Institute Inc. (2013). SAS/STAT 12.3 User's Guide. Chapter 58, The MIANALYZE Procedure.
vignette("mianalyze", package = "kstools") for worked examples and
SAS comparisons.
est <- c(0.345, 0.303, 0.298, 0.378) err <- c(0.056, 0.101, 0.099, 0.034) out <- mianalyze(est, err) out ## Barnard-Rubin adjusted df (equivalent to SAS EDF=120) mianalyze(est, err, edf = 120)est <- c(0.345, 0.303, 0.298, 0.378) err <- c(0.056, 0.101, 0.099, 0.034) out <- mianalyze(est, err) out ## Barnard-Rubin adjusted df (equivalent to SAS EDF=120) mianalyze(est, err, edf = 120)
Mardia's skewness and kurtosis tests are implemented in base R (verified against 'QuantPsyc::mult.norm'); the energy test ('energy::mvnorm.etest') is attached automatically when the suggested energy package is installed, and silently skipped otherwise.
ks_mv_normality(x, R = 999, seed = NULL)ks_mv_normality(x, R = 999, seed = NULL)
x |
A numeric matrix or data frame (rows = observations). 'NA' rows are dropped. |
R |
Number of energy-test replicates. |
seed |
Integer seed for the energy permutation (reproducibility). |
Mardia: under multivariate normality, 'A = n b1_p / 6' is chi-squared with 'p(p+1)(p+2)/6' df, and the standardized 'b2_p' is standard normal. The energy test is a consistent omnibus alternative; with ‘n < p + 2' Mardia’s test is not defined and 'NULL' is returned.
A list with 'mardia' (a '2 x 3' matrix: Beta-hat, kappa, p-val; 'NULL' if the sample is too small), 'energy' (or 'NULL' when the energy package is missing), and 'n', 'p'.
set.seed(0) dat <- data.frame(x1 = rnorm(50), x2 = rnorm(50), x3 = rnorm(50)) out <- ks_mv_normality(dat) out$mardia out$energy$statisticset.seed(0) dat <- data.frame(x1 = rnorm(50), x2 = rnorm(50), x3 = rnorm(50)) out <- ks_mv_normality(dat) out$mardia out$energy$statistic
Produces formatted "n (xx.x%)" strings from absolute counts and
denominators. An S3 generic dispatching on the class of n:
numeric method — formats a single vector of counts.
data.frame method — formats one or more named count
columns inside a data.frame or tibble simultaneously,
leaving all other columns unchanged.
ks_n_pct(n, totals, na.rep = NA_character_)ks_n_pct(n, totals, na.rep = NA_character_)
n |
Numeric (integer) vector of counts, or a |
totals |
Numeric denominator(s).
|
na.rep |
Replacement string for
|
Counts are converted to integer before formatting so that both integer and whole-number double vectors are accepted.
Percentages are formatted as "%5.1f%%", giving one decimal place
and a minimum field width of 5 characters
(e.g. " 5.0%", "100.0%"). The fixed width ensures
vertical alignment when columns are printed side-by-side.
A warning is issued when any element of totals is zero, as this
produces Inf in the percentage.
numeric method: a character vector of the same length as
n.
data.frame method: the input data.frame with the
named count columns replaced by formatted character strings; all
other columns are unchanged.
## Numeric method — single total ks_n_pct(c(10L, 20L, 30L), 56L) ## Numeric method — element-wise totals ks_n_pct(c(10L, 20L, 30L), c(34L, 42L, 56L)) ## NA propagation (default) ks_n_pct(c(10L, NA_integer_, 30L), 50L) ## Replace NA with an explicit string ks_n_pct(c(10L, NA_integer_, 30L), 50L, na.rep = "0 ( 0.0%)") ## Replace NA with an empty string ks_n_pct(c(10L, NA_integer_, 30L), 50L, na.rep = "") ## data.frame method dex <- data.frame( par = c("A", "B", "C"), Therapy_A = c(11L, 23L, 14L), Therapy_B = c(20L, 1L, NA_integer_) ) ks_n_pct(dex, c(Therapy_A = 56L, Therapy_B = 67L)) ## data.frame method — keep only one column ks_n_pct(dex, c(Therapy_A = 56L))## Numeric method — single total ks_n_pct(c(10L, 20L, 30L), 56L) ## Numeric method — element-wise totals ks_n_pct(c(10L, 20L, 30L), c(34L, 42L, 56L)) ## NA propagation (default) ks_n_pct(c(10L, NA_integer_, 30L), 50L) ## Replace NA with an explicit string ks_n_pct(c(10L, NA_integer_, 30L), 50L, na.rep = "0 ( 0.0%)") ## Replace NA with an empty string ks_n_pct(c(10L, NA_integer_, 30L), 50L, na.rep = "") ## data.frame method dex <- data.frame( par = c("A", "B", "C"), Therapy_A = c(11L, 23L, 14L), Therapy_B = c(20L, 1L, NA_integer_) ) ks_n_pct(dex, c(Therapy_A = 56L, Therapy_B = 67L)) ## data.frame method — keep only one column ks_n_pct(dex, c(Therapy_A = 56L))
Computes a confidence interval for using Newcombe's
Hybrid Score method (Newcombe, 1998, Method 10). Each group's proportion
is first bounded by its Wilson score interval, and the two intervals are
then combined to yield the CI for the difference.
ks_newcombe_hs(x1, n1, x2, n2, alpha = 0.05)ks_newcombe_hs(x1, n1, x2, n2, alpha = 0.05)
x1 |
Integer vector of event counts for group 1 ( |
n1 |
Integer vector of sample sizes for group 1 ( |
x2 |
Integer vector of event counts for group 2 ( |
n2 |
Integer vector of sample sizes for group 2 ( |
alpha |
Significance level; scalar in |
Algorithm (Newcombe, 1998):
Compute the Wilson score CI for each group at
level :
Form the CI for :
The function is vectorised: if vectors of length are supplied,
each corresponding tuple is
processed independently and the result is a matrix.
A numeric matrix with one row per input pair and three columns:
deltaObserved difference .
lclLower confidence limit.
uclUpper confidence limit.
Newcombe, R. G. (1998). Interval estimation for the difference between independent proportions: comparison of eleven methods. Statistics in Medicine, 17(8), 873–890. doi:10.1002/(SICI)1097-0258(19980430)17:8<873::AID-SIM779>3.0.CO;2-I
ks_newcombe_hs_strata,
ks_eairdiff_mn
## Single comparison ks_newcombe_hs(36L, 54L, 41L, 56L, alpha = 0.10) ## Zero events in one group ks_newcombe_hs(0L, 50L, 10L, 50L) ## Vectorised: three comparisons at once ks_newcombe_hs( x1 = c(36L, 38L, 3L), n1 = c(54L, 57L, 86L), x2 = c(41L, 42L, 3L), n2 = c(56L, 57L, 93L), alpha = 0.10 )## Single comparison ks_newcombe_hs(36L, 54L, 41L, 56L, alpha = 0.10) ## Zero events in one group ks_newcombe_hs(0L, 50L, 10L, 50L) ## Vectorised: three comparisons at once ks_newcombe_hs( x1 = c(36L, 38L, 3L), n1 = c(54L, 57L, 86L), x2 = c(41L, 42L, 3L), n2 = c(56L, 57L, 93L), alpha = 0.10 )
Extends Newcombe's Hybrid Score method to stratified data using Mantel-Haenszel harmonic-mean weights. Within each stratum the Wilson score intervals are computed for both groups; the stratum-level contributions are then pooled via weighted combination to produce a single CI for the overall difference.
ks_newcombe_hs_strata(x1, n1, x2, n2, alpha = 0.05)ks_newcombe_hs_strata(x1, n1, x2, n2, alpha = 0.05)
x1 |
Integer vector of event counts for group 1, one element per stratum. |
n1 |
Integer vector of sample sizes for group 1, one element per
stratum ( |
x2 |
Integer vector of event counts for group 2, one element per stratum. |
n2 |
Integer vector of sample sizes for group 2, one element per
stratum ( |
alpha |
Significance level; scalar in |
Algorithm:
Compute Wilson CIs and
for each stratum .
Assign stratum weights
(harmonic-mean / Mantel-Haenszel weights).
Pooled point estimate:
.
Pooled lower limit:
Pooled upper limit: analogously.
A single stratum reduces to the un-stratified formula.
A named numeric vector of length 3:
deltaWeighted pooled point estimate.
lclLower confidence limit.
uclUpper confidence limit.
Newcombe, R. G. (1998). Interval estimation for the difference between independent proportions: comparison of eleven methods. Statistics in Medicine, 17(8), 873–890. doi:10.1002/(SICI)1097-0258(19980430)17:8<873::AID-SIM779>3.0.CO;2-I
## Two-stratum example ks_newcombe_hs_strata( x1 = c(36L, 10L), n1 = c(54L, 30L), x2 = c(41L, 8L), n2 = c(56L, 30L) ) ## Single stratum — identical to ks_newcombe_hs() ks_newcombe_hs_strata(36L, 54L, 41L, 56L) ks_newcombe_hs(36L, 54L, 41L, 56L) # same result## Two-stratum example ks_newcombe_hs_strata( x1 = c(36L, 10L), n1 = c(54L, 30L), x2 = c(41L, 8L), n2 = c(56L, 30L) ) ## Single stratum — identical to ks_newcombe_hs() ks_newcombe_hs_strata(36L, 54L, 41L, 56L) ks_newcombe_hs(36L, 54L, 41L, 56L) # same result
Wrapper around shapiro.test; returns the p-value or NA on error.
ks_p_norm(x)ks_p_norm(x)
x |
Numeric vector. |
A single numeric (p-value or NA).
Computes the 25th percentile using quantile.
The quantile algorithm type can be set globally via
options(kstools.quantile.type = <1..9>) (e.g. in ‘.Rprofile’).
Type 2 matches SAS default; type 7 is the R default.
When the option is not set the package default of 2 is used.
ks_q1(x, na.rm = TRUE, type = getOption("kstools.quantile.type", default = 2L))ks_q1(x, na.rm = TRUE, type = getOption("kstools.quantile.type", default = 2L))
x |
Numeric vector. |
na.rm |
Logical; remove |
type |
Integer (1–9); quantile algorithm passed to
|
## Use R default algorithm for this call ks_q1(rnorm(100), type = 7) ## Set package-wide default (e.g. in .Rprofile) old <- options(kstools.quantile.type = 7L) ks_q1(rnorm(100)) # now uses type 7 options(old) # restore## Use R default algorithm for this call ks_q1(rnorm(100), type = 7) ## Set package-wide default (e.g. in .Rprofile) old <- options(kstools.quantile.type = 7L) ks_q1(rnorm(100)) # now uses type 7 options(old) # restore
Computes the 75th percentile using quantile.
The quantile algorithm type can be set globally via
options(kstools.quantile.type = <1..9>) (e.g. in ‘.Rprofile’).
Type 2 matches SAS default; type 7 is the R default.
When the option is not set the package default of 2 is used.
ks_q3(x, na.rm = TRUE, type = getOption("kstools.quantile.type", default = 2L))ks_q3(x, na.rm = TRUE, type = getOption("kstools.quantile.type", default = 2L))
x |
Numeric vector. |
na.rm |
Logical; remove |
type |
Integer (1–9); quantile algorithm passed to
|
## Use SAS-compatible algorithm for this call ks_q3(rnorm(100), type = 2) ## Set package-wide default (e.g. in .Rprofile) old <- options(kstools.quantile.type = 7L) ks_q3(rnorm(100)) # now uses type 7 options(old) # restore## Use SAS-compatible algorithm for this call ks_q3(rnorm(100), type = 2) ## Set package-wide default (e.g. in .Rprofile) old <- options(kstools.quantile.type = 7L) ks_q3(rnorm(100)) # now uses type 7 options(old) # restore
Reads a SAS XPORT v5 or v8/v9 file using a built-in C++ parser. Variable labels, formats, and informats are preserved as column attributes ('"label"', '"format.sas"', '"sas.informat"'). Multi-member files are returned as a named list of tibbles, one per dataset.
ks_read_xpt(file, convert_dates = TRUE, warn = TRUE, n_threads = NULL)ks_read_xpt(file, convert_dates = TRUE, warn = TRUE, n_threads = NULL)
file |
Path to a SAS XPORT file. |
convert_dates |
Logical. If 'TRUE' (default), columns formatted as SAS dates or datetimes are converted to R 'Date' / 'POSIXct'. If 'FALSE', such columns are returned as numeric SAS day / second values. |
warn |
Logical. If 'TRUE' (default), parser warnings are emitted via [cli::cli_warn]. |
n_threads |
Integer or 'NULL'. Number of decoder threads to use. 'NULL' (default) selects 'max(1, parallel::detectCores() - 1)'. Set to '1L' for fully deterministic single-threaded decoding. |
Date and datetime conversion, when enabled, applies the SAS-to-R epoch shift of 3653 days (315,619,200 seconds) and assigns the appropriate 'Date' / 'POSIXct' class to columns whose SAS format is recognised as a date or datetime format.
A tibble for a single-member file, or a named list of tibbles for a multi-member file.
[ks_write_xpt()], [ks_xpt_labels()], [ks_xpt_formats()], [ks_set_labels()], [ks_set_formats()]
R analogue of the classic SAS ROC macro: computes AUC estimates for multiple predictors measured on the same subjects, estimates their covariance matrix using Mann-Whitney components with pairwise deletion, and performs Wald-style tests for user-specified linear contrasts.
ks_roc(data, var, response, contrast = NULL, details = FALSE, alpha = 0.05)ks_roc(data, var, response, contrast = NULL, details = FALSE, alpha = 0.05)
data |
A data frame containing predictors and the binary response. |
var |
Character vector of predictor column names. |
response |
Single character scalar naming the binary response column coded as 0/1. |
contrast |
Optional numeric contrast matrix. Columns must match
|
details |
Logical; if |
alpha |
Significance level for two-sided confidence intervals. |
A list with elements:
Data frame with AUC, standard error, and confidence bounds.
Data frame with contrast estimates, SE, CI, chi-square, and p-value (1 df each row).
Contrast matrix used in the analysis.
Named numeric vector: chi-square, df, p-value for simultaneous contrast test.
Overall contrast-test p-value (same as
overall_test[["p_value"]]).
Only when details = TRUE: list of intermediate
matrices t, v, nx, ny, lv.
Sets the '"format.sas"' attribute of selected columns from a named character vector.
ks_set_formats(data, formats)ks_set_formats(data, formats)
data |
A data frame or tibble. |
formats |
Named character vector. Names are column names; values are SAS format strings (e.g. '"DATE9."', '"F8.2"', '"$20."'). |
The input 'data' with '"format.sas"' attributes updated.
[ks_xpt_formats()], [ks_set_labels()]
Sets the '"label"' attribute of selected columns from a named character vector. Names of 'labels' must match column names in 'data'. Unknown names are warned about and skipped.
ks_set_labels(data, labels)ks_set_labels(data, labels)
data |
A data frame or tibble. |
labels |
Named character vector. Names are column names; values are labels. Use 'NA' or empty string to keep an existing label. |
The input 'data' with '"label"' attributes updated.
[ks_xpt_labels()], [ks_set_formats()]
Generates a random sample of subjects with random intercepts and slopes,
irregular observation times, and Gaussian AR(1) serially correlated residuals.
The result is an groupedData object suitable for
exploratory plots or simulation studies.
ks_simulate_ar1( n, beta0, beta1, ar.val = 0.4, sigma, tau0, tau1, tau01 = 0.3, m, seed = NULL )ks_simulate_ar1( n, beta0, beta1, ar.val = 0.4, sigma, tau0, tau1, tau01 = 0.3, m, seed = NULL )
n |
Number of subjects ( |
beta0 |
Population mean intercept (fixed effect on the log-time scale). |
beta1 |
Population mean slope (coefficient of |
ar.val |
Autoregressive coefficient |
sigma |
Marginal standard deviation of the AR(1) error (after the scaling described in Details). |
tau0, tau1
|
Standard deviations of random intercept and random slope. |
tau01 |
Correlation between random intercept and slope (in |
m |
Maximum possible number of visits per subject (must be |
seed |
Optional integer; if not |
Observation counts are uniform on (rounded
from continuous uniforms). Observation times are plus a random
subset of of size .
Random effects are bivariate normal with mean zero,
SDs tau0, tau1 and correlation tau01. Outcomes are
with an AR(1) Gaussian series. Innovations are
scaled so that the marginal residual SD is sigma:
arima.sim(...) is multiplied by .
An groupedData object (also inherits from
data.frame) with columns id, obs, and y.
arima.sim, mvrnorm,
groupedData
set.seed(1) dt <- ks_simulate_ar1( n = 10, beta0 = 5, beta1 = 1, ar.val = 0.4, sigma = 2, tau0 = 1, tau1 = 0.5, tau01 = 0.2, m = 8, seed = 1L ) head(dt)set.seed(1) dt <- ks_simulate_ar1( n = 10, beta0 = 5, beta1 = 1, ar.val = 0.4, sigma = 2, tau0 = 1, tau1 = 0.5, tau01 = 0.2, m = 8, seed = 1L ) head(dt)
Add two values ignoring NAs. Usually 5.0 + NA gives NA as a result, but here 5.0 %+na% NA will give 5.0. NA %+na% 5.0 also gives 5.0. Applicable for scalars and vectors.
ks_sum_na(x, y) x %+na% yks_sum_na(x, y) x %+na% y
x |
Numeric scalar or vector |
y |
Numeric scalar or vector |
NA %+na% 10 12.3 %+na% 10 12.3 %+na% NA ks_sum_na(12.3, NA) NA %+na% NA c(1,2,NA) %+na% c(NA,2,3)NA %+na% 10 12.3 %+na% 10 12.3 %+na% NA ks_sum_na(12.3, NA) NA %+na% NA c(1,2,NA) %+na% c(NA,2,3)
Computes n, mean, SD, CV, median, Q1, Q3, min, max by group. Optionally adds a p-value for change from zero (paired t-test or Wilcoxon signed-rank, chosen by Shapiro-Wilk normality test).
ks_summary( data, var, ..., na.rm = TRUE, p.change = FALSE, extras = NULL, numdec = TRUE )ks_summary( data, var, ..., na.rm = TRUE, p.change = FALSE, extras = NULL, numdec = TRUE )
data |
A data frame or tibble. |
var |
Unquoted name of the numeric variable to summarise. |
... |
Unquoted grouping variables. Must come before |
na.rm |
Logical; remove |
p.change |
Logical; if |
extras |
An optional named list of functions for additional
per-group statistics. Each function is called as
Example: Names that clash with built-in columns ( |
numdec |
Logical; if |
Q1 and Q3 use ks_q1 / ks_q3, whose
type argument defaults to
getOption("kstools.quantile.type", 2L) (SAS-compatible).
Set that option in ‘.Rprofile’ to change the algorithm globally.
A tibble with one row per group and columns n,
mean, sd, median, q1,
q3, min, max, any extras columns,
cv, (if numdec = TRUE) NUMDEC, and
(if p.change = TRUE) p.chg.
d <- data.frame( grp = rep(c("A", "B"), each = 10), val = c(rnorm(10, 5), rnorm(10, 8)) ) ks_summary(d, val, grp) ## Without NUMDEC ks_summary(d, val, grp, numdec = FALSE) ## With extra statistics ks_summary(d, val, grp, extras = list( mad = stats::mad, sem = function(x, na.rm) sd(x, na.rm = na.rm) / sqrt(sum(!is.na(x))) ) )d <- data.frame( grp = rep(c("A", "B"), each = 10), val = c(rnorm(10, 5), rnorm(10, 8)) ) ks_summary(d, val, grp) ## Without NUMDEC ks_summary(d, val, grp, numdec = FALSE) ## With extra statistics ks_summary(d, val, grp, extras = list( mad = stats::mad, sem = function(x, na.rm) sd(x, na.rm = na.rm) / sqrt(sum(!is.na(x))) ) )
Validates that a SUPP– domain data frame has the required SDTM structure
and internally consistent IDVAR/IDVARVAL columns.
ks_supp_consistency_check(data)ks_supp_consistency_check(data)
data |
A data frame or tibble representing a SUPP– domain. |
Two structural rules are enforced:
All seven standard SUPP columns must be present:
STUDYID, USUBJID, IDVAR, IDVARVAL,
QNAM, QLABEL, QVAL.
Every one of those seven columns must be of type character.
If both rules pass, the function further inspects
IDVAR/IDVARVAL:
All blank (empty string or NA): the merge key is
USUBJID alone (noid = TRUE).
All non-blank: a record-level key is available
(noid = FALSE).
Mixed: considered invalid — isvalid is set to
FALSE.
A named logical vector of length 3:
isvalidDataset passes structural checks.
noidTRUE when all IDVAR/IDVARVAL
are blank — merge key is USUBJID only.
multidTRUE when more than one distinct non-blank
IDVAR value is present.
supp <- data.frame( STUDYID = "S1", USUBJID = c("01", "02"), IDVAR = "", IDVARVAL = "", QNAM = "RACE2", QLABEL = "Race (detail)", QVAL = c("Asian", "White"), stringsAsFactors = FALSE ) ks_supp_consistency_check(supp) ## Invalid: missing required column bad <- supp[, -which(names(supp) == "QLABEL")] ks_supp_consistency_check(bad)supp <- data.frame( STUDYID = "S1", USUBJID = c("01", "02"), IDVAR = "", IDVARVAL = "", QNAM = "RACE2", QLABEL = "Race (detail)", QVAL = c("Asian", "White"), stringsAsFactors = FALSE ) ks_supp_consistency_check(supp) ## Invalid: missing required column bad <- supp[, -which(names(supp) == "QLABEL")] ks_supp_consistency_check(bad)
Transposes a SUPP– dataset from long (one row per variable) to wide and left-joins it onto the primary domain using the appropriate key columns.
ks_supp_merge(primary, supp, mvars = NULL, get_labels = TRUE, rep_na = FALSE)ks_supp_merge(primary, supp, mvars = NULL, get_labels = TRUE, rep_na = FALSE)
primary |
A data frame or tibble: the primary SDTM domain
(e.g. |
supp |
A data frame or tibble: the corresponding SUPP– domain
(e.g. |
mvars |
Character vector of |
get_labels |
Logical; if |
rep_na |
Logical; if |
The merge strategy depends on IDVAR/IDVARVAL:
noid = TRUE)All
IDVAR/IDVARVAL are blank. The join key is
STUDYID + USUBJID.
noid = FALSE)Each row in the SUPP
is tied to a specific record in the primary domain. IDVAR
names the primary domain variable (e.g. EXSEQ) and
IDVARVAL holds its value. When multiple distinct
IDVAR values are present, each is processed in a separate
join pass.
If get_labels = TRUE and the primary domain was loaded via
haven (or another reader that preserves column "label"
attributes), the per-variable labels from QLABEL are attached to the
newly added columns.
The primary data frame with SUPP variables added as new
columns.
dm <- data.frame( STUDYID = "S1", USUBJID = c("01", "02", "03"), AGE = c(34L, 52L, 47L), stringsAsFactors = FALSE ) suppdm <- data.frame( STUDYID = "S1", USUBJID = c("01", "02", "03"), IDVAR = "", IDVARVAL = "", QNAM = "RACE2", QLABEL = "Race (detail)", QVAL = c("Asian", "White", "Asian"), stringsAsFactors = FALSE ) ks_supp_merge(dm, suppdm)dm <- data.frame( STUDYID = "S1", USUBJID = c("01", "02", "03"), AGE = c(34L, 52L, 47L), stringsAsFactors = FALSE ) suppdm <- data.frame( STUDYID = "S1", USUBJID = c("01", "02", "03"), IDVAR = "", IDVARVAL = "", QNAM = "RACE2", QLABEL = "Race (detail)", QVAL = c("Asian", "White", "Asian"), stringsAsFactors = FALSE ) ks_supp_merge(dm, suppdm)
Exchanges column contents by name: for each named argument lhs = rhs,
the column named lhs receives the values that were in rhs,
and rhs receives the values that were in lhs. Multiple
independent pairs can be given in one call.
ks_swap_cols(df, ...)ks_swap_cols(df, ...)
df |
A |
... |
Named pairs |
The syntax USUBJID = SUBJID swaps the two columns: after the call,
the column still named USUBJID holds the former SUBJID values
and vice versa. This is useful when harmonising SDTM-style identifiers or
fixing column order after merges.
With zero pairs in ..., df is returned unchanged.
df with the specified column pairs exchanged (same class as
the input where possible).
d <- data.frame(SUBJID = c("001", "002"), USUBJID = c("01-001", "01-002")) ks_swap_cols(d, USUBJID = SUBJID)d <- data.frame(SUBJID = c("001", "002"), USUBJID = c("01-001", "01-002")) ks_swap_cols(d, USUBJID = SUBJID)
Encodes each level of a categorical variable with a smoothed log-odds of the
response, relative to the overall response rate. Useful as a numeric
replacement for a factor when screening logistic-type models. Designed for
use inside dplyr::mutate().
ks_swoe(x, y, prior.n = 24)ks_swoe(x, y, prior.n = 24)
x |
Categorical predictor (factor, character, or other grouping vector). |
y |
Binary response (numeric 0/1 or logical), same length as |
prior.n |
Pseudo-count (prior strength) for the empirical-Bayes-style
smoothing in the logit; the original implementation used |
Let be the overall mean of the response and, for each level
of x, let be the number of events (response = 1)
and the number of non-missing responses. The smoothed logit for
level is
with prior.n. A global logit is
appended internally for completeness; row values are taken by matching each
row's x level to the named level logits.
If , the global logit is undefined; the function clamps
to for that term only, with a
warning.
A numeric vector of length length(x) (unnamed), giving the
SWOE score for each row's level of x.
The construction follows common “weight of evidence” encodings with
Dirichlet-style smoothing; the choice prior.n = 24 matches the
historical default in this package.
glm for logistic regression
set.seed(1234) dt <- data.frame( yn = rbinom(50, 1, 0.3), sex = factor(rbinom(50, 1, 0.55), levels = c(0, 1), labels = c("F", "M")) ) ks_swoe(dt$sex, dt$yn) if (requireNamespace("dplyr", quietly = TRUE)) { dplyr::mutate(dt, sex_swoe = ks_swoe(sex, yn)) }set.seed(1234) dt <- data.frame( yn = rbinom(50, 1, 0.3), sex = factor(rbinom(50, 1, 0.55), levels = c(0, 1), labels = c("F", "M")) ) ks_swoe(dt$sex, dt$yn) if (requireNamespace("dplyr", quietly = TRUE)) { dplyr::mutate(dt, sex_swoe = ks_swoe(sex, yn)) }
Looks up each element of 'x' among the named values of '...' — a 'switch()' that works on a whole vector at once, like SAS 'select(when ...)' or a format-driven recode.
ks_vswitch(.x, ..., .na = NULL, .default = NULL)ks_vswitch(.x, ..., .na = NULL, .default = NULL)
.x |
A vector of lookup keys (character, factor, or numeric;
coerced with |
... |
Named values of a **single common type** — the names are keys, the values are results. 'vapply()' errors when the types disagree. |
.na |
Value returned where 'x' is 'NA'; defaults to an 'NA' of the same type as the first value of '...'. |
.default |
Value returned for keys present in 'x' but absent from '...'; defaults to an 'NA' of the same type as the first value of '...'. |
A vector the same length as '.x' with the storage type of the values in '...'. The key argument is named '.x' (leading dot) so that bare case names like 'x = ...' in dots cannot collide with it.
ks_vswitch(c("a", "b", "a", NA, "zz"), a = 10L, b = 20L) ks_vswitch(c("a", "b", NA, "zz"), a = 10L, b = 20L, .na = 0L, .default = -1L) ks_vswitch(factor(c("x", "y")), x = 1.5, y = 2.5)ks_vswitch(c("a", "b", "a", NA, "zz"), a = 10L, b = 20L) ks_vswitch(c("a", "b", NA, "zz"), a = 10L, b = 20L, .na = 0L, .default = -1L) ks_vswitch(factor(c("x", "y")), x = 1.5, y = 2.5)
R analogue of the SAS WILLIAMS macro. The function constructs a
Williams design from a randomized cyclic Latin square and generates a
block-randomized subject schedule.
ks_williams(tmts_num, tmt_names, sample_size, out_rtf = NULL, seed_num = NULL)ks_williams(tmts_num, tmt_names, sample_size, out_rtf = NULL, seed_num = NULL)
tmts_num |
Integer number of treatments. |
tmt_names |
Character vector of treatment labels (length must equal
|
sample_size |
Integer target number of subjects. |
out_rtf |
Optional output file path. If provided, a simple RTF report with design and randomization schedule tables is written. |
seed_num |
Optional integer seed. If |
A list with:
Data frame of Williams sequences by period.
Data frame of randomized subject assignments.
Seed used.
Logical; TRUE when seed was auto-generated.
Number of unique sequences.
Number of full randomization blocks used.
Logical; TRUE when
sample_size is not a multiple of n_sequences.
Writes a data frame to a SAS XPORT V5 or V8 file using a built-in C++ writer. Variable labels and SAS formats are taken from per-column attributes ('"label"', '"format.sas"') or the optional 'labels' / 'formats' arguments, which override attributes.
ks_write_xpt( data, file, version = 5L, convert_dates = TRUE, dataset_name = NULL, dataset_label = NULL, labels = NULL, formats = NULL )ks_write_xpt( data, file, version = 5L, convert_dates = TRUE, dataset_name = NULL, dataset_label = NULL, labels = NULL, formats = NULL )
data |
A data frame. |
file |
Output path. |
version |
Integer 5 or 8. Default '5L' (widest compatibility). |
convert_dates |
Logical. If 'TRUE' (default), 'Date' and 'POSIXct' columns are converted to SAS numeric (days / seconds since 1960-01-01). |
dataset_name |
SAS dataset name. Up to 8 characters for V5, up to 32 for V8. Defaults to '"DATASET"'. |
dataset_label |
Up to 40 characters describing the dataset. |
labels |
Optional named character vector of variable labels keyed by column name. Overrides per-column '"label"' attributes. |
formats |
Optional named character vector of SAS formats (e.g. '"DATE9."', '"F8.2"') keyed by column name. Overrides per-column '"format.sas"' attributes. |
Date and datetime conversion, when enabled, applies the R-to-SAS epoch shift to columns of class 'Date' or 'POSIXct'.
The output file path, invisibly.
[ks_read_xpt()]
Scans the raw bytes of a SAS Transport Version 8 file for member
(MEMBV8) headers and returns the byte range of each member.
ks_xpt_content(file, chunk_size = 5L * 80L * 1024L)ks_xpt_content(file, chunk_size = 5L * 80L * 1024L)
file |
Path to a |
chunk_size |
Number of raw bytes to read per pass. Default |
The file is read in chunks of chunk_size bytes (default approximately 400 KB).
A MEMBV8 header that spans two adjacent chunks would not be detected;
this is exceedingly unlikely for practical SDTM files.
A named list; each element corresponds to one dataset and contains
a named integer vector c(start = <1-based pos>, stop = <1-based pos>).
ks_extract_single_xpt,
ks_batch_load_from_single_xpt
## Not run: ## content <- ks_xpt_content("path/to/study.xpt") ## names(content) # dataset names (lower-case) ## content[["ae"]] # byte range for the AE member ## End(Not run)## Not run: ## content <- ks_xpt_content("path/to/study.xpt") ## names(content) # dataset names (lower-case) ## content[["ae"]] # byte range for the AE member ## End(Not run)
Returns the '"format.sas"' attribute of each column as a named character vector. Columns without a '"format.sas"' attribute are reported as the empty string.
ks_xpt_formats(data)ks_xpt_formats(data)
data |
A data frame or tibble. |
Named character vector of SAS format strings (one entry per column).
[ks_set_formats()], [ks_xpt_labels()], [ks_read_xpt()], [ks_write_xpt()]
Returns the '"label"' attribute of each column as a named character vector. Columns without a '"label"' attribute are reported as the empty string.
ks_xpt_labels(data)ks_xpt_labels(data)
data |
A data frame or tibble. |
Named character vector of variable labels (one entry per column).
[ks_set_labels()], [ks_xpt_formats()], [ks_read_xpt()], [ks_write_xpt()]
For evaluation of how far a measured value is from the mean (in case of data normality), following formulation used
ks_z_score(x, na.rm = TRUE)ks_z_score(x, na.rm = TRUE)
x |
Numeric vector |
na.rm |
Flag for ignoring NAs. Default = TRUE |
Useful values are +\/-3.
set.seed(345) ks_z_score(rnorm(10)) ## -0.9102967 -0.4446436 -0.3358703 -0.4548544 -0.2493297 ## -0.7708130 -1.0418824 1.3891393 1.3375406 1.4810102set.seed(345) ks_z_score(rnorm(10)) ## -0.9102967 -0.4446436 -0.3358703 -0.4548544 -0.2493297 ## -0.7708130 -1.0418824 1.3891393 1.3375406 1.4810102
Read a single CSV file into R. Optionally parses KeyStat-style comment headers that encode column type, length (for character variables), and labels.
read.csv3(file, with_length = FALSE)read.csv3(file, with_length = FALSE)
file |
Path to the source CSV file. |
with_length |
If |
File encoding should be UTF-8.
Readable comments in the CSV file:
#NAME$character(length)|numeric$Label.
This function was written for correct reading of CSV files. Some CSV files without metadata in comments cannot be read correctly into R.
A data frame with column labels set from the comment metadata (when available).