ggspec provides a family of spec_*() functions that inspect a ggplot2 object and return its declarative specification as tidy data frames. This makes it easy to answer questions like:
facet_wrap(), and on which variable?library(ggspec)
library(ggplot2)p <- ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(colour = class)) +
geom_smooth(method = "lm", se = FALSE) +
facet_wrap(~drv) +
scale_colour_brewer(palette = "Set1") +
labs(title = "Engine displacement vs highway MPG",
x = "Displacement (L)",
y = "Highway MPG (hwy)")
p
#> `geom_smooth()` using formula = 'y ~ x'spec_layers() returns one row per layer, with columns for the geom, stat, position adjustment, and resolved aesthetic mappings.
spec_layers(p)
#> # A tibble: 3 × 8
#> layer geom stat position mapping params inherit_aes data_id
#> <int> <chr> <chr> <chr> <list> <list> <lgl> <int>
#> 1 0 <NA> <NA> <NA> <chr [2]> <list [0]> NA 1
#> 2 1 point identity identity <chr [3]> <named list [2]> TRUE NA
#> 3 2 smooth smooth identity <chr [2]> <named list [7]> TRUE NAThe mapping column is a list-column of named character vectors, where each name is an aesthetic and each value is the variable mapped to it.
# Inspect the mapping for layer 1 (geom_point)
spec_layers(p)$mapping[[1]]
#> x y
#> "displ" "hwy"By default (inherit = "resolve"), global mappings set in ggplot() are merged with layer-local mappings, with the local mapping taking precedence for any aesthetic defined in both.
# inherit = FALSE: only mappings explicitly set on the layer
spec_layers(p, inherit = FALSE)$mapping
#> [[1]]
#> x y
#> "displ" "hwy"
#>
#> [[2]]
#> colour
#> "class"
#>
#> [[3]]
#> named character(0)spec_aes() pivots the mapping information into long format — one row per (layer-aesthetic) pair — making it easy to filter and inspect with standard dplyr operations.
spec_aes(p)
#> # A tibble: 7 × 5
#> layer geom aesthetic variable source
#> <int> <chr> <chr> <chr> <chr>
#> 1 0 <NA> x displ global
#> 2 0 <NA> y hwy global
#> 3 1 point x displ global
#> 4 1 point y hwy global
#> 5 1 point colour class local
#> 6 2 smooth x displ global
#> 7 2 smooth y hwy globalThe source column distinguishes:
"global" — set in ggplot(aes(...)) only"local" — set in the layer only (e.g. geom_point(aes(...)))"resolved" — set in both; the local value takes precedencelibrary(dplyr)
spec_aes(p) |>
filter(aesthetic == "colour")
#> # A tibble: 1 × 5
#> layer geom aesthetic variable source
#> <int> <chr> <chr> <chr> <chr>
#> 1 1 point colour class localspec_scales() lists only explicitly added scales (default scales inferred at render time are not included, as they do not exist in the object before rendering).
spec_scales(p)
#> # A tibble: 1 × 6
#> aesthetic scale_class scale_type name transform guide
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 colour ScaleDiscrete discrete <NA> <NA> legendspec_facets() returns a single-row data frame describing the faceting.
spec_facets(p)
#> # A tibble: 1 × 6
#> facet_type rows cols scales space labeller
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 wrap <NA> drv fixed <NA> params$labellerFor an unfaceted plot, facet_type is "null".
spec_labels(p)
#> # A tibble: 3 × 2
#> aesthetic label
#> <chr> <chr>
#> 1 x Displacement (L)
#> 2 y Highway MPG (hwy)
#> 3 title Engine displacement vs highway MPGspec_coord(p)
#> # A tibble: 1 × 5
#> coord_type xlim ylim expand clip
#> <chr> <list> <list> <lgl> <chr>
#> 1 cartesian <NULL> <NULL> TRUE on
# Flipped coordinates
spec_coord(p + coord_flip())
#> # A tibble: 1 × 5
#> coord_type xlim ylim expand clip
#> <chr> <list> <list> <lgl> <chr>
#> 1 flip <NULL> <NULL> TRUE onspec_plot()spec_plot() joins all of the above into a single wide data frame with one row per layer. Scale, facet, coordinate, and label information are embedded as list-columns, so all plot information is reachable from a single object.
sp <- spec_plot(p)
sp
#> # A tibble: 3 × 14
#> layer geom stat position mapping params inherit_aes data_id aes_long
#> <int> <chr> <chr> <chr> <list> <list> <lgl> <int> <list>
#> 1 0 <NA> <NA> <NA> <chr> <list [0]> NA 1 <tibble>
#> 2 1 point ident… identity <chr> <named list> TRUE NA <tibble>
#> 3 2 smooth smooth identity <chr> <named list> TRUE NA <tibble>
#> # ℹ 5 more variables: datasets <list>, scales <list>, facets <list>,
#> # coord <list>, labels <list>
# Access per-layer aesthetics
sp$aes_long[[1]]
#> # A tibble: 2 × 5
#> layer geom aesthetic variable source
#> <int> <chr> <chr> <chr> <chr>
#> 1 0 <NA> x displ global
#> 2 0 <NA> y hwy global
# Access the facet spec (same in every row)
sp$facets[[1]]
#> # A tibble: 1 × 6
#> facet_type rows cols scales space labeller
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 wrap <NA> drv fixed <NA> params$labeller