---
title: "Sequence Distances, Clustering, and Stability"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Sequence Distances, Clustering, and Stability}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

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

## Synthetic paths

```{r data}
paths <- list(
  s1 = c("A", "B", "C", "D"),
  s2 = c("A", "B", "C", "C"),
  s3 = c("A", "C", "C", "D"),
  s4 = c("D", "C", "B", "A"),
  s5 = c("D", "C", "A", "A"),
  s6 = c("D", "B", "B", "A")
)
sequence_data <- do.call(rbind, lapply(seq_along(paths), function(i) {
  data.frame(sequence_id = names(paths)[i],
             sequence_order = seq_along(paths[[i]]),
             state = paths[[i]], stringsAsFactors = FALSE)
}))
```

## Transparent distance families

```{r distances}
levenshtein <- compute_sequence_distance(sequence_data, method = "levenshtein")
lcs <- compute_sequence_distance(sequence_data, method = "lcs")
om <- compute_sequence_distance(
  sequence_data,
  method = "optimal_matching",
  indel_cost = 1,
  substitution_cost = 2,
  normalise = "max_length"
)
transition <- compute_sequence_distance(sequence_data, method = "transition")
summarise_sequence_distance(lcs)$overall
```

The distance method and all costs are retained as object attributes. The
transition method compares first-order transition-probability profiles; it is
not a general stochastic-process model.

## Clustering and validation

```{r clustering}
fit <- cluster_sequences(lcs, k = 2L, method = "hierarchical", linkage = "average")
fit$assignments
validate_sequence_clusters(fit)$overall
extract_representative_sequences(fit)
```

## Subsampling stability

```{r stability}
stability <- bootstrap_sequence_clusters(
  lcs,
  k = 2L,
  n_boot = 20L,
  sample_fraction = 0.8,
  seed = 100L
)
summarise_sequence_cluster_stability(stability)$overall
```

Cluster stability describes reproducibility under the selected resampling and
clustering settings. It does not establish that the clusters are natural,
causal, or substantively meaningful.

## Co-association ensemble

```{r ensemble}
transition_fit <- cluster_sequences(
  transition,
  k = 2L,
  method = "hierarchical",
  linkage = "average"
)
ensemble <- create_sequence_cluster_ensemble(
  fit,
  transition_fit,
  k = 2L
)
ensemble$assignments
ensemble$coassociation
```

The ensemble records how often pairs are assigned together across supplied
solutions. It does not automatically validate the number or meaning of
clusters.

## Optional PAM and CLARA interfaces

```{r optional-cluster}
if (requireNamespace("cluster", quietly = TRUE)) {
  pam_fit <- cluster_sequences(lcs, k = 2L, method = "pam", seed = 11L)
  clara_fit <- cluster_sequences(
    lcs,
    k = 2L,
    method = "clara",
    seed = 11L,
    samples = 5L,
    sampsize = 5L
  )
  list(pam = pam_fit$assignments, clara = clara_fit$assignments)
}
```

PAM uses the supplied dissimilarities directly. CLARA uses a documented
classical multidimensional-scaling embedding because `cluster::clara()`
expects observations rather than a dissimilarity object.
