8  Options and Scopes

Plotje has three kinds of values you set, each answering a different question:

The distinguishing test is simple: can this kind of value meaningfully have a cross-plot default? A title cannot – each plot needs its own. A palette can – a whole project can share one. A layer’s color mapping is per-layer, but when several layers should share it, you lift it to a wider scope.

Setup

(ns plotje-book.options-and-scopes
  (:require
   ;; Kindly -- notebook rendering protocol
   [scicloj.kindly.v4.kind :as kind]
   ;; Rdatasets -- standard datasets
   [scicloj.metamorph.ml.rdatasets :as rdatasets]
   ;; Plotje -- composable plotting
   [scicloj.plotje.api :as pj]))

A helper to inspect pose structure. It prints :mapping, :layers, and :opts – everything except the :data field, which is omitted for readability.

(defn strip-data [pose]
  (cond-> (dissoc pose :data)
    (:layers pose) (update :layers (partial mapv #(dissoc % :data)))
    (:poses pose) (update :poses (partial mapv strip-data))))
(defn pose-summary
  "Print pose structure without :data (for readability)."
  [pose]
  (kind/pprint (strip-data pose)))

Layer Options

Layer options describe a specific layer. They include:

  • The layer type – the rendering recipe: a mark (point, line, bar, histogram, …), a stat (identity, linear-model, density, binning, …), and a position adjustment (dodge, stack, jitter). The layer type is chosen by which pj/lay-* function you call; its constituents can be overridden via :mark, :stat, and :position keys in the options map.

  • Layer-type parameters – settings specific to the layer type, like :bandwidth for pj/lay-density or :bins for pj/lay-histogram.

  • Aesthetics – how the layer maps data to visuals: :color, :size, :alpha, :shape, :group, plus mark-specific keys like :text and :fill. Either mapped from a column (:color :species) or given as a constant (:alpha 0.3).

  • Data – a per-layer :data key, if the layer should use a different dataset from the rest of the pose.

The primary way to set them is in the options map of pj/lay-*:

(-> (rdatasets/datasets-iris)
    (pj/pose :sepal-length :sepal-width)
    (pj/lay-point {:color :species}))
sepal widthsepal lengthspeciessetosaversicolorvirginica4.55.05.56.06.57.07.58.02.02.53.03.54.04.5

And the pose structure:

(-> (rdatasets/datasets-iris)
    (pj/pose :sepal-length :sepal-width)
    (pj/lay-point {:color :species})
    pose-summary)
{:mapping {:x :sepal-length, :y :sepal-width},
 :layers [{:layer-type :point, :mapping {:color :species}}]}

Scope: generalizing layer options upward

Layer-type parameters, aesthetics, and per-layer data can also be set at wider scopes when they should apply to more than one layer. For a single-panel plot the scope hierarchy has two levels, from narrow to broad:

Scope Set via Reaches
Layer pj/lay-* mapping that one layer
Pose pj/pose mapping every layer on this pose

When the same key is set at both scopes, the narrower one wins.

Composite poses introduce a third level: an outer pose’s mapping flows into each of its descendant leaves, where it combines with the leaf’s own mapping. See Composition for examples.

The layer type itself is chosen per layer – you pick which pj/lay-* to call. Core Concepts teaches how layer placement interacts with pose mappings, and the detailed combination rules for each category of layer option.

Plot Options

Plot options describe a plot as a whole: its title, labels, axis scales, coordinate system, facets. A single-panel plot has one of each. A composite has a scope for them: every cell is a pose, so each cell can have its own title and its own scales, and the options written on the composite itself apply to every cell that does not set them.

Three functions write plot options to the pose’s :opts field:

  • pj/options – plot text (title, subtitle, caption, axis and legend labels) and panel dimensions. It also accepts configuration keys as per-plot overrides (see Configuration below).

  • pj/coord – coordinate system (cartesian, flipped, polar, fixed).

  • pj/facet and pj/facet-grid – split the plot into panels by a column.

pj/scale is the one that does not: a scale belongs to the aesthetic it reads, so it is written into that aesthetic’s mapping rather than into :opts. It takes an axis (:x, :y) or a visual aesthetic. Axis scales accept :linear, :log, :categorical; the continuous visual ones (:size, :alpha, :fill, :color) accept :linear and :log; :shape accepts :categorical. :group has no scale to set – it splits a layer into one drawn group per value and draws nothing of its own – and is refused.

Reference lines and shaded bands – pj/lay-rule-h, pj/lay-rule-v, pj/lay-band-h, pj/lay-band-v – are layers, not plot options. They scope like any other lay-*: bare calls attach to the pose, while passing :x/:y columns targets the most recent matching leaf (or creates a new one).

(-> (rdatasets/datasets-iris)
    (pj/pose :sepal-length :sepal-width)
    pj/lay-point
    (pj/options {:title "Iris"})
    (pj/coord :flip))
Irissepal lengthsepal width2.02.53.03.54.04.54.55.05.56.06.57.07.58.0

And the pose structure:

(-> (rdatasets/datasets-iris)
    (pj/pose :sepal-length :sepal-width)
    pj/lay-point
    (pj/options {:title "Iris"})
    (pj/coord :flip)
    pose-summary)
{:mapping {:x :sepal-length, :y :sepal-width},
 :layers [{:layer-type :point}],
 :opts {:title "Iris", :coord :flip}}

Both the title and the coordinate system landed in :opts. On a single-panel plot like this one they describe the whole plot; written on one cell of a composite they would describe that cell.

A note on faceted plots: a scale has two parts that behave differently across panels.

  • Scale type (log, categorical, linear, etc.) is shared across all panels – if you set pj/scale :x :log on a faceted pose, every panel has a log x-axis.

  • Scale domain (the extent of values shown) is shared as well, so the panels can be compared. {:scales :free-x}, :free-y or :free gives each panel its own on the axis named, and the appearance aesthetics keep one domain either way.

A note on the scope of scales and coord: pj/scale and pj/coord write at the pose they are called on and flow down from there, so called on the pose you are building they cover the whole plot, and called on one cell before the cells are arranged, that cell alone. A scale written in a mapping is the same setting written closer to the layer, and the two accumulate. What has no spelling yet is a type that varies across the panels of a facet: those panels come from one pose, so they share it.

A note on terminology: other chapters call these values plot-level options. Be aware that plot-level here names a category, while pose-level (used for layer options) names a place in a scope hierarchy – the shared word β€œlevel” refers to different things. Putting a plot option inside a pj/lay-* options map – for example {:title "Growth"} – is a category mistake: plot options belong in :opts via their dedicated functions above.

A scale is not a plot option at all. pj/scale writes the mapping the scale reads, so neither {:x-scale {:type :log}} nor any other *-scale key belongs in an options map, at either level. Write (pj/scale pose :x :log), or say it in the mapping as {:x {:column :year :scale :log}}.

Configuration

Configuration controls the rendering defaults – palette, theme, default dimensions, color scale. A configuration value is resolved at render time from a layered stack of sources, from highest priority to lowest:

  1. Plot options on the pose (pj/options) – a per-plot override that wins for that one plot.
  2. Thread-local overrides via pj/with-config.
  3. Global overrides via pj/set-config!.
  4. Project file (plotje.edn), if present.
  5. Library defaults – the baseline included with Plotje.

Sources 2-5 sit outside any specific pose and carry across every plot you render. Source 1 is how a specific pose dips into the chain to override a configuration key for itself – (pj/options {:color-values :dark2}) sets :color-values on one pose, and at render time wins over any palette set through the other four sources.

The Configuration chapter covers each source in depth and lists every configuration key.

pj/config returns the resolved configuration – the merged result of all five sources above.

(select-keys (pj/config) [:width :height :margin])
{:width 600, :height 400, :margin 10}

A Worked Example

The pose below touches two categories explicitly. The third, configuration, shows up at render time.

(def demo
  (-> (rdatasets/datasets-iris)
      (pj/pose :sepal-length :sepal-width)
      ;; layer option: a layer-scope color mapping
      (pj/lay-point {:color :species})
      ;; plot options: stored in :opts
      (pj/options {:title "Iris measurements"})
      (pj/coord :flip)))

The rendered plot:

demo
Iris measurementssepal lengthsepal widthspeciessetosaversicolorvirginica2.02.53.03.54.04.54.55.05.56.06.57.07.58.0

The pose structure:

(pose-summary demo)
{:mapping {:x :sepal-length, :y :sepal-width},
 :layers [{:layer-type :point, :mapping {:color :species}}],
 :opts {:title "Iris measurements", :coord :flip}}

Reading the summary:

  • :color :species is inside the layer’s mapping – a layer option at layer scope.

  • :title and :coord are in :opts – plot options, which on this single-panel plot describe the whole of it.

  • Configuration does not appear in the pose. The renderer will consult (pj/config) for theme, default dimensions, and other defaults.

Coming from ggplot2

For readers familiar with ggplot2, the three categories map roughly as follows:

ggplot2 construct Plotje category
geom_*(aes(...)) layer option (layer scope)
ggplot(aes(...)) layer option (pose scope)
+ geom_*() a layer
+ scale_*(), + coord_*(), + facet_*() plot option
+ labs(...), one-off + theme(...) plot option
theme_set(...), options(...) configuration

In ggplot2, these constructs are reached via separate function families (labs(), theme(), coord_*(), scale_*()). Plotje folds the plot-level concerns into one entry point: pj/options accepts title, axis labels, theme, legend position, dimensions, and other settings as keys in a single map. pj/scale, pj/coord, and pj/facet remain dedicated functions because each carries more structure, but everything else merges.

See Also

  • Core Concepts – the scope hierarchy for layer options in full.

  • Pose Rules – precise rules for each pose-world function: pj/pose, pj/lay-*, pj/arrange, pj/options, pj/scale, pj/coord, pj/facet. Twenty-nine rules with tested assertions.

  • Configuration – the four configuration sources and every configuration key.

  • Glossary – definitions of layer option, plot option, and configuration.

source: notebooks/plotje_book/options_and_scopes.clj