---
title: "Profile Analysis via Multidimensional Scaling with pams"
author: "Se-Kang Kim and Donghoh Kim"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Profile Analysis via Multidimensional Scaling with pams}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
```

## What pams adds

Profile Analysis via Multidimensional Scaling (PAMS) separates each person's
scores into an overall level and an ipsatized pattern. Without `pams`, an R
user would need to program the standardization, level--pattern decomposition,
inter-variable dissimilarities, MDS fitting, person-level bootstrap, sign
alignment, BCa intervals, and person-weight regressions as separate steps.
`BootSmacof()` integrates those operations and returns a fitted object with
`summary()` and `plot()` methods.

Let $z_i$ be person $i$'s $J$-variable score vector. The level and pattern
are

$$
\ell_i = J^{-1}\sum_j z_{ij}, \qquad p_i = z_i - \ell_i\mathbf{1}.
$$

If the retained MDS coordinates form the $J \times K$ matrix $C$, PAMS
represents the pattern as

$$
p_i = Cw_i + e_i.
$$

The values `w1`, ..., `wK` are the unstandardized no-intercept ordinary
least-squares coefficients in $w_i$. The `corDim` values are partial
correlations between the person pattern and each coordinate vector after
controlling for the remaining vectors.

## Workflow

The recommended workflow is:

1. prepare the persons-by-variables matrix and standardize when measurement
   scales differ;
2. inspect preliminary MDS solutions and dimensionality evidence;
3. choose interpretable axis directions;
4. run `BootSmacof()` with at least 1,000 bootstrap samples for an analysis;
5. use `summary()`, `plot()`, and the person-level output; and
6. treat downstream group comparisons as optional analyses of returned
   quantities rather than part of the PAMS estimator.

The small bootstrap count below keeps vignette-building fast. It is not a
recommended value for substantive work.

## Reproducible example

The built-in `USArrests` data are used only to demonstrate the software
workflow. States are treated as persons and the four variables as related
measurements.

```{r data}
library(pams)

example_data <- as.data.frame(USArrests[1:12, ])
example_names <- colnames(example_data)
```

Because the variables use different units, the preliminary dissimilarities
are computed after column standardization. Stress can be inspected across
candidate dimensions; with four variables, two dimensions are used here.

```{r preliminary}
standardized <- scale(example_data)
proximity <- dist(t(standardized))
preliminary <- smacof::smacofSym(proximity, ndim = 2, type = "ordinal")
preliminary$stress
preliminary$conf
```

Axis signs are arbitrary. Inspect the preliminary coordinates and select `1`
or `-1` for each axis so that prespecified anchor variables appear on the
desired side. The example retains both signs.

```{r fit}
set.seed(2026)
fit <- suppressWarnings(BootSmacof(
  testdata = example_data,
  participant = 1:3,
  mds = "smacof",
  type = "ordinal",
  distance = "euclid",
  scale = TRUE,
  nprofile = 2,
  direction = c(1, 1),
  cl = 0.95,
  nBoot = 10,
  testname = example_names
))
```

## Summaries and plots

```{r summary}
summary(fit)
round(fit$MDSsummary[[1]], 3)
round(fit$Weight[1:5, ], 3)
```

```{r plot, fig.width=7, fig.height=4}
plot(fit, profiles = 1:2, interval = "BCa")
```

The original-sample MDS fit stored in `fit$MDS` is the result of applying the
selected directions to the same SMACOF analysis. The stress therefore agrees
with the preliminary fit. Coordinate signs should be resolved before direct
comparison.

```{r comparison}
c(preliminary = preliminary$stress, BootSmacof = fit$MDS$stress)

signs <- sign(diag(cor(preliminary$conf, fit$MDS$conf)))
signs[signs == 0] <- 1
aligned_preliminary <- sweep(preliminary$conf, 2, signs, `*`)
max(abs(aligned_preliminary - fit$MDS$conf))
```

## Tidyverse-style handling

The fitted object remains a named list for backward compatibility, so it can
be passed through a base-R pipe and its tabular components can be converted
directly to tibbles. The `tibble` package is optional rather than a required
dependency.

```{r tidyverse}
fit |> summary()

if (requireNamespace("tibble", quietly = TRUE)) {
  weights_tbl <- tibble::as_tibble(fit$Weight, rownames = "participant")
  weights_tbl
}
```

## Sign and alignment limitation

For every bootstrap and jackknife sample, `BootSmacof()` aligns each dimension
to its original-sample counterpart using the sign of their correlation. It
does not perform general rotational alignment or dimension-permutation
matching. Coordinate-wise intervals should therefore be interpreted
cautiously when dimensions are weak or nearly interchangeable. Reversing an
axis changes only its reported sign; it does not change interpoint distances,
stress, overall fit, or whether an interval excludes zero.
