Utilities and helpers for Single-Case Experimental Design (SCED) using ggplot2
The ggsced package extends the powerful
ggplot2 visualization framework to provide specialized
tools for creating high-quality graphics for Single-Case Experimental
Design (SCED) research. SCED studies are a crucial methodology in
behavioral and educational research, where individual participants serve
as their own controls through carefully designed experimental phases.
This approaches rests on careful visual inspection of data presented in
graphs that clearly delineate phase changes and patterns.
Single-case experimental designs require specific visualization
conventions that are not easily achieved with standard plotting
approaches. The ggsced package bridges the gap between the
flexibility of ggplot2 and the specific visualization needs
of single-case researchers by providing:
ggsced(): Primary function for adding
phase change lines to existing ggplot objectsggsced_style_x() and
ggsced_style_y(): Styling functions for
axes that follow SCED conventions with broken axis appearanceggplot2 faceting for participant comparisons# Install from GitHub (development version)
# install.packages("devtools")
devtools::install_github("miyamot0/ggsced")
# Load the package
library(ggsced)library(ggsced)
library(tidyverse)
library(ggh4x)
data <- Gilroyetal2021
y_mult = .05
x_mult = .02
p = ggplot(data, aes(Session, Responding,
group = Condition)) +
geom_line() +
geom_point(size = 2.5,
pch = 21,
fill = 'black') +
geom_line(mapping = aes(Session, Reinforcers),
lty = 2) +
geom_point(mapping = aes(Session, Reinforcers),
size = 2.5,
pch = 24,
fill = 'white') +
scale_x_continuous(breaks = c(1:25),
limits = c(1, 25),
expand = expansion(mult = x_mult)) +
facet_grid2(Participant ~ .,
scales = "free_y",
remove_labels = "x",
axes = "x") +
facetted_pos_scales(
y = list(
scale_y_continuous(name = "Frequency",
breaks = c(0, 10, 20),
limits = c(0, 20),
expand = expansion(mult = y_mult)),
scale_y_continuous(name = "Frequency",
breaks = c(0, 5, 10),
limits = c(0, 10),
expand = expansion(mult = y_mult)),
scale_y_continuous(name = "Frequency",
breaks = c(0, 10, 20),
limits = c(0, 20),
expand = expansion(mult = y_mult))
)
) +
theme(
text = element_text(size = 14,
color = 'black'),
panel.background = element_blank(),
strip.background = element_blank(),
strip.text = element_blank()
) +
ggsced_style_x(x_mult, lwd = 2) +
ggsced_style_y(y_mult, lwd = 2)
simple_facet_labels_df = ggsced_facet_labels(p, y = 20)
simple_facet_labels_df[2, "Responding"] <- 10
simple_facet_labels_df[3, "Responding"] <- 8
p <- p + geom_text(data = simple_facet_labels_df,
hjust = 1,
vjust = 0.5,
mapping = aes(label = label))
simple_condition_labels_df = ggsced_condition_labels(p)
simple_condition_labels_df$label = gsub("2", "", simple_condition_labels_df$label)
p <- p + geom_text(data = simple_condition_labels_df,
mapping = aes(label = label),
hjust = 0.5,
vjust = 0.5)
# Create extra rows for Bx Labels
extra_labels_df <- simple_condition_labels_df[1:2,]
extra_labels_df$Session <- 21.25
extra_labels_df$x0 <- 21
extra_labels_df$x1 <- 19.5
extra_labels_df$y <- 15
extra_labels_df[1, "label"] <- 'Responding'
extra_labels_df[1, "Responding"] <- 15
extra_labels_df[2, "label"] <- 'Reinforcers'
extra_labels_df[2, "Responding"] <- 5
extra_labels_df[2, "y"] <- 5
p <- p + geom_text(data = extra_labels_df,
mapping = aes(label = label),
hjust = 0,
vjust = 0.5)
p <- p + geom_segment(data = extra_labels_df,
mapping = aes(x = x0,
y,
xend = x1,
yend = y),
arrow = arrow(length = unit(0.25, "cm")))
staggered_pls = list(
'1' = c(3.5, 3.5, 3.5),
'2' = c(6.5, 6.5, 8.5),
'3' = c(9.5, 9.5, 11.5),
'4' = c(12.5, 16.5, 16.5),
'5' = c(15.5, 22.5, 19.5)
)
offsets_pls = list(
'1' = c(F, F, F),
'2' = c(F, F, F),
'3' = c(F, F, F),
'4' = c(F, F, F),
'5' = c(T, F, F)
)
ggsced(p, legs = staggered_pls, offs = offsets_pls)
library(ggsced)
library(tidyverse)
library(ggh4x)
data <- Gilroyetal2021
y_mult = .05
x_mult = .02
p = ggplot(data, aes(Session, Responding,
group = Condition)) +
geom_line() +
geom_point(size = 2.5,
pch = 21,
fill = 'black') +
geom_line(mapping = aes(Session, Reinforcers),
lty = 2) +
geom_point(mapping = aes(Session, Reinforcers),
size = 2.5,
pch = 24,
fill = 'white') +
scale_x_continuous(breaks = c(1:25),
limits = c(1, 25),
expand = expansion(mult = x_mult)) +
facet_grid2(Participant ~ .,
scales = "free_y",
remove_labels = "x",
axes = "x") +
facetted_pos_scales(
y = list(
scale_y_continuous(name = "Frequency",
breaks = c(0, 10, 20),
limits = c(0, 20),
expand = expansion(mult = y_mult)),
scale_y_continuous(name = "Frequency",
breaks = c(0, 5, 10),
limits = c(0, 10),
expand = expansion(mult = y_mult)),
scale_y_continuous(name = "Frequency",
breaks = c(0, 10, 20),
limits = c(0, 20),
expand = expansion(mult = y_mult))
)
) +
theme(
text = element_text(size = 14,
color = 'black'),
panel.background = element_blank(),
strip.background = element_blank(),
strip.text = element_blank()
) +
ggsced_style_x(x_mult, lwd = 2) +
ggsced_style_y(y_mult, lwd = 2)
simple_facet_labels_df = ggsced_facet_labels(p, y = 20)
simple_facet_labels_df[2, "Responding"] <- 10
simple_facet_labels_df[3, "Responding"] <- 8
p <- p + geom_text(data = simple_facet_labels_df,
hjust = 1,
vjust = 0.5,
mapping = aes(label = label))
simple_condition_labels_df = ggsced_condition_labels(p)
simple_condition_labels_df$label = gsub("2", "", simple_condition_labels_df$label)
p <- p + geom_text(data = simple_condition_labels_df,
mapping = aes(label = label),
hjust = 0.5,
vjust = 0.5)
# Create extra rows for Bx Labels
extra_labels_df <- simple_condition_labels_df[1:2,]
extra_labels_df$Session <- 21.25
extra_labels_df$x0 <- 21
extra_labels_df$x1 <- 19.5
extra_labels_df$y <- 15
extra_labels_df[1, "label"] <- 'Responding'
extra_labels_df[1, "Responding"] <- 15
extra_labels_df[2, "label"] <- 'Reinforcers'
extra_labels_df[2, "Responding"] <- 5
extra_labels_df[2, "y"] <- 5
p <- p + geom_text(data = extra_labels_df,
mapping = aes(label = label),
hjust = 0,
vjust = 0.5)
p <- p + geom_segment(data = extra_labels_df,
mapping = aes(x = x0,
y,
xend = x1,
yend = y),
arrow = arrow(length = unit(0.25, "cm")))
staggered_pls = list(
'1' = c(3.5, 3.5, 3.5),
'2' = c(6.5, 6.5, 8.5),
'3' = c(9.5, 9.5, 11.5),
'4' = c(12.5, 16.5, 16.5),
'5' = c(15.5, 22.5, 19.5)
)
offsets_pls = list(
'1' = c(F, F, F),
'2' = c(F, F, F),
'3' = c(F, F, F),
'4' = c(F, F, F),
'5' = c(T, F, F)
)
ggsced(p, legs = staggered_pls, offs = offsets_pls)
The package includes several real research datasets for learning and demonstration:
Gilroyetal2015: Multiple baseline
design dataGilroyetal2021: Cross-lagged
Alternating Treatment Designdemo/ directoryggplot2: Core plotting functionalitygrid: Low-level graphics operationsgtable: Plot layout managementggh4x: Extended ggplot2 functionalityShawn P. Gilroy, Ph.D.
Louisiana State University
📧
sgilroy1@lsu.edu
🆔 ORCID: 0000-0002-1097-8366
If you encounter any issues or have suggestions for improvements, please:
sessionInfo())This package is licensed under the GPL License (V2+).
If you use ggsced in your research, please cite it
appropriately:
Gilroy, S. P. (2026). ggsced: Utilities and helpers for Single-Case
Experimental Design using ggplot2. R package version 0.1.0.
https://github.com/miyamot0/ggsced
This package was developed to support the single-case research
community with publication-quality visualization tools. Special thanks
to the researchers who provided data for demonstration examples and to
the broader R community for the foundation provided by
ggplot2 and related packages.
Keywords: single-case design, SCED, behavioral research, data visualization, ggplot2, R package