# Snake Plots

Snake plots display data as horizontal bands in a serpentine layout — each row reverses direction and connects to the next through a U-turn arc. This compact layout lets you compare many items at once while preserving adjacency information such as inter-item correlations.

The package ships with three datasets from an experience sampling study of university students (Neubauer & Schmiedek, 2024), rescaled to a 1–5 Likert scale, and 10 built-in color palettes via `snake_palettes`:

    library(snakeplot)

    labs5 <- c("1" = "Not at all", "2" = "Slightly", "3" = "Moderate",
               "4" = "Quite",      "5" = "Extremely")

## Daily EMA — value distribution ticks

When you have beep-level data, `var` and `day` auto-pivot into one band per day. Ticks are positioned within proportional zones by response level:

    survey_snake(ema_beeps, var = "angry", day = "day",
                 colors = snake_palettes$sunset, level_labels = labs5,
                 title = "Anger — 14 days, value distribution")

## Daily EMA — distribution bars

The same daily data with `tick_shape = "bar"` shows how response distributions shift across days. Use `bar_reverse = TRUE` to start from the highest level:

    survey_snake(ema_beeps, var = "happy", day = "day",
                 tick_shape = "bar", bar_reverse = TRUE,
                 colors = snake_palettes$sunset, level_labels = labs5,
                 title = "Happiness — 14 days, distribution bars")

## Activity timelines

`activity_snake()` shows daily event timelines — rug ticks or duration blocks on a dark ribbon:

    set.seed(42)
    days <- c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
    d <- data.frame(
      day      = rep(days, each = 40),
      start    = round(runif(280, 360, 1400)),
      duration = 0
    )
    activity_snake(d)

Pass character timestamps directly — they are parsed automatically:

    set.seed(42)
    dates <- seq(as.POSIXct("2024-03-11"), as.POSIXct("2024-03-17"),
                 by = "day")
    events <- data.frame(
      timestamp = format(
        rep(dates, each = 30) + round(runif(210, 6*3600, 22*3600)),
        "%Y-%m-%d %H:%M:%S"
      ),
      stringsAsFactors = FALSE
    )
    activity_snake(events, title = "From character timestamps — 7 days")

Duration blocks show event length as filled rectangles:

    set.seed(42)
    days <- c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
    d2 <- data.frame(
      day      = rep(days, each = 8),
      start    = round(runif(56, 360, 1200)),
      duration = round(runif(56, 15, 120))
    )
    activity_snake(d2, event_color = "#e09480", band_color = "#3d2518",
                   title = "Weekly activity — duration blocks")

With `flow = "natural"`, all bands read 6AM→12AM left-to-right — no alternating. Compare: the default boustrophedon flips morning events between left and right on alternate rows, while natural keeps them consistently on the left:

    set.seed(1)
    d_morning <- data.frame(
      day      = rep(days, each = 15),
      start    = round(runif(105, 360, 720)),
      duration = 0
    )
    activity_snake(d_morning, flow = "natural",
                   title = "Morning events — flow='natural' (6AM always left)")

## Line snake

`line_snake()` draws a continuous intensity line winding through bands:

    set.seed(42)
    hours <- seq(0, 1440, by = 10)
    d_line <- data.frame(
      day   = rep(c("Mon", "Tue", "Wed", "Thu", "Fri"), each = length(hours)),
      time  = rep(hours, 5),
      value = sin(rep(hours, 5) / 1440 * 4 * pi) * 50 + 50 +
              rnorm(5 * length(hours), 0, 8)
    )
    line_snake(d_line, fill_color = "#e74c3c")

## Timeline snake

`timeline_snake()` takes a 3-column data.frame (role, start, end) and auto-generates monthly blocks, transition labels, and band year labels:

    career <- data.frame(
      role  = c("Intern", "Junior Dev", "Mid Dev",
                "Senior Dev", "Tech Lead", "Architect"),
      start = c("2015-01", "2015-07", "2017-01",
                "2019-07", "2022-07", "2024-01"),
      end   = c("2015-06", "2016-12", "2019-06",
                "2022-06", "2023-12", "2024-12")
    )
    timeline_snake(career,
                   title = "Software Engineer — Career Path (2015-2024)")

## Tick lines with correlation arcs

Individual responses as colored tick marks, with inter-item Pearson r displayed at each U-turn arc:

    survey_snake(ema_emotions, tick_shape = "line",
                 arc_fill = "correlation", sort_by = "mean",
                 colors = snake_palettes$berry, level_labels = labs5,
                 title = "Emotions — correlations at U-turns")

## Dot plot with dark bands

Use `band_palette` for darker band shading. Dots with jitter show individual responses:

    survey_snake(ema_emotions, tick_shape = "dot", sort_by = "mean",
                 colors = snake_palettes$viridis, level_labels = labs5,
                 band_palette = c("#1a1228", "#1a2a42"),
                 title = "Emotions — dots on dark bands")

## Mean and median markers

A diamond shows the item mean; a dashed line shows the median:

    survey_snake(ema_emotions, tick_shape = "bar", sort_by = "mean",
                 show_mean = TRUE, show_median = TRUE,
                 colors = snake_palettes$sunset, level_labels = labs5,
                 title = "Emotions — mean (diamond) and median (dashed)")

## Faceted multi-construct

When column names share a prefix (e.g., `Emo_`, `Mot_`), `facet = TRUE` auto-groups them into panels. Prefixes are stripped from labels:

    survey_snake(student_survey, facet = TRUE, facet_ncol = 2L,
                 tick_shape = "bar", sort_by = "mean",
                 colors = snake_palettes$earth, level_labels = labs5)

## Survey sequence

`survey_sequence()` renders 100% stacked horizontal bars in a serpentine layout:

    survey_sequence(ema_emotions, colors = snake_palettes$earth)

Matrix with row/col names — labels and levels are inferred automatically:

    m <- matrix(c(50, 120, 80, 30,
                  40,  90, 110, 60,
                  70, 100,  70, 50,
                  80,  85,  95, 40,
                  30, 110,  90, 70,
                  60,  75, 105, 55,
                  45, 130,  65, 35), nrow = 7, byrow = TRUE)
    rownames(m) <- c("Satisfaction", "Engagement", "Motivation",
                      "Belonging", "Autonomy", "Competence", "Wellbeing")
    colnames(m) <- c("Low", "Medium", "High", "Very High")
    survey_sequence(m, title = "Labels from matrix dimnames",
                    colors = snake_palettes$sunset)

## Sequential distribution

`sequential_dist()` is a monochrome variant of `survey_sequence()`:

    sequential_dist(ema_emotions)

## Sequence snake

`sequence_snake()` displays a state sequence as colored blocks flowing through the serpentine layout — each block is one time point colored by its state:

    set.seed(42)
    verbs <- c("Read", "Write", "Discuss", "Listen",
               "Search", "Plan", "Code", "Review")
    seq75 <- character(0)
    while (length(seq75) < 75) {
      seq75 <- c(seq75, rep(sample(verbs, 1), sample(1:4, 1)))
    }
    seq75 <- seq75[seq_len(75)]
    sequence_snake(seq75, title = "75-step learning sequence")

Pass a data.frame directly — the first character/factor column is auto-detected:

    set.seed(1)
    logs <- data.frame(
      id     = 1:80,
      action = sample(c("Read", "Write", "Discuss", "Search", "Code",
                         "Plan", "Review", "Listen"), 80, replace = TRUE)
    )
    sequence_snake(logs, rows = 5,
                   title = "From a data.frame — column auto-detected")
    #> Using column 'action' as sequence

## Built-in palettes

10 palettes are available via `snake_palettes` or `snake_palette()`:

    names(snake_palettes)
    #>  [1] "classic" "earth"   "ocean"   "sunset"  "berry"   "blues"   "greens"
    #>  [8] "grays"   "warm"    "viridis"

Use any palette by name:

    survey_snake(ema_emotions, colors = snake_palettes$earth, tick_shape = "bar")
    snake_palette("sunset", n = 5)  # interpolate to 5 colors