--- title: "Introduction to songR" author: "Raban Heller" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Introduction to songR} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} resource_files: - figures/logo.svg - figures/logo-dark.svg --- [![R-CMD-check](https://github.com/CTTIR/songR/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/CTTIR/songR/actions/workflows/R-CMD-check.yaml) [![pkgdown](https://github.com/CTTIR/songR/actions/workflows/pkgdown.yaml/badge.svg)](https://cttir.github.io/songR/) [![CRAN status](https://www.r-pkg.org/badges/version/songR)](https://CRAN.R-project.org/package=songR) [![Codecov test coverage](https://codecov.io/gh/CTTIR/songR/branch/main/graph/badge.svg)](https://app.codecov.io/gh/CTTIR/songR?branch=main) [![CRAN downloads](https://cranlogs.r-pkg.org/badges/songR)](https://cran.r-project.org/package=songR) [![CRAN downloads total](https://cranlogs.r-pkg.org/badges/grand-total/songR)](https://cran.r-project.org/package=songR) [![License: MIT](https://img.shields.io/badge/license-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental) ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 5 ) ``` ## What is SONG? The Self-Organizing Nebulous Growths (SONG) algorithm is a parametric method for nonlinear dimensionality reduction. Unlike t-SNE and UMAP, SONG supports **incremental visualization**: new data can be added to an existing embedding without reinitializing or retraining the model. SONG is also robust to noise and highly mixed clusters. | Property | SONG | t-SNE | UMAP | |----------|------|-------|------| | Incremental updates | Yes | No | Limited | | Parametric model | Yes (codebook) | No | Optional | | Noise robustness | High | Low | Medium | | Speed | Moderate | Slow | Fast | | Deterministic | With seed | With seed | With seed | The algorithm is described in: > Senanayake, D. A., Wang, W., Naik, S. H., & Halgamuge, S. (2021). > Self-Organizing Nebulous Growths for Robust and Incremental Data > Visualization. *IEEE TNNLS*, 32(10), 4588--4602. ## Installation ```{r install, eval = FALSE} # Install from GitHub devtools::install_github("cttir/songR") ``` ## Quick Start with Iris ```{r quickstart} library(songR) model <- song(as.matrix(iris[, 1:4]), seed = 42, epochs = 20L) plot(model, color_by = iris$Species) ``` ## Working with the Bundled Dataset ```{r blobs} data(songR_blobs) model_blobs <- song(songR_blobs$data, seed = 42, epochs = 15L, verbose = FALSE) plot(model_blobs, color_by = songR_blobs$labels) ``` ## Incremental Visualization This is SONG's key feature. We train on the first half of the data, then incrementally add the second half. ```{r incremental} # Split data data(songR_blobs) n <- nrow(songR_blobs$data) idx1 <- 1:(n / 2) idx2 <- (n / 2 + 1):n # Train on first half model_v1 <- song(songR_blobs$data[idx1, ], seed = 42, epochs = 15L, verbose = FALSE) # Update with second half model_v2 <- update(model_v1, songR_blobs$data[idx2, ], epochs = 10L, verbose = FALSE) par(mfrow = c(1, 2)) plot(model_v1$embedding, pch = 16, cex = 0.5, col = rainbow(8)[as.integer(songR_blobs$labels[idx1])], main = "Before update", xlab = "SONG 1", ylab = "SONG 2", bty = "n") plot(model_v2$embedding, pch = 16, cex = 0.5, col = rainbow(8)[as.integer(songR_blobs$labels[idx2])], main = "After update", xlab = "SONG 1", ylab = "SONG 2", bty = "n") ``` The codebook grows to accommodate new data while preserving the existing embedding structure. ## Predicting New Points ```{r predict} # Train on 90%, predict on 10% train_idx <- 1:135 test_idx <- 136:150 model <- song(as.matrix(iris[train_idx, 1:4]), epochs = 15L, seed = 42, verbose = FALSE) new_coords <- predict(model, newdata = as.matrix(iris[test_idx, 1:4])) # Plot training and test points together plot(model$embedding[, 1], model$embedding[, 2], col = "gray70", pch = 16, cex = 0.6, xlab = "SONG 1", ylab = "SONG 2", main = "Training (gray) + Predicted (red)") points(new_coords[, 1], new_coords[, 2], col = "red", pch = 17, cex = 1.2) ``` ## Comparison: SONG vs t-SNE vs UMAP ```{r comparison, eval = requireNamespace("Rtsne", quietly = TRUE) && requireNamespace("uwot", quietly = TRUE)} mat <- as.matrix(iris[, 1:4]) # SONG song_model <- song(mat, seed = 42, epochs = 20L, verbose = FALSE) # t-SNE tsne_result <- Rtsne::Rtsne(mat, dims = 2, perplexity = 30, verbose = FALSE, check_duplicates = FALSE) # UMAP umap_result <- uwot::umap(mat, n_neighbors = 15, verbose = FALSE) par(mfrow = c(1, 3)) col <- as.integer(iris$Species) plot(song_model$embedding, col = col, pch = 16, main = "SONG", xlab = "SONG 1", ylab = "SONG 2") plot(tsne_result$Y, col = col, pch = 16, main = "t-SNE", xlab = "tSNE 1", ylab = "tSNE 2") plot(umap_result, col = col, pch = 16, main = "UMAP", xlab = "UMAP 1", ylab = "UMAP 2") ``` All three methods separate the Iris species, but only SONG supports incremental updates and retains a parametric codebook model. ## Tuning Guide | Parameter | Default | Description | |-----------|---------|-------------| | `spread_factor` | 0.5 | Growth threshold; higher = more coding vectors. Range: (0, 1). | | `k` | 3 | Neighborhood size. Must be >= `d + 1`. Lower = finer topology. | | `epsilon` | 0.9 | Edge decay rate (0--1). Lower = sparser, faster-pruning graph. | | `epochs` | 50 | Number of self-organisation iterations. More = better convergence. | | `alpha` | 1.0 | Initial learning rate. | | `a`, `b` | 1.577, 0.895 | Kernel shape parameters from the UMAP literature. | | `dispersion` | TRUE | UMAP refinement step for visual cluster separation. | ## The Codebook Model SONG retains a codebook of coding vectors connected by a topology-preserving graph. This is what enables incremental updates and fast projection. ```{r codebook} model <- song(as.matrix(iris[, 1:4]), seed = 42, epochs = 15L, verbose = FALSE) summary(model) plot(model, type = "graph", color_by = iris$Species) ``` ## Interactive Comparison App For interactive exploration, launch the Shiny comparison app: ```{r shiny, eval = FALSE} run_songR_app() ``` This opens a browser-based interface to compare SONG, t-SNE, and UMAP side-by-side on your own data. ## Citation ```{r citation} citation("songR") ```