| Title: | Serpentine Plots for Long Timeline, Sequential and Survey Data |
|---|---|
| Description: | Visualize long timelines, extended sequences and temporally chained survey responses and experience sampling data using intuitive serpentine (snake) plots. Supports distribution bars, tick-mark plots, inter-item correlation arcs, faceted multi-construct panels, and daily time-of-day positioning for ecological momentary assessment data. |
| Authors: | Mohammed Saqr [aut, cre, cph], Sonsoles Lopez-Pernas [aut] |
| Maintainer: | Mohammed Saqr <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.3.0 |
| Built: | 2026-06-09 05:56:03 UTC |
| Source: | https://github.com/mohsaqr/snakeplot |
A daily activity timeline where each band is one day and colored ticks or blocks on a dark ribbon represent events. The serpentine (boustrophedon) layout connects days via U-turn arcs.
activity_snake( data, band_height = 28, band_gap = 18, day_start = 360, day_end = 1440, plot_width = 500, band_color = "#3d3d4a", event_color = "#d4a843", arc_color = "#2a2a3a", band_opacity = 0.9, arc_opacity = 0.85, event_opacity = 0.85, tick_width = 1.5, show_grid = TRUE, show_total = TRUE, show_count = FALSE, show_hour_labels = TRUE, show_arc_labels = TRUE, shadow = TRUE, grid_color = "rgba(255,255,255,0.25)", label_color = "#cccccc", label_size = 0.85, label_align = "left", orientation = c("horizontal", "vertical"), start_from = c("left", "right"), flow = c("snake", "natural"), day_format = NULL, legend = NULL, title = NULL, margin = c(top = 30, right = 10, bottom = 50, left = 80), background = "white" )activity_snake( data, band_height = 28, band_gap = 18, day_start = 360, day_end = 1440, plot_width = 500, band_color = "#3d3d4a", event_color = "#d4a843", arc_color = "#2a2a3a", band_opacity = 0.9, arc_opacity = 0.85, event_opacity = 0.85, tick_width = 1.5, show_grid = TRUE, show_total = TRUE, show_count = FALSE, show_hour_labels = TRUE, show_arc_labels = TRUE, shadow = TRUE, grid_color = "rgba(255,255,255,0.25)", label_color = "#cccccc", label_size = 0.85, label_align = "left", orientation = c("horizontal", "vertical"), start_from = c("left", "right"), flow = c("snake", "natural"), day_format = NULL, legend = NULL, title = NULL, margin = c(top = 30, right = 10, bottom = 50, left = 80), background = "white" )
data |
Input in one of three formats.
(1) POSIXct vector: a bare vector of timestamps, producing rug
ticks grouped by day.
(2) Numeric format: a data.frame with columns |
band_height |
Numeric. Height of each day band in plot units (default 28). |
band_gap |
Numeric. Vertical gap between bands (default 18). |
day_start |
Numeric. Start of the time window in minutes from midnight (default 360 = 6AM). |
day_end |
Numeric. End of the time window in minutes from midnight (default 1440 = midnight). |
plot_width |
Numeric. Width of the band area in plot units (default 500). |
band_color |
Character or character vector. Band ribbon color(s). If a vector, colors cycle per day (default "#3d3d4a"). |
event_color |
Character or character vector. Event tick/block color(s). If a vector, colors cycle per day (default "#d4a843"). |
arc_color |
Character. Overnight arc color (default "#2a2a3a"). |
band_opacity |
Numeric 0-1 (default 0.90). |
arc_opacity |
Numeric 0-1 (default 0.85). |
event_opacity |
Numeric 0-1 (default 0.85). |
tick_width |
Numeric. Minimum event width in plot units (default 1.5). Use 1.0 for thin rug style. |
show_grid |
Logical. Show hour gridlines (default TRUE). |
show_total |
Logical. Show total duration after day label (default TRUE). |
show_count |
Logical. Show event count in parentheses after day label (default FALSE). |
show_hour_labels |
Logical. Show hour labels at bottom (default TRUE). |
show_arc_labels |
Logical. Show "12AM" at arc tips (default TRUE). |
shadow |
Logical. Draw drop shadows (default TRUE). |
grid_color |
Character. Gridline color (default "rgba(255,255,255,0.25)"). |
label_color |
Character. Day label color (default "#cccccc"). |
label_size |
Numeric. Label font size multiplier (default 0.85). |
label_align |
Character. Label alignment: "left" (default), "right", or "direction" (follows band reading direction). |
orientation |
Character: "horizontal" (default) or "vertical". Controls whether the snake runs left-right or top-bottom. |
start_from |
Character: "left" (default) or "right". Which side the first band starts from. |
flow |
Character, |
day_format |
Optional strftime format for day labels when |
legend |
List of legend items, each with |
title |
Optional plot title. |
margin |
Named numeric vector with top, right, bottom, left margins. |
background |
Background color (default "white"). |
Invisible snake_layout object (for downstream use).
# Weekly rug-style activity plot 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) # Duration blocks 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")# Weekly rug-style activity plot 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) # Duration blocks 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")
All 11 474 experience-sampling beeps from a 14-day study of 321
university students. Each row is one beep with the participant's emotion
ratings and a timestamp. Use with the var/day/timestamp
interface of survey_snake for daily snake plots.
ema_beepsema_beeps
A data.frame with 11 474 rows and 5 columns.
idCharacter. Anonymised participant identifier.
dayInteger 1–14. Study day.
start_timePOSIXct. Timestamp of the beep.
happyInteger 1–5. Self-reported happiness (rescaled from original 1–7).
angryInteger 1–5. Self-reported anger (rescaled from original 1–7).
Neubauer, A. B., & Schmiedek, F. (2024). Approaching academic adjustment on multiple time scales. Zeitschrift fuer Erziehungswissenschaft, 27(1), 147–168. doi:10.1007/s11618-023-01182-8
Data: https://osf.io/bhq3p | Codebook: https://osf.io/csfwg | Code: https://osf.io/84kdr/files | License: CC-BY 4.0
# Anger over 14 days, ticks by time-of-day survey_snake(ema_beeps, var = "angry", day = "day", timestamp = "start_time") # Happiness over 14 days, distribution bars survey_snake(ema_beeps, var = "happy", day = "day", tick_shape = "bar")# Anger over 14 days, ticks by time-of-day survey_snake(ema_beeps, var = "angry", day = "day", timestamp = "start_time") # Happiness over 14 days, distribution bars survey_snake(ema_beeps, var = "happy", day = "day", tick_shape = "bar")
Person-level mean emotion ratings (rounded to integers) from a 14-day
experience sampling study of 280 university students. Ten emotion items
rescaled to a 1–5 Likert scale (original study used 1–7; rescaled via
linear transformation for simplicity). Ready to pass directly to
survey_snake.
ema_emotionsema_emotions
A data.frame with 280 rows and 10 columns (integers 1–5): Happy, Afraid, Sad, Balanced, Exhausted, Cheerful, Worried, Lively, Angry, Relaxed.
Neubauer, A. B., & Schmiedek, F. (2024). Approaching academic adjustment on multiple time scales. Zeitschrift fuer Erziehungswissenschaft, 27(1), 147–168. doi:10.1007/s11618-023-01182-8
Data: https://osf.io/bhq3p | Codebook: https://osf.io/csfwg | Code: https://osf.io/84kdr/files | License: CC-BY 4.0
survey_snake(ema_emotions, tick_shape = "bar", sort_by = "mean")survey_snake(ema_emotions, tick_shape = "bar", sort_by = "mean")
Splits data by a grouping variable and draws side-by-side snake panels.
Works with activity_snake, survey_snake, survey_sequence,
sequential_dist, or line_snake.
facet_snake(data, facet_var, FUN = activity_snake, ncol = NULL, ...)facet_snake(data, facet_var, FUN = activity_snake, ncol = NULL, ...)
data |
Data to plot (passed to |
facet_var |
Character. Column name in |
FUN |
Function to call for each panel. Default |
ncol |
Integer. Number of columns in the facet grid. Default: number of facet levels (all in one row). |
... |
Additional arguments passed to |
Invisible list of results from each panel call.
set.seed(42) d <- data.frame( group = rep(c("A", "B"), each = 70), day = rep(rep(c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"), each = 10), 2), start = round(runif(140, 360, 1400)), duration = 0 ) facet_snake(d, "group")set.seed(42) d <- data.frame( group = rep(c("A", "B"), each = 70), day = rep(rep(c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"), each = 10), 2), start = round(runif(140, 360, 1400)), duration = 0 ) facet_snake(d, "group")
A continuous intensity line winding through a serpentine layout. Each band represents a time segment (e.g., a day); the line's vertical position within the band encodes a continuous value (e.g., foot traffic, CPU usage). The line smoothly curves through U-turn arcs between bands.
line_snake( data, band_height = 40, band_gap = 18, day_start = 0, day_end = 1440, plot_width = 500, line_color = "#e74c3c", line_width = 1.5, fill_color = NULL, fill_opacity = 0.3, band_color = "#2d2d3d", arc_color = "#1a1a2e", band_opacity = 0.9, arc_opacity = 0.85, show_grid = TRUE, shadow = TRUE, label_color = "#cccccc", label_size = 0.85, orientation = c("horizontal", "vertical"), start_from = c("left", "right"), flow = c("snake", "natural"), title = NULL, margin = c(top = 30, right = 10, bottom = 50, left = 80), background = "white" )line_snake( data, band_height = 40, band_gap = 18, day_start = 0, day_end = 1440, plot_width = 500, line_color = "#e74c3c", line_width = 1.5, fill_color = NULL, fill_opacity = 0.3, band_color = "#2d2d3d", arc_color = "#1a1a2e", band_opacity = 0.9, arc_opacity = 0.85, show_grid = TRUE, shadow = TRUE, label_color = "#cccccc", label_size = 0.85, orientation = c("horizontal", "vertical"), start_from = c("left", "right"), flow = c("snake", "natural"), title = NULL, margin = c(top = 30, right = 10, bottom = 50, left = 80), background = "white" )
data |
A data.frame with columns:
Alternatively, a numeric vector (interpreted as evenly-spaced values for a single band). |
band_height |
Numeric (default 40). |
band_gap |
Numeric (default 18). |
day_start |
Numeric, minutes from midnight (default 0). |
day_end |
Numeric, minutes from midnight (default 1440). |
plot_width |
Numeric (default 500). |
line_color |
Character (default "#e74c3c"). |
line_width |
Numeric (default 1.5). |
fill_color |
Optional fill color below the line (default NULL = no fill). |
fill_opacity |
Numeric 0-1 (default 0.3). |
band_color |
Character (default "#2d2d3d"). |
arc_color |
Character (default "#1a1a2e"). |
band_opacity |
Numeric (default 0.90). |
arc_opacity |
Numeric (default 0.85). |
show_grid |
Logical (default TRUE). |
shadow |
Logical (default TRUE). |
label_color |
Character (default "#cccccc"). |
label_size |
Numeric (default 0.85). |
orientation |
Character, "horizontal" or "vertical" (default "horizontal"). |
start_from |
Character, "left" or "right" (default "left"). |
flow |
Character, |
title |
Optional title. |
margin |
Named numeric vector. |
background |
Background color. |
Invisible snake_layout object.
set.seed(42) hours <- seq(0, 1440, by = 10) d <- data.frame( day = rep(c("Mon", "Tue", "Wed"), each = length(hours)), time = rep(hours, 3), value = sin(rep(hours, 3) / 1440 * 4 * pi) * 50 + 50 + rnorm(3 * length(hours), 0, 8) ) line_snake(d, fill_color = "#e74c3c")set.seed(42) hours <- seq(0, 1440, by = 10) d <- data.frame( day = rep(c("Mon", "Tue", "Wed"), each = length(hours)), time = rep(hours, 3), value = sin(rep(hours, 3) / 1440 * 4 * pi) * 50 + 50 + rnorm(3 * length(hours), 0, 8) ) line_snake(d, fill_color = "#e74c3c")
Displays many sequences simultaneously in a serpentine layout. Time points
are packed as blocks within each band (like sequence_snake), with
multiple time points flowing through bands and arcs.
multi_snake( sequences, type = c("index", "distribution"), states = NULL, colors = NULL, sort_by = c("none", "first", "last", "freq", "entropy"), rows = NULL, band_height = 28, band_gap = 18, plot_width = 500, margin = c(top = 30, right = 10, bottom = 50, left = 80), flow = c("snake", "natural"), show_labels = TRUE, show_legend = TRUE, show_percent = TRUE, border_color = NA, title = NULL, background = "white", shadow = TRUE, legend_text_size = 0.8, tick_opacity = 0.85 )multi_snake( sequences, type = c("index", "distribution"), states = NULL, colors = NULL, sort_by = c("none", "first", "last", "freq", "entropy"), rows = NULL, band_height = 28, band_gap = 18, plot_width = 500, margin = c(top = 30, right = 10, bottom = 50, left = 80), flow = c("snake", "natural"), show_labels = TRUE, show_legend = TRUE, show_percent = TRUE, border_color = NA, title = NULL, background = "white", shadow = TRUE, legend_text_size = 0.8, tick_opacity = 0.85 )
sequences |
Matrix or data.frame where rows are sequences and columns are time points. Each cell contains a state label. |
type |
Character, |
states |
Character vector of unique states in desired legend order.
If |
colors |
Named or unnamed character vector of colors. If |
sort_by |
Character controlling sequence order in index mode:
|
rows |
Integer, number of serpentine rows. If |
band_height |
Numeric, height of each band in pixels (default 28). |
band_gap |
Numeric, gap between bands (default 18). |
plot_width |
Numeric, width of each band (default 500). |
margin |
Named numeric vector with top, right, bottom, left margins. |
flow |
Character, |
show_labels |
Logical, show time-point range per row (default TRUE). |
show_legend |
Logical, draw color legend (default TRUE). |
show_percent |
Logical, show percentage labels inside distribution bars
(default TRUE). Only used when |
border_color |
Color for thin borders between blocks, or |
title |
Optional character string for plot title. |
background |
Background color (default |
shadow |
Logical, draw drop shadows (default TRUE). |
legend_text_size |
Numeric, legend text size (default 0.8). |
tick_opacity |
Numeric 0-1, opacity of ticks in index mode (default 0.85). |
Two display modes are supported:
"index"Each block contains thin colored ticks stacked
side by side — one tick per sequence, colored by state at that time
point. Like TraMineR's seqiplot() folded into a serpentine.
"distribution"Each block is a stacked proportional bar
showing what fraction of sequences is in each state at that time
point. Like TraMineR's seqdplot().
Invisible NULL. Called for its side effect of producing
a plot.
set.seed(42) states <- c("Active", "Passive", "Absent") seqs <- matrix(sample(states, 500, replace = TRUE), nrow = 50, ncol = 10) multi_snake(seqs, type = "index") multi_snake(seqs, type = "distribution")set.seed(42) states <- c("Active", "Passive", "Absent") seqs <- matrix(sample(states, 500, replace = TRUE), nrow = 50, ncol = 10) multi_snake(seqs, type = "index") multi_snake(seqs, type = "distribution")
Displays a state sequence as colored blocks flowing through a serpentine (boustrophedon) layout. Each block represents one time point colored by its state. Blocks flow continuously through both bands AND arcs, wrapping a long sequence into a compact multi-row display.
sequence_snake( sequence, states = NULL, colors = NULL, rows = NULL, band_height = 28, band_gap = 18, plot_width = 500, margin = c(top = 30, right = 10, bottom = 50, left = 80), orientation = "horizontal", start_from = "left", flow = c("natural", "snake"), show_labels = TRUE, show_legend = TRUE, show_numbers = FALSE, show_state = FALSE, state_size = 0.35, show_ticks = FALSE, tick_labels = NULL, transition_labels = NULL, transition_pos = NULL, tick_color = "#333333", tick_length = 5, tick_size = 0.4, style = c("block", "rug"), band_color = "#3d3d4a", rug_opacity = 0.9, jitter = 0, border_color = NA, block_labels = NULL, band_labels = NULL, title = NULL, background = "white", shadow = TRUE, text_size = 0.5, legend_text_size = 0.8 )sequence_snake( sequence, states = NULL, colors = NULL, rows = NULL, band_height = 28, band_gap = 18, plot_width = 500, margin = c(top = 30, right = 10, bottom = 50, left = 80), orientation = "horizontal", start_from = "left", flow = c("natural", "snake"), show_labels = TRUE, show_legend = TRUE, show_numbers = FALSE, show_state = FALSE, state_size = 0.35, show_ticks = FALSE, tick_labels = NULL, transition_labels = NULL, transition_pos = NULL, tick_color = "#333333", tick_length = 5, tick_size = 0.4, style = c("block", "rug"), band_color = "#3d3d4a", rug_opacity = 0.9, jitter = 0, border_color = NA, block_labels = NULL, band_labels = NULL, title = NULL, background = "white", shadow = TRUE, text_size = 0.5, legend_text_size = 0.8 )
sequence |
Input in flexible formats:
NA values are dropped with a warning. |
states |
Character vector of unique states in desired order.
If |
colors |
Named character vector of colors keyed by state, or an
unnamed vector recycled to match |
rows |
Integer, number of serpentine rows. If |
band_height |
Numeric, height of each band in pixels (default 28). |
band_gap |
Numeric, gap between bands (default 18). |
plot_width |
Numeric, width of each band (default 500). |
margin |
Named numeric vector with top, right, bottom, left margins. |
orientation |
Character, |
start_from |
Character, |
flow |
Character, |
show_labels |
Logical, show position range labels per row
(default |
show_legend |
Logical, draw color legend (default |
show_numbers |
Logical, print small position numbers inside blocks
(default |
show_state |
Logical, print the state name inside each block
(default |
state_size |
Numeric, text size for state labels (default 0.35). |
show_ticks |
Logical, draw ruler-style tick marks at block
boundaries outside the bands (default |
tick_labels |
Character vector of labels for evenly spaced ruler
marks within each band (e.g., |
transition_labels |
Character vector of date labels for state
transition points (e.g., |
transition_pos |
Numeric vector of fractional block positions for
transition labels (e.g., |
tick_color |
Color for tick marks (default |
tick_length |
Numeric, length of tick marks in pixels (default 5). |
tick_size |
Numeric, text size for tick labels (default 0.4). |
style |
Character, |
band_color |
Character, band ribbon color for rug mode
(default |
rug_opacity |
Numeric 0-1, opacity of rug tick marks (default 0.9). |
jitter |
Numeric 0-1, vertical jitter as fraction of band
height (default 0). When |
border_color |
Color for thin borders between blocks, or |
block_labels |
Optional character vector of labels to display inside
each block (same length as |
band_labels |
Character vector of labels to display centered below
each band (e.g., year labels). Length must equal |
title |
Optional character string for plot title. |
background |
Background color (default |
shadow |
Logical, draw drop shadows (default |
text_size |
Numeric, text size multiplier for block labels (default 0.5). |
legend_text_size |
Numeric, legend text size (default 0.8). |
Invisible NULL. Called for its side effect of producing
a plot.
set.seed(42) verbs <- c("Read", "Write", "Discuss", "Listen", "Search", "Plan", "Code", "Review") seq75 <- sample(verbs, 75, replace = TRUE) sequence_snake(seq75) # Custom colors cols <- c(Read = "#E41A1C", Write = "#377EB8", Discuss = "#4DAF4A", Listen = "#984EA3", Search = "#FF7F00", Plan = "#A6D854", Code = "#A65628", Review = "#F781BF") sequence_snake(seq75, colors = cols, rows = 5)set.seed(42) verbs <- c("Read", "Write", "Discuss", "Listen", "Search", "Plan", "Code", "Review") seq75 <- sample(verbs, 75, replace = TRUE) sequence_snake(seq75) # Custom colors cols <- c(Read = "#E41A1C", Write = "#377EB8", Discuss = "#4DAF4A", Listen = "#984EA3", Search = "#FF7F00", Plan = "#A6D854", Code = "#A65628", Review = "#F781BF") sequence_snake(seq75, colors = cols, rows = 5)
Like survey_sequence but uses a sequential (monochrome)
palette instead of diverging colors. Suitable for ordinal scales without
a natural midpoint (e.g., "Never" to "Always").
sequential_dist( counts, labels = NULL, levels = NULL, hue = 210, band_height = 28, band_gap = 14, plot_width = 500, colors = NULL, show_percent = TRUE, min_segment = 34, arc_style = c("gradient", "neutral"), arc_opacity = 0.85, sort_by = c("none", "mean", "net"), shadow = TRUE, show_legend = TRUE, label_color = "#333333", label_size = 0.85, label_align = "left", reverse_rtl = FALSE, start_from = c("left", "right"), flow = c("snake", "natural"), title = NULL, margin = c(top = 30, right = 10, bottom = 55, left = 100), background = "white" )sequential_dist( counts, labels = NULL, levels = NULL, hue = 210, band_height = 28, band_gap = 14, plot_width = 500, colors = NULL, show_percent = TRUE, min_segment = 34, arc_style = c("gradient", "neutral"), arc_opacity = 0.85, sort_by = c("none", "mean", "net"), shadow = TRUE, show_legend = TRUE, label_color = "#333333", label_size = 0.85, label_align = "left", reverse_rtl = FALSE, start_from = c("left", "right"), flow = c("snake", "natural"), title = NULL, margin = c(top = 30, right = 10, bottom = 55, left = 100), background = "white" )
counts |
Numeric matrix of response counts (rows=items, cols=levels). |
labels |
Character vector of item labels. |
levels |
Character vector of level labels. |
hue |
Numeric 0-360. Base hue for the sequential palette (default 210 = blue). |
band_height |
Numeric (default 28). |
band_gap |
Numeric (default 14). |
plot_width |
Numeric (default 500). |
colors |
Character vector of segment colors. Default: diverging palette. |
show_percent |
Logical. Show percentages inside segments (default TRUE). |
min_segment |
Numeric. Hide label if segment narrower than this (default 34). |
arc_style |
Character: "gradient" or "neutral" (default "gradient"). |
arc_opacity |
Numeric 0-1 (default 0.5). |
sort_by |
Character: "none", "mean", "net" (default "none"). |
shadow |
Logical (default TRUE). |
show_legend |
Logical (default TRUE). |
label_color |
Character (default "#333333"). |
label_size |
Numeric (default 0.85). |
label_align |
Character. Label alignment: "left" (default), "right", or "direction" (follows band reading direction). |
reverse_rtl |
Logical. Reverse segment order on right-to-left bands so the visual reading direction mirrors the data order (default FALSE). |
start_from |
Character: "left" (default) or "right". Which side the first band starts from. |
flow |
Character, |
title |
Optional title. |
margin |
Named numeric vector. |
background |
Background color. |
Invisible snake_layout object.
counts <- matrix(c( 15, 25, 60, 80, 45, 10, 20, 50, 90, 55, 20, 30, 65, 70, 40 ), nrow = 3, byrow = TRUE) labels <- c("Behavior A", "Behavior B", "Behavior C") levs <- c("Never", "Rarely", "Sometimes", "Often", "Always") sequential_dist(counts, labels, levs, hue = 160)counts <- matrix(c( 15, 25, 60, 80, 45, 10, 20, 50, 90, 55, 20, 30, 65, 70, 40 ), nrow = 3, byrow = TRUE) labels <- c("Behavior A", "Behavior B", "Behavior C") levs <- c("Never", "Rarely", "Sometimes", "Often", "Always") sequential_dist(counts, labels, levs, hue = 160)
Returns a color palette interpolated to n colors.
snake_palette(name = "classic", n = 7L)snake_palette(name = "classic", n = 7L)
name |
Character, palette name (see |
n |
Integer, number of colors to return (default 7). |
Character vector of n hex color strings.
snake_palette("ocean", 5) snake_palette("earth", 7) snake_palette("blues", 3)snake_palette("ocean", 5) snake_palette("earth", 7) snake_palette("blues", 3)
A named list of 10 color palettes for snake plots. Each palette contains
7 anchor colors that can be interpolated to any length with
snake_palette.
snake_palettessnake_palettes
A named list of 10 character vectors, each with 7 hex color strings.
Diverging red-to-blue. Clean Likert default.
Diverging brown-to-teal. Natural, understated.
Diverging coral-to-navy. Warm/cool contrast.
Diverging orange-to-indigo. Vivid but balanced.
Diverging rose-to-green. High contrast.
Sequential light-to-dark blue.
Sequential light-to-dark green.
Sequential light-to-dark gray.
Sequential cream-to-dark red.
Sequential yellow-green-blue-purple (viridis-inspired).
snake_palettes$ocean survey_snake(ema_emotions, colors = snake_palettes$earth, tick_shape = "bar", sort_by = "mean")snake_palettes$ocean survey_snake(ema_emotions, colors = snake_palettes$earth, tick_shape = "bar", sort_by = "mean")
Person-level mean scores (rounded to integers) from a 14-day experience
sampling study of 280 university students. Contains 34 items across four
construct groups, rescaled to a 1–5 Likert scale (original 1–7). Column
name prefixes enable automatic faceting with
survey_snake(student_survey, facet = TRUE).
student_surveystudent_survey
A data.frame with 280 rows and 34 integer columns (values 1–5).
Emo_10 emotion items: Happy, Afraid, Sad, Balanced, Exhausted, Cheerful, Worried, Lively, Angry, Relaxed.
Mot_8 study motivation items: Disappointed, FeltBad, Important, Interesting, Compulsory, Proving, Understanding, Enjoyment.
Reg_5 emotion regulation items: SeeGood, FocusGood, Suppression, ChangedFeeling, Rumination.
Eng_11 study engagement items: Enjoy, WearingDown, Satisfied, DifficultReconcile, Interesting, Exhausted, OnlyNecessary, Energy, Identification, Expectations, ConsiderQuitting.
Neubauer, A. B., & Schmiedek, F. (2024). Approaching academic adjustment on multiple time scales. Zeitschrift fuer Erziehungswissenschaft, 27(1), 147–168. doi:10.1007/s11618-023-01182-8
Data: https://osf.io/bhq3p | Codebook: https://osf.io/csfwg | Code: https://osf.io/84kdr/files | License: CC-BY 4.0
survey_snake(student_survey, facet = TRUE, tick_shape = "bar", sort_by = "mean", facet_ncol = 2L)survey_snake(student_survey, facet = TRUE, tick_shape = "bar", sort_by = "mean", facet_ncol = 2L)
Each survey item is a 100\ Color segments represent response levels; percentages are shown inside segments when wide enough. Arcs blend adjacent end-colors.
survey_sequence( counts, labels = NULL, levels = NULL, band_height = 28, band_gap = 14, plot_width = 500, colors = NULL, show_percent = TRUE, min_segment = 34, arc_style = c("gradient", "neutral"), arc_opacity = 0.5, sort_by = c("none", "mean", "net"), shadow = TRUE, show_legend = TRUE, label_color = "#333333", label_size = 0.85, label_align = "left", reverse_rtl = FALSE, start_from = c("left", "right"), flow = c("snake", "natural"), title = NULL, margin = c(top = 30, right = 10, bottom = 55, left = 100), background = "white" )survey_sequence( counts, labels = NULL, levels = NULL, band_height = 28, band_gap = 14, plot_width = 500, colors = NULL, show_percent = TRUE, min_segment = 34, arc_style = c("gradient", "neutral"), arc_opacity = 0.5, sort_by = c("none", "mean", "net"), shadow = TRUE, show_legend = TRUE, label_color = "#333333", label_size = 0.85, label_align = "left", reverse_rtl = FALSE, start_from = c("left", "right"), flow = c("snake", "natural"), title = NULL, margin = c(top = 30, right = 10, bottom = 55, left = 100), background = "white" )
counts |
Numeric matrix of response counts (rows=items, cols=levels). |
labels |
Character vector of item labels. |
levels |
Character vector of level labels. |
band_height |
Numeric (default 28). |
band_gap |
Numeric (default 14). |
plot_width |
Numeric (default 500). |
colors |
Character vector of segment colors. Default: diverging palette. |
show_percent |
Logical. Show percentages inside segments (default TRUE). |
min_segment |
Numeric. Hide label if segment narrower than this (default 34). |
arc_style |
Character: "gradient" or "neutral" (default "gradient"). |
arc_opacity |
Numeric 0-1 (default 0.5). |
sort_by |
Character: "none", "mean", "net" (default "none"). |
shadow |
Logical (default TRUE). |
show_legend |
Logical (default TRUE). |
label_color |
Character (default "#333333"). |
label_size |
Numeric (default 0.85). |
label_align |
Character. Label alignment: "left" (default), "right", or "direction" (follows band reading direction). |
reverse_rtl |
Logical. Reverse segment order on right-to-left bands so the visual reading direction mirrors the data order (default FALSE). |
start_from |
Character: "left" (default) or "right". Which side the first band starts from. |
flow |
Character, |
title |
Optional title. |
margin |
Named numeric vector. |
background |
Background color. |
Invisible snake_layout object.
counts <- matrix(c( 110, 210, 79, 84, 42, 126, 205, 68, 100, 26, 184, 226, 47, 58, 10, 200, 205, 52, 47, 21, 205, 210, 42, 53, 15, 197, 214, 53, 47, 14, 194, 242, 47, 31, 11 ), nrow = 7, byrow = TRUE) labels <- c("LOC1", "LOC2", "LOC3", "CCA1", "LOC5", "LOC5", "LOC4") labels <- paste0(labels, " (n=525)") levs <- as.character(1:5) survey_sequence(counts, labels, levs)counts <- matrix(c( 110, 210, 79, 84, 42, 126, 205, 68, 100, 26, 184, 226, 47, 58, 10, 200, 205, 52, 47, 21, 205, 210, 42, 53, 15, 197, 214, 53, 47, 14, 194, 242, 47, 31, 11 ), nrow = 7, byrow = TRUE) labels <- c("LOC1", "LOC2", "LOC3", "CCA1", "LOC5", "LOC5", "LOC4") labels <- paste0(labels, " (n=525)") levs <- as.character(1:5) survey_sequence(counts, labels, levs)
Each survey item is a horizontal band in a serpentine layout. The band body is shaded by item mean (warm = low, cool = high). Individual responses are shown as colored tick marks. Inter-item correlations appear at U-turns.
survey_snake( counts, labels = NULL, levels = NULL, var = NULL, day = NULL, timestamp = NULL, level_labels = NULL, band_height = 28, band_gap = 18, plot_width = 500, tick_shape = c("line", "dot", "bar"), bar_reverse = FALSE, tick_opacity = 0.75, level_gap = 15, color_mode = c("level", "individual"), colors = NULL, shade_band = TRUE, show_mean = TRUE, show_median = FALSE, show_correlation = TRUE, jitter_range = 0.22, sort_by = c("none", "mean", "net"), shadow = TRUE, label_color = "#333333", label_size = 0.85, label_align = "left", show_legend = TRUE, legend_text_size = 0.65, arc_color = "#2c3e6b", arc_opacity = 0.8, arc_fill = c("none", "correlation", "mean_prev", "blend"), band_palette = NULL, start_from = c("left", "right"), flow = c("snake", "natural"), facet = FALSE, facet_ncol = 2L, title = NULL, margin = c(top = 30, right = 10, bottom = 55, left = 100), background = "white", seed = 42L )survey_snake( counts, labels = NULL, levels = NULL, var = NULL, day = NULL, timestamp = NULL, level_labels = NULL, band_height = 28, band_gap = 18, plot_width = 500, tick_shape = c("line", "dot", "bar"), bar_reverse = FALSE, tick_opacity = 0.75, level_gap = 15, color_mode = c("level", "individual"), colors = NULL, shade_band = TRUE, show_mean = TRUE, show_median = FALSE, show_correlation = TRUE, jitter_range = 0.22, sort_by = c("none", "mean", "net"), shadow = TRUE, label_color = "#333333", label_size = 0.85, label_align = "left", show_legend = TRUE, legend_text_size = 0.65, arc_color = "#2c3e6b", arc_opacity = 0.8, arc_fill = c("none", "correlation", "mean_prev", "blend"), band_palette = NULL, start_from = c("left", "right"), flow = c("snake", "natural"), facet = FALSE, facet_ncol = 2L, title = NULL, margin = c(top = 30, right = 10, bottom = 55, left = 100), background = "white", seed = 42L )
counts |
Input in one of four formats: Raw responses (data.frame) — each column is a survey item, each row
is a respondent. Responses are auto-tabulated into counts.
Counts matrix — rows = items, columns = response levels. Counts data.frame — coerced to matrix. ESM / longitudinal data.frame — when |
labels |
Character vector of item labels (length = nrow(counts)). |
levels |
Character vector of level labels (length = ncol(counts)). |
var |
Character, column name of the response variable for ESM mode.
When provided with |
day |
Character, column name for the day/period grouping variable
(e.g. |
timestamp |
Character, column name of a POSIXct timestamp for ESM
mode. When provided alongside |
level_labels |
Optional named character vector mapping raw level
values to display labels (e.g.
|
band_height |
Numeric (default 28). |
band_gap |
Numeric (default 18). |
plot_width |
Numeric (default 500). |
tick_shape |
Character: "line" (default), "dot", or "bar" (stacked proportional bars with percentage labels). |
bar_reverse |
Logical. When |
tick_opacity |
Numeric 0-1 (default 0.55). |
level_gap |
Numeric. Gap between response-level zones in plot units (default 15). Set to 0 for no separation. |
color_mode |
Character, "level" (color by response level) or "individual" (unique hue per respondent). Default "level". |
colors |
Character vector of colors for response levels. Default uses a diverging palette. |
shade_band |
Logical. Shade band body by item mean (default TRUE). |
show_mean |
Logical. Diamond marker at mean (default TRUE). |
show_median |
Logical. Vertical line at median (default FALSE). |
show_correlation |
Logical. Show Pearson r at U-turns (default TRUE). |
jitter_range |
Numeric. Vertical jitter fraction (default 0.22). |
sort_by |
Character: "none", "mean", or "net" (default "none"). |
shadow |
Logical (default TRUE). |
label_color |
Character (default "#333333"). |
label_size |
Numeric (default 0.85). |
label_align |
Character. Label alignment: "left" (default), "right", or "direction" (follows band reading direction). |
show_legend |
Logical (default TRUE). |
legend_text_size |
Numeric, legend text size (default 0.65). |
arc_color |
Character (default "#2c3e6b"). |
arc_opacity |
Numeric (default 0.80). |
arc_fill |
Character controlling arc fill style:
|
band_palette |
Character vector of 2+ anchor colors for the band
shading gradient. Low item means map to the first color, high means to
the last. Default |
start_from |
Character: "left" (default) or "right". Which side the first band starts from. |
flow |
Character, |
facet |
Logical or named list. When |
facet_ncol |
Integer, number of columns in the facet grid (default 2). |
title |
Optional plot title. |
margin |
Named numeric vector. |
background |
Background color. |
seed |
Integer for reproducible jitter (default 42). |
Invisible snake_layout object (or list of layouts when
faceted).
counts <- matrix(c( 110, 210, 79, 84, 42, 126, 205, 68, 100, 26, 184, 226, 47, 58, 10, 205, 210, 42, 53, 15, 197, 214, 53, 47, 14 ), nrow = 5, byrow = TRUE) labels <- paste0("LOC", 1:5, " (n=525)") levs <- as.character(1:5) survey_snake(counts, labels, levs)counts <- matrix(c( 110, 210, 79, 84, 42, 126, 205, 68, 100, 26, 184, 226, 47, 58, 10, 205, 210, 42, 53, 15, 197, 214, 53, 47, 14 ), nrow = 5, byrow = TRUE) labels <- paste0("LOC", 1:5, " (n=525)") levs <- as.character(1:5) survey_snake(counts, labels, levs)
Displays a career or life-event timeline as a serpentine sequence of colored phases. Each block represents one month, colored by the current state/role. State names are overlaid inside runs of consecutive blocks, and transition dates are shown at juncture points where the state changes.
timeline_snake( sequence, states = NULL, colors = NULL, rows = NULL, band_height = 28, band_gap = 30, plot_width = 500, margin = c(top = 35, right = 10, bottom = 65, left = 20), orientation = "horizontal", start_from = "left", flow = c("natural", "snake"), show_labels = FALSE, show_legend = TRUE, show_numbers = FALSE, show_state = TRUE, state_size = 1, show_ticks = FALSE, tick_labels = NULL, transition_labels = NULL, transition_pos = NULL, tick_color = "#444444", tick_length = 6, tick_size = 0.8, border_color = NA, block_labels = NULL, band_labels = NULL, title = NULL, background = "white", shadow = TRUE, text_size = 0.5, legend_text_size = 1.2 )timeline_snake( sequence, states = NULL, colors = NULL, rows = NULL, band_height = 28, band_gap = 30, plot_width = 500, margin = c(top = 35, right = 10, bottom = 65, left = 20), orientation = "horizontal", start_from = "left", flow = c("natural", "snake"), show_labels = FALSE, show_legend = TRUE, show_numbers = FALSE, show_state = TRUE, state_size = 1, show_ticks = FALSE, tick_labels = NULL, transition_labels = NULL, transition_pos = NULL, tick_color = "#444444", tick_length = 6, tick_size = 0.8, border_color = NA, block_labels = NULL, band_labels = NULL, title = NULL, background = "white", shadow = TRUE, text_size = 0.5, legend_text_size = 1.2 )
sequence |
Either a character/factor vector of states (one per time
unit), or a data.frame with 3 columns: state/role, start date
( |
states |
Character vector of unique states in desired order.
If |
colors |
Named character vector of colors keyed by state, or an
unnamed vector recycled to match |
rows |
Integer, number of serpentine rows. If |
band_height |
Numeric, height of each band (default 28). |
band_gap |
Numeric, gap between bands (default 18). |
plot_width |
Numeric, width of each band (default 500). |
margin |
Named numeric vector with top, right, bottom, left margins
(default |
orientation |
Character, |
start_from |
Character, |
flow |
Character, |
show_labels |
Logical, show position range labels (default
|
show_legend |
Logical, draw color legend (default |
show_numbers |
Logical, print small position numbers inside blocks
(default |
show_state |
Logical, show state names inside blocks (default
|
state_size |
Numeric, state label size (default 1). |
show_ticks |
Logical, draw ruler-style tick marks at block
boundaries outside the bands (default |
tick_labels |
Character vector of labels for evenly spaced ruler
marks within each band (e.g., |
transition_labels |
Character vector of date labels for state
transition points (e.g., |
transition_pos |
Numeric vector of fractional block positions for
transition labels (e.g., |
tick_color |
Color for tick marks (default |
tick_length |
Numeric, length of tick marks in pixels (default 5). |
tick_size |
Numeric, text size for band and transition labels (default 0.8). |
border_color |
Color for thin borders between blocks, or |
block_labels |
Optional character vector of labels to display inside
each block (same length as |
band_labels |
Character vector of labels to display centered below
each band (e.g., year labels). Length must equal |
title |
Optional character string for plot title. |
background |
Background color (default |
shadow |
Logical, draw drop shadows (default |
text_size |
Numeric, text size multiplier for block labels (default 0.5). |
legend_text_size |
Numeric, legend text size (default 1.2). |
Invisible NULL. Called for its side effect of producing
a plot.
# Data.frame input (easiest) career <- data.frame( role = c("Junior", "Senior", "Lead"), start = c("2018-01", "2020-06", "2023-01"), end = c("2020-05", "2022-12", "2024-12") ) timeline_snake(career, title = "Career Timeline")# Data.frame input (easiest) career <- data.frame( role = c("Junior", "Senior", "Lead"), start = c("2018-01", "2020-06", "2023-01"), end = c("2020-05", "2022-12", "2024-12") ) timeline_snake(career, title = "Career Timeline")