10  Layer Types

A layer type is the bundle that determines how data becomes a visual element. It combines three concepts:

Layer functions (pj/lay-point, pj/lay-histogram, pj/lay-bar, (pj/lay-smooth {:stat :linear-model}), etc.) each add a layer with the corresponding layer type. When no layer is added, Plotje infers a layer type from the column types.

All built-in layer types are registered in a data registry. The tables below are generated from that registry – they stay in sync with the code.

(ns plotje-book.layer-types
  (:require
   ;; Kindly -- notebook rendering protocol
   [scicloj.kindly.v4.kind :as kind]
   ;; Plotje -- composable plotting
   [scicloj.plotje.api :as pj]
   ;; Layer-type registry -- for inspecting layer-type data
   [scicloj.plotje.layer-type :as layer-type]
   ;; String utilities
   [clojure.string :as str]))

Reading the Registry

The tables below are generated directly from the layer-type registry, so they track whatever is currently registered. Two small helpers query the registry: used-by returns the comma-separated list of layer types whose given field equals a value, and distinct-in-order returns each distinct field value in the order layer types were registered. Both are used to populate the Mark, Stat, and Position tables further down.

(defn used-by
  "Sorted comma-separated layer-type names whose `field` equals `value`."
  [field value]
  (->> (layer-type/registered)
       (filter (fn [[_ m]] (= value (or (get m field) :identity))))
       (map (comp name key))
       sort
       (str/join ", ")))
(defn distinct-in-order
  "Distinct values of `field` across layer types, in first-seen order."
  [field]
  (let [seen (volatile! #{})]
    (reduce (fn [acc k]
              (let [v (get (layer-type/lookup k) field)]
                (if (@seen v) acc
                    (do (vswap! seen conj v) (conj acc v)))))
            [] layer-type/layer-type-order)))

Layer Types

Each row is a registered layer type showing its mark, stat, and position.

(kind/table
 {:column-names ["Layer type" "Mark" "Stat" "Position"]
  :row-maps
  (for [k layer-type/layer-type-order
        :let [m (layer-type/lookup k)]]
    {"Layer type" (kind/code (pr-str k))
     "Mark" (kind/code (pr-str (:mark m)))
     "Stat" (kind/code (pr-str (:stat m)))
     "Position" (kind/code (pr-str (or (:position m) :identity)))})})
Layer type Mark Stat Position
:point
:point
:identity
:identity
:line
:line
:identity
:identity
:step
:step
:identity
:identity
:area
:area
:identity
:identity
:histogram
:bar
:bin
:identity
:bar
:rect
:count
:identity
:value-bar
:rect
:identity
:identity
:smooth
:line
:loess
:identity
:density
:area
:density
:identity
:tile
:tile
:bin2d
:identity
:density-2d
:tile
:density-2d
:identity
:contour
:contour
:density-2d
:identity
:boxplot
:boxplot
:boxplot
:identity
:violin
:violin
:violin
:identity
:ridgeline
:ridgeline
:violin
:identity
:summary
:pointrange
:summary
:identity
:errorbar
:errorbar
:identity
:identity
:lollipop
:lollipop
:identity
:identity
:text
:text
:identity
:identity
:label
:label
:identity
:identity
:rug
:rug
:identity
:identity
:interval-h
:interval-h
:identity
:identity
:rule-h
:rule-h
:identity
:identity
:rule-v
:rule-v
:identity
:identity
:band-h
:band-h
:identity
:identity
:band-v
:band-v
:identity
:identity

Marks

A mark is the visual shape shown for each data point or group. Several layer types may share the same mark – for instance, histogram and value-bar both produce bars, and lm (linear model) and loess (local regression) both produce lines.

(kind/table
 {:column-names ["Mark" "Shape" "Used by"]
  :row-maps
  (for [mk (distinct-in-order :mark)]
    {"Mark" (kind/code (pr-str mk))
     "Shape" (pj/mark-doc mk)
     "Used by" (used-by :mark mk)})})
Mark Shape Used by
:point
Filled circle point
:line
Connected path line, smooth
:step
Horizontal-then-vertical path step
:area
Filled region under a curve area, density
:bar
Vertical rectangles (binned) histogram
:rect
Positioned rectangles bar, value-bar
:tile
Grid of colored cells density-2d, tile
:contour
Iso-value polylines contour
:boxplot
Box-and-whisker boxplot
:violin
Mirrored density shape violin
:ridgeline
Stacked density curves ridgeline
:pointrange
Point with error bar summary
:errorbar
Vertical error bar errorbar
:lollipop
Stem with dot lollipop
:text
Data-driven label text
:label
Label with background box label
:rug
Axis-margin tick marks rug
:interval-h
Horizontal bars from x to x-end at categorical y interval-h
:rule-h
(no description) rule-h
:rule-v
(no description) rule-v
:band-h
(no description) band-h
:band-v
(no description) band-v

Stats

A stat (statistical transform) processes raw data before rendering. Each stat takes data-space inputs and produces the geometry that its mark will show.

(kind/table
 {:column-names ["Stat" "What it computes" "Used by"]
  :row-maps
  (for [st (distinct-in-order :stat)]
    {"Stat" (kind/code (pr-str st))
     "What it computes" (pj/stat-doc st)
     "Used by" (used-by :stat st)})})
Stat What it computes Used by
:identity
Pass-through β€” no transform area, band-h, band-v, errorbar, interval-h, label, line, lollipop, point, rug, rule-h, rule-v, step, text, value-bar
:bin
Bin numerical values into ranges histogram
:count
Count occurrences per category bar
:loess
LOESS (local regression) smoothing smooth
:density
Density β€” 1D kernel density estimation (KDE) density
:bin2d
2D grid binning (heatmap counts) tile
:density-2d
Density 2D β€” 2D Gaussian kernel density estimation (KDE) contour, density-2d
:boxplot
Five-number summary + outliers boxplot
:violin
KDE per category (density profile) ridgeline, violin
:summary
Mean Β± standard error per category summary

Positions

A position adjustment determines how groups share a categorical axis slot. Position runs between stat computation and rendering.

(kind/table
 {:column-names ["Position" "What it does" "Used by"]
  :row-maps
  (for [pos [:identity :dodge :stack :fill]]
    {"Position" (kind/code (pr-str pos))
     "What it does" (pj/position-doc pos)
     "Used by" (used-by :position pos)})})
Position What it does Used by
:identity
Plot at exact data coordinates (groups overlap) area, band-h, band-v, bar, boxplot, contour, density, density-2d, errorbar, histogram, interval-h, label, line, lollipop, point, ridgeline, rug, rule-h, rule-v, smooth, step, summary, text, tile, value-bar, violin
:dodge
Shift groups side-by-side within a band
:stack
Pile groups cumulatively
:fill
Stack normalized to [0, 1] (proportions)

You can override the default position by passing :position in the layer options. When multiple layers share :position :dodge, they are coordinated together – error bars automatically align with bars.

Layer Options

The options map passed to lay- functions controls aesthetics, statistical parameters, and spatial adjustments for that layer.

Universal options

Accepted by every layer type:

(kind/table
 {:column-names ["Option" "Description"]
  :row-maps
  (for [k layer-type/universal-layer-options]
    {"Option" (kind/code (pr-str k))
     "Description" (get layer-type/layer-option-docs k)})})
Option Description
:x
:y
:color
Column keyword (categorical grouping) or literal color string
:color-type
Override inferred color type β€” :categorical or :numerical. Use :categorical to treat numeric IDs as groups.
:alpha
Column keyword (per-point opacity) or fixed number 0.0–1.0
:group
Column keyword for grouping without color
:position
Position adjustment keyword β€” how overlapping groups are arranged (see pj/position-doc)
:data
:x-type
Override inferred x-column type β€” :categorical, :numerical, or :temporal. Use :categorical on numeric x (hours, years, IDs) when a categorical-axis mark (bar, boxplot) is needed.
:y-type
Override inferred y-column type β€” :categorical, :numerical, or :temporal. Mirror of :x-type, used for horizontal layouts.
:mark
:stat

Layer-type-specific options

Some layer types accept additional keys beyond the universal set. Layer types not listed here accept only the universal options above.

(kind/table
 {:column-names ["Layer type" "Additional options"]
  :row-maps
  (for [k layer-type/layer-type-order
        :let [m (layer-type/lookup k)
              accepts (:accepts m)]
        :when (seq accepts)]
    {"Layer type" (kind/code (pr-str k))
     "Additional options" accepts})})
Layer type Additional options
:point
[:size :shape :jitter :text :nudge-x :nudge-y]
:line
[:size :nudge-x :nudge-y]
:step
[:size]
:histogram
[:normalize :bins :binwidth]
:smooth
[:confidence-band
 :bootstrap-resamples
 :bandwidth
 :size
 :nudge-x
 :nudge-y]
:density
[:bandwidth]
:tile
[:fill :density-2d-grid]
:density-2d
[:density-2d-grid]
:contour
[:levels :size]
:boxplot
[:size]
:violin
[:bandwidth :size]
:ridgeline
[:bandwidth]
:summary
[:size]
:errorbar
[:y-min :y-max :size :nudge-x :nudge-y]
:lollipop
[:size]
:text
[:text :nudge-x :nudge-y]
:label
[:text :nudge-x :nudge-y]
:rug
[:side]
:interval-h
[:x-end :interval-thickness]
:rule-h
[:y-intercept]
:rule-v
[:x-intercept]
:band-h
[:y-min :y-max]
:band-v
[:x-min :x-max]

All layer option keys

(kind/table
 {:column-names ["Option" "Description"]
  :row-maps
  (for [[k desc] (sort-by key layer-type/layer-option-docs)]
    {"Option" (kind/code (pr-str k))
     "Description" desc})})
Option Description
:alpha
Column keyword (per-point opacity) or fixed number 0.0–1.0
:bandwidth
Smoothing bandwidth for density and LOESS methods
:bootstrap-resamples
Number of bootstrap resamples for a LOESS confidence ribbon (default 200)
:color
Column keyword (categorical grouping) or literal color string
:color-type
Override inferred color type β€” :categorical or :numerical. Use :categorical to treat numeric IDs as groups.
:confidence-band
true to show a standard-error confidence ribbon around the fitted line
:density-2d-grid
2D density grid resolution β€” number of bins per axis (default 25)
:fill
Column keyword for tile fill values (pre-computed heatmap)
:group
Column keyword for grouping without color
:interval-thickness
Fraction (0.0–1.0) of the categorical band that an interval bar fills (default 0.7)
:jitter
true or pixel amount β€” random offset to reduce overplotting
:levels
Number of contour iso-levels (default 5)
:normalize
Histogram normalization β€” :density (area integrates to 1) or nil
:nudge-x
Shift all x-coordinates by this data-space amount
:nudge-y
Shift all y-coordinates by this data-space amount
:position
Position adjustment keyword β€” how overlapping groups are arranged (see pj/position-doc)
:shape
Column keyword for per-point shape
:side
Rug tick position β€” :x (default), :y, or :both
:size
Column keyword or fixed number β€” point radius or stroke width
:text
Column keyword for label content
:x-end
Column keyword for the right-edge x value of a horizontal interval bar
:x-intercept
Numeric or temporal x-axis position for a vertical reference line
:x-max
Upper x bound of a vertical shaded band
:x-min
Lower x bound of a vertical shaded band
:x-type
Override inferred x-column type β€” :categorical, :numerical, or :temporal. Use :categorical on numeric x (hours, years, IDs) when a categorical-axis mark (bar, boxplot) is needed.
:y-intercept
Numeric or temporal y-axis position for a horizontal reference line
:y-max
Column keyword for upper error bound of an errorbar, or upper y bound of a horizontal shaded band
:y-min
Column keyword for lower error bound of an errorbar, or lower y bound of a horizontal shaded band
:y-type
Override inferred y-column type β€” :categorical, :numerical, or :temporal. Mirror of :x-type, used for horizontal layouts.

What’s Next

  • Scatter Plots – see point, line, and regression layer types in action
  • Distributions – histograms, density, boxplots, violins
  • Customization – colors, palettes, themes, and per-layer options
source: notebooks/plotje_book/layer_types.clj