Appends per-observation factor scores for every level to a data frame.
Score columns are named .m{k}f{j} (e.g., .m1f1, .m2f1, .m2f2),
matching the factor labels used throughout the object.
Arguments
- x
An
ackwardsobject.- data
A data frame or numeric matrix with the same variables (columns) used to fit
x. WhenNULL(default), uses pre-stored scores if available (requireskeep_scores = TRUEat fit time).- append
Logical. When
TRUE(default) the score columns are appended todata(or to a.obsindex whendataisNULL). WhenFALSEonly the score columns are returned (plus anyid_cols).- id_cols
Optional character vector naming columns of
datato carry through alongside the scores whenappend = FALSE(for example a subject identifier, so scores can be rejoined after filtering). Ignored – and an error – whenappend = TRUE(all columns are already kept) or whendataisNULL(there are no source columns to carry).NULL(default) returns the bare score columns.- scaling
Which item means/SDs standardize
databefore the weights are applied."fit"(default) uses the fit-time moments stored in the object – the correct choice for scoring new observations (e.g. a cross-validation test split) or subsets on the training metric."sample"standardizes by the supplied data's own moments (the only option for objects fit from a correlation matrix, which carry no raw-data moments). Only used whendatais supplied; passing it withoutdatais an error (stored scores are returned exactly as computed at fit time).- ...
Ignored.
Value
A data frame. With append = TRUE: the supplied data (or a .obs
index when data is NULL) with score columns appended. With
append = FALSE: only the score columns, optionally prefixed by the
id_cols. Row order and count always match the input.
Details
Score computation. Scores are S = Z W / sqrt(score_var), where Z is
the item z-scores, W is the per-level weight matrix stored in the object,
and sqrt(score_var) standardizes by the real score standard deviations
(Invariant 1: never assume unit variance). For PCA the method is
"components"; for EFA/ESEM it is "tenBerge". The scaling argument
controls which means/SDs build Z: by default the fit-time moments
stored in the object, so any data you score — the training data, a subset
of it, or entirely new observations — lands on the same metric the model
was estimated in.
Scoring new observations (cross-validation). Because scoring only needs
the stored weight matrices and the fit-time moments, you can fit
ackwards() on a training split and score a held-out test split without
retraining: augment(x, data = test_set) (or, equivalently,
predict.ackwards()). Under the default scaling = "fit" the test
observations are standardized by the training means/SDs, which is what
"applying the trained model" means: a test observation's score does not
depend on which other observations happen to share its split, and train and
test scores are directly comparable. scaling = "sample" instead
re-standardizes by the supplied data's own moments — a deliberate choice
when scoring a sample from a different population in its own metric, and
the only option for objects fit from a correlation matrix (which carry no
raw-data moments). For non-Pearson bases (polychoric, Spearman) the usual
caveat applies either way: the weights derive from the non-Pearson R
while Z is a linear standardization, so empirical score SDs are close to
but not exactly 1 (a one-time warning says so); train/test comparability
under scaling = "fit" is unaffected. For objects fit with
missing = "fiml" (PCA/EFA), the stored moments are the observed
(na.rm) means/SDs of the training items — the correlation matrix was
FIML-estimated, but scoring operates on observed responses, so the
observed moments are the consistent frame; incomplete rows still score
NA (scoring does not impute).
Missing data. Score projection applies weights row-wise and propagates
NAs listwise: any observation with at least one missing item variable will
produce NA scores at every level. This differs from fitting, which uses
pairwise-complete correlations. A warning is issued if NA rows are detected.
Use na.omit(data) before scoring if NA rows are unwanted.
Data source. If data is supplied, scores are always recomputed from
it using the stored weights – this is how to score new observations. If
data is NULL and keep_scores = TRUE was set at fit time, the stored
scores are returned. If neither is available an informative error is raised.
Scores-only output (append = FALSE). By default (append = TRUE) the
scores are appended to the supplied data, following the broom convention.
Set append = FALSE to return only the score columns – convenient for
feeding scores straight into cor(), lm(), or a clustering call without
dragging the item columns along. Because augment() always preserves row
order and row count, cbind(data, augment(x, data, append = FALSE))
reproduces the appended output exactly. A row that scores NA (because it is
missing an item – see Missing data above) is still returned in place, so the
positional alignment holds even with NA scores. For a rejoin that survives
filtering the scores afterwards, name identifier columns with id_cols so
they travel with the scores.
Examples
# Score the training data on the fly (no keep_scores = TRUE needed)
x <- ackwards(sim16, k_max = 5)
scores_df <- augment(x, data = sim16)
head(scores_df[, startsWith(names(scores_df), ".m")])
#> .m1f1 .m2f1 .m2f2 .m3f1 .m3f2 .m3f3
#> 1 0.7084102 2.08220676 -1.0812105 -1.0846753 1.2529408 1.66899528
#> 2 -0.5732057 -0.64530574 -0.1652010 -0.1361687 -0.6229163 -0.29658576
#> 3 -1.5222509 0.21929430 -2.3727764 -2.2976644 -0.5385154 0.79318695
#> 4 0.2786514 0.02349164 0.3706740 0.3590532 0.1184985 -0.07677641
#> 5 -1.0170680 -0.97317875 -0.4650366 -0.3494137 -1.4985488 0.09413691
#> 6 -0.2675764 -0.95846783 0.5804698 0.6811951 -1.3327340 -0.03027230
#> .m4f1 .m4f2 .m4f3 .m4f4 .m5f1 .m5f2
#> 1 0.8367367 1.98120705 0.22462994 -1.61915885 0.8342794 1.98244560
#> 2 -0.4644816 -0.40243702 -0.47549374 0.22730576 -0.4843440 -0.40064269
#> 3 0.2058961 0.31402515 -3.08727273 -0.32514479 0.1577276 0.31322943
#> 4 0.2256783 -0.16054771 0.03139285 0.45632266 0.2093034 -0.15629258
#> 5 -1.4268829 0.06527855 -0.58548084 0.02156911 -1.3840396 0.05153121
#> 6 -1.0361522 -0.23837148 -0.35914294 1.20575073 -1.0246840 -0.24329695
#> .m5f3 .m5f4 .m5f5
#> 1 0.2196692 -1.6038056 -0.2456481
#> 2 -0.4732311 0.3005452 -0.8525288
#> 3 -3.0886969 -0.1871828 -1.6902637
#> 4 0.0294733 0.5224934 -0.7365249
#> 5 -0.5787203 -0.1735409 2.2069843
#> 6 -0.3566370 1.1374441 0.8127603
# Scores-only: just the .m{k}f{j} columns, ready for cor()/lm()
scores_only <- augment(x, data = sim16, append = FALSE)
round(cor(scores_only[, c(".m5f1", ".m5f2")]), 2)
#> .m5f1 .m5f2
#> .m5f1 1 0
#> .m5f2 0 1
# Carry an identifier through for a safe post-filter rejoin
df <- data.frame(id = seq_len(nrow(sim16)), sim16)
scored <- augment(x, data = df, append = FALSE, id_cols = "id")
head(scored[, c("id", ".m5f1")])
#> id .m5f1
#> 1 1 0.8342794
#> 2 2 -0.4843440
#> 3 3 0.1577276
#> 4 4 0.2093034
#> 5 5 -1.3840396
#> 6 6 -1.0246840
# Store at fit time and augment without re-supplying data
x2 <- ackwards(sim16, k_max = 5, keep_scores = TRUE)
scores_df2 <- augment(x2)
# Cross-validation: fit on a training split, score the test split on the
# training metric (no retraining; see also predict.ackwards())
train_idx <- seq_len(500)
x_train <- ackwards(sim16[train_idx, ], k_max = 5)
test_scores <- augment(x_train, data = sim16[-train_idx, ], append = FALSE)
head(test_scores)
#> .m1f1 .m2f1 .m2f2 .m3f1 .m3f2 .m3f3
#> 1 0.9866825 1.10925975 0.2651224 0.22302480 0.83381091 0.7455003
#> 2 -1.3121549 0.41877448 -2.3452698 -2.31934440 -0.09306001 0.5863426
#> 3 1.0695420 -0.96685053 2.5697581 2.66347846 -0.91744168 -0.2973669
#> 4 0.4676516 -0.17389635 0.8617838 1.01558023 -0.99973200 0.8581230
#> 5 0.5951633 0.47949881 0.3594096 0.09180377 1.93864339 -1.3480924
#> 6 1.3390624 0.01471201 1.9282385 1.84818068 0.62412605 -0.5416109
#> .m4f1 .m4f2 .m4f3 .m4f4 .m5f1 .m5f2
#> 1 0.70928336 0.8094370 0.45520440 -0.05611942 0.7250445 0.8034631
#> 2 -0.08628128 0.6373020 -1.40074580 -1.85689000 -0.1442605 0.6544344
#> 3 -0.78400610 -0.4193713 1.15211798 2.47154121 -0.7642160 -0.4206876
#> 4 -0.76118123 0.7194296 -0.07002827 1.34363823 -0.7490204 0.7191977
#> 5 1.29627226 -1.0086779 1.62903644 -1.17221196 1.2950204 -1.0113150
#> 6 0.25414070 -0.3714401 1.87301298 0.86141204 0.2659999 -0.3729043
#> .m5f3 .m5f4 .m5f5
#> 1 0.4524271 -0.07636977 0.3129451
#> 2 -1.4086669 -1.75811718 -1.6661291
#> 3 1.1651161 2.42899090 0.7111726
#> 4 -0.0638014 1.32385148 0.3743907
#> 5 1.6229873 -1.17404983 -0.1157916
#> 6 1.8770026 0.83487823 0.3350342
