25 API Reference
Complete reference for every public function in scicloj.plotje.api.
Each entry shows the docstring, a live example, and a test. For galleries of mark variations, see the Reference notebooks (Scatter, Distributions, Ranking, Change Over Time, Relationships).
Sample Data
(def tiny {:x [1 2 3 4 5]
:y [2 4 1 5 3]
:group [:a :a :b :b :b]})(def sales {:product [:widget :gadget :gizmo :doohickey]
:revenue [120 340 210 95]})(def measurements {:treatment ["A" "B" "C" "D"]
:mean [10.0 15.0 12.0 18.0]
:ci-lo [8.0 12.0 9.5 15.5]
:ci-hi [12.0 18.0 14.5 20.5]})Construction
pose
[]
[x]
[x y]
[x y z]
[x y z opts]
Construct or extend a pose.
On raw data (first argument is not itself a pose): (pj/pose) – empty leaf (pj/pose data) – leaf with data; on 1-3 column datasets the mapping is auto-inferred (:x, then :y, then :color) so the pose renders without an explicit mapping call (pj/pose data {:color :species}) – leaf with aesthetic mapping (pj/pose data :x-col) – leaf with {:x :x-col} (pj/pose data :x-col {:color :c}) – univariate x + opts (pj/pose data :x-col :y-col) – leaf with :x and :y (pj/pose data :x-col :y-col {:color :c}) – positional x/y + opts (pj/pose data [[:a :b] [:c :d]]) – multi-pair: N bivariate panels (pj/pose data [:a :b :c]) – multi-pair: N univariate panels (pj/pose data (pj/cross cols cols) {:color :c}) – multi-pair plus aesthetic mapping at the composite root
Threaded over an existing pose (first argument is a pose): (pj/pose fr) – pass-through; lifts a literal map for notebook auto-render if it is not already tagged (pj/pose fr :x-col :y-col) – extend a leaf-without-position, or promote a leaf-with-position into a 2-panel composite, or append a panel to a composite (pj/pose fr :x-col :y-col {:color :c}) – same, with aesthetic routed to the composite root on promote (pj/pose fr {:color :c}) – aesthetic-only: extend mapping or (on leaf-with-position) promote (pj/pose fr [[:a :b] [:c :d]]) – multi-pair: append N panels (pj/pose fr (pj/cross cols cols)) – SPLOM N^2 panels in one call (pj/pose fr (pj/cross cols cols) {:color :c}) – SPLOM plus aesthetic mapping at the composite root (pj/pose fr {:data X :color :c}) – extend mapping AND replace the top-level data with X
On a hand-built pose-shaped map (1-arity, input has :layers or :poses): the map is validated and tagged with Kindly auto-render metadata, but its keys are not reordered and its :data is not coerced – the typed shape is preserved verbatim. A flat composite (:poses of leaf maps) is supported; literal nested composites (any sub-pose itself has :poses) are rejected, matching pj/arrange’s rule that its elements must be leaves.
Create a leaf pose with data and columns:
(-> (rdatasets/datasets-iris)
(pj/pose :sepal-length :sepal-width)
pj/lay-point)Map form – include aesthetics on the pose so every layer sees them:
(-> (rdatasets/datasets-iris)
(pj/pose :sepal-length :sepal-width {:color :species})
pj/lay-point
(pj/lay-smooth {:stat :linear-model}))with-data
[pose data]
Supply or replace the top-level dataset on a pose. Useful for building a template once and applying it to different datasets:
(def template (-> (pj/pose)
(pj/pose :x :y {:color :group})
pj/lay-point
(pj/lay-smooth {:stat :linear-model})))
(-> template (pj/with-data my-data))
(-> template (pj/with-data other-data))
At attach time, every keyword column reference in the template’s mapping, layers, sub-poses, and facet options must exist in the dataset – otherwise an error is thrown naming the missing columns and listing what is available. Per-layer / per-sub-pose :data still overrides the top-level data. (with-data pose data)
Attach or replace the top-level dataset on a pose. Useful for building a dataless template and applying it to many datasets:
(def scatter-template
(-> (pj/pose nil {:x :x :y :y :color :group})
pj/lay-point))(-> scatter-template
(pj/with-data tiny))Multi-pair pose – a vector of [x y] pairs creates a composite with one sub-pose per pair:
(-> (rdatasets/datasets-iris)
(pj/pose [[:sepal-length :sepal-width]
[:petal-length :petal-width]])
(pj/lay-point {:color :species}))Map form – explicit keys on a pose:
(-> (rdatasets/datasets-iris)
(pj/pose {:x :sepal-length :y :sepal-width})
pj/lay-point)cross
[xs ys]
Build a vector of [x y] pairs from two column-name sequences. Pair with pj/pose for SPLOM grids: when an MxN rectangle of pairs is threaded through pj/pose, the result is an MxN composite with shared scales.
(pj/cross [:a :b] [:c :d]) ; => [[:a :c] [:a :d] [:b :c] [:b :d]]
(pj/cross [:a :b] [1 2 3])([:a 1] [:a 2] [:a 3] [:b 1] [:b 2] [:b 3])Combine pj/cross with pj/pose to build a SPLOM:
(-> (rdatasets/datasets-iris)
(pj/pose (pj/cross [:sepal-length :petal-length]
[:sepal-width :petal-width])
{:color :species}))Multi-column vector creates one panel per column:
(pj/lay-histogram (rdatasets/datasets-iris) [:sepal-length :sepal-width])Layer Functions
lay
[pose-or-data layer-type-key]
[pose-or-data layer-type-key opts]
Add a root-scope layer. The layer attaches to :layers and flows to every descendant leaf at plan time (composite) or renders on the single panel (leaf).
The generic layer adder. pj/lay-point, pj/lay-bar, etc. are convenience wrappers around pj/lay with a registered layer-type key. Use pj/lay directly when you have a custom layer type (from pj/layer-type-lookup on a registered key, or a raw layer-type map from an extension):
(-> (rdatasets/datasets-iris)
(pj/pose :sepal-length :sepal-width)
(pj/lay :point))The above just delegates to :point – equivalent to pj/lay-point. The intended use of pj/lay is with a layer type that isn’t a built-in convenience: a registered custom layer type from an extension, or a raw layer-type map. See the Waterfall Extension chapter for the full pattern – registering a :waterfall layer type and calling (pj/lay pose (layer-type/lookup :waterfall)) or wrapping it in a pj/lay-waterfall convenience function.
lay-point
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add a :point (scatter) layer to a pose. Without columns -> bare layer at the pose’s root (flows to every leaf). With columns -> position-bearing layer (attaches to the matching leaf via DFS-last identity, or appends a new sub-pose on miss). (lay-point fr) – bare layer at root (lay-point fr {:color :species}) – bare layer with aesthetic opts (lay-point data :x :y) – coerce data to a leaf, then attach (lay-point data :x :y {:color :c}) – same with aesthetic opts
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width {:color :species}))lay-line
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :line layer type – connected line through data points. Requires x (numerical) and y (numerical). Accepts :color, :alpha, :size (stroke width), :nudge-x, :nudge-y.
(def wave {:x (range 30)
:y (map #(Math/sin (* % 0.3)) (range 30))})(-> wave
(pj/lay-line :x :y))lay-histogram
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :histogram layer type – bin numerical values into bars. X-only: pass one column. Accepts :bins (count), :binwidth, :color, :normalize (:density for density-normalized heights).
(-> (rdatasets/datasets-iris)
(pj/lay-histogram :sepal-length))lay-bar
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :bar layer type – count occurrences of each category. X-only: pass one categorical column. Accepts :color for grouped bars.
(-> (rdatasets/datasets-iris)
(pj/lay-bar :species))Stacked bars: pass {:position :stack} to pj/lay-bar.
(-> (rdatasets/palmerpenguins-penguins)
(pj/lay-bar :island {:position :stack :color :species}))100% stacked bars: pass {:position :fill} to pj/lay-bar.
(-> (rdatasets/palmerpenguins-penguins)
(pj/lay-bar :island {:position :fill :color :species}))lay-value-bar
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :value-bar layer type – bars with pre-computed heights. Requires categorical x and numerical y. Unlike :bar (which counts), :value-bar uses the y value directly as the bar height.
(-> sales
(pj/lay-value-bar :product :revenue))Linear regression: pass {:stat :linear-model} to pj/lay-smooth.
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width)
(pj/lay-smooth {:stat :linear-model}))lay-smooth
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :smooth layer type – a smoothed trend line. Defaults to LOESS (local regression). Pass {:stat :linear-model} for ordinary least squares instead. Requires x and y (both numerical). Accepts {:confidence-band true} for a confidence ribbon.
(-> (let [r (rng/rng :jdk 42)
xs (vec (range 50))]
{:x xs
:y (mapv #(+ (Math/sin (* % 0.2))
(* 0.3 (- (rng/drandom r) 0.5)))
xs)})
(pj/lay-point :x :y)
(pj/lay-smooth {:bandwidth 0.2}))lay-density
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :density layer type – kernel density estimate curve. X-only: pass one numerical column. Accepts :color, :bandwidth.
(-> (rdatasets/datasets-iris)
(pj/lay-density :sepal-length))lay-area
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :area layer type – filled region between y and the baseline. Requires x and y (both numerical). Accepts :color, :alpha.
(-> wave
(pj/lay-area :x :y))Stacked areas: pass {:position :stack} to pj/lay-area.
(-> {:x (concat (range 10) (range 10) (range 10))
:y (concat [1 2 3 4 5 4 3 2 1 0]
[2 2 2 3 3 3 2 2 2 2]
[1 1 1 1 2 2 2 1 1 1])
:group (concat (repeat 10 "A") (repeat 10 "B") (repeat 10 "C"))}
(pj/lay-area :x :y {:position :stack :color :group}))lay-text
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :text layer type – text labels at data coordinates. Requires x, y, and {:text :column} for label content.
(-> {:x [1 2 3 4] :y [4 7 5 8] :name ["A" "B" "C" "D"]}
(pj/lay-text :x :y {:text :name}))lay-label
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :label layer type – text labels with background box at data coordinates. Like :text but with a rectangular background for readability.
(-> {:x [1 2 3 4] :y [4 7 5 8] :name ["A" "B" "C" "D"]}
(pj/lay-point :x :y {:size 5})
(pj/lay-label {:text :name}))lay-boxplot
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :boxplot layer type – box-and-whisker plot. Requires categorical x and numerical y. Shows median, quartiles, whiskers, and outliers. Accepts :color for grouped boxplots.
(-> (rdatasets/datasets-iris)
(pj/lay-boxplot :species :sepal-width))lay-violin
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :violin layer type – mirrored density estimate by category. Requires categorical x and numerical y. Accepts :color, :bandwidth.
(-> (rdatasets/reshape2-tips)
(pj/lay-violin :day :total-bill))lay-errorbar
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :errorbar layer type – vertical error bars from pre-computed bounds. Requires x, y, and {:y-min :col :y-max :col} for lower/upper bounds.
(-> measurements
(pj/lay-point :treatment :mean)
(pj/lay-errorbar {:y-min :ci-lo :y-max :ci-hi}))lay-lollipop
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :lollipop layer type – dot on a stem from the baseline. Requires categorical x and numerical y. Like value-bar but with a circle+line instead of a filled rectangle.
(-> sales
(pj/lay-lollipop :product :revenue))lay-tile
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :tile layer type – colored grid cells (heatmap). With :fill option: pre-computed tile colors from a column. Without :fill: auto-binned 2D histogram (stat :bin2d).
(-> (rdatasets/datasets-iris)
(pj/lay-tile :sepal-length :sepal-width))lay-density-2d
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :density-2d layer type – 2D kernel density heatmap. Requires x and y (both numerical). Produces a smoothed density surface as colored tiles with a continuous gradient legend.
(-> (rdatasets/datasets-iris)
(pj/lay-density-2d :sepal-length :sepal-width))lay-contour
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :contour layer type – iso-density contour lines from 2D KDE. Requires x and y (both numerical). Accepts {:levels 10} for the number of contour levels.
(-> (rdatasets/datasets-iris)
(pj/lay-contour :sepal-length :sepal-width))lay-ridgeline
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :ridgeline layer type – stacked density curves by category. Requires categorical x and numerical y. Categories stack vertically with density curves rendered horizontally.
(-> (rdatasets/datasets-iris)
(pj/lay-ridgeline :species :sepal-length))lay-rug
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :rug layer type – short tick marks along the axis showing individual values. X-only: pass one column. Often layered with density or scatter.
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width)
(pj/lay-rug {:side :both}))lay-step
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :step layer type – staircase line (horizontal then vertical). Requires x and y (both numerical).
(-> tiny
(pj/lay-step :x :y)
pj/lay-point)lay-summary
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :summary layer type – mean +/- standard error per category. Requires categorical x and numerical y. Shows a point at the mean with error bars for +/- 1 SE. Accepts :color for grouped summaries.
(-> (rdatasets/datasets-iris)
(pj/lay-summary :species :sepal-length))lay-interval-h
[pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :interval-h layer type – horizontal bar from x to x-end at categorical y. Each row becomes one rectangle; the y column is treated categorically so each distinct value occupies its own lane. Required: x (numeric or temporal start), y (categorical lane), :x-end column ref in opts (numeric or temporal end). Accepts :color, :alpha, :interval-thickness (band fill fraction, 0.0-1.0, default 0.7). (lay-interval-h data :start :task {:x-end :end :color :status})
(-> {:start [#inst "2024-01-01" #inst "2024-03-01" #inst "2024-05-01"]
:end [#inst "2024-04-01" #inst "2024-06-01" #inst "2024-08-01"]
:task ["Design" "Build" "Test"]}
(pj/lay-interval-h :start :task {:x-end :end}))Annotations
Reference lines and shaded bands are regular layers. Position comes from the options map (:y-intercept for lay-rule-h, :x-intercept for lay-rule-v; :y-min/:y-max for lay-band-h, :x-min/:x-max for lay-band-v); appearance aesthetics like :color and :alpha work the same as on any other layer. Without x/y columns they attach at the root (every panel); with x/y columns they attach to one matching leaf.
Rule intercepts also accept temporal values (LocalDate, LocalDateTime, Instant, java.util.Date) so date-axis annotations need no manual conversion – see the second lay-rule-v example below.
lay-rule-v
[_pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :rule-v layer – vertical reference line at x = x-intercept. Position comes from opts (not data columns); :x-intercept is required. Accepts :x-intercept (numeric or temporal – LocalDate, LocalDateTime, Instant, java.util.Date), :color (literal string), :alpha. Temporal values are converted internally to match the x-axis scale so date-axis annotations work without manual conversion. The 4-arity finds or creates a sub-pose with these x/y columns and attaches the rule there (only panels matching that leaf show it). (lay-rule-v pose {:x-intercept 5}) – root-level, flows to every panel (lay-rule-v pose :x :y {:x-intercept 5}) – panel-scope (columns pick or create a sub-pose) (lay-rule-v pose {:x-intercept 5 :color “red” :alpha 0.5}) (lay-rule-v pose {:x-intercept #inst “2008-09-15”})
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width)
(pj/lay-rule-v {:x-intercept 6.0}))A temporal rule on a date axis – the same pose pattern, with :x-intercept taking a LocalDate.
(-> {:date [#inst "2024-01-01" #inst "2024-04-01" #inst "2024-08-01"]
:value [3 5 9]}
(pj/lay-line :date :value)
(pj/lay-rule-v {:x-intercept (java.time.LocalDate/parse "2024-06-01")
:color "#c0392b"}))lay-rule-h
[_pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :rule-h layer – horizontal reference line at y = y-intercept. Position comes from opts (not data columns); :y-intercept is required. Accepts :y-intercept (numeric or temporal – LocalDate, LocalDateTime, Instant, java.util.Date), :color (literal string), :alpha. Temporal values are converted internally to match the y-axis scale so date-axis annotations work without manual conversion. The 4-arity finds or creates a sub-pose with these x/y columns and attaches the rule there (only panels matching that leaf show it). (lay-rule-h pose {:y-intercept 3}) – root-level, flows to every panel (lay-rule-h pose :x :y {:y-intercept 3}) – panel-scope (columns pick or create a sub-pose) (lay-rule-h pose {:y-intercept 3 :color “red” :alpha 0.5}) (lay-rule-h pose {:y-intercept (java.time.LocalDate/parse “2024-01-01”)})
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width)
(pj/lay-rule-h {:y-intercept 3.0}))lay-band-v
[_pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :band-v layer – vertical shaded band between x = x-min and x = x-max. Position comes from opts (not data columns); :x-min and :x-max are required and :x-min must be <= :x-max. Accepts :x-min (required), :x-max (required), :color (literal string), :alpha. The 4-arity finds or creates a sub-pose with these x/y columns and attaches the band there (only panels matching that leaf show it). (lay-band-v pose {:x-min 4 :x-max 6}) – root-level, flows to every panel (lay-band-v pose :x :y {:x-min 4 :x-max 6}) – panel-scope (columns pick or create a sub-pose) (lay-band-v pose {:x-min 4 :x-max 6 :color “blue” :alpha 0.3})
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width)
(pj/lay-band-v {:x-min 5.5 :x-max 6.5}))lay-band-h
[_pose-or-data]
[pose-or-data x-or-opts]
[pose-or-data x y-or-opts]
[pose-or-data x y opts]
Add :band-h layer – horizontal shaded band between y = y-min and y = y-max. Position comes from opts (not data columns); :y-min and :y-max are required and :y-min must be <= :y-max. Accepts :y-min (required), :y-max (required), :color (literal string), :alpha. The 4-arity finds or creates a sub-pose with these x/y columns and attaches the band there (only panels matching that leaf show it). (lay-band-h pose {:y-min 2 :y-max 4}) – root-level, flows to every panel (lay-band-h pose :x :y {:y-min 2 :y-max 4}) – panel-scope (columns pick or create a sub-pose) (lay-band-h pose {:y-min 2 :y-max 4 :color “blue” :alpha 0.3})
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width)
(pj/lay-band-h {:y-min 2.5 :y-max 3.5}))Transforms
coord
[pose coord-type]
Set coordinate transform on a pose. Coord is plot-level – it applies across every panel. On a composite pose the coord attaches to the root so every descendant leaf inherits it at plan time. (coord pose :flip) – flipped coordinates.
Flip axes:
(-> (rdatasets/datasets-iris)
(pj/lay-bar :species) (pj/coord :flip))Polar coordinates:
(-> (rdatasets/datasets-iris)
(pj/lay-bar :species) (pj/coord :polar))scale
[pose channel scale-type]
Set scale on a pose. Scale is plot-level – it applies across every panel. Accepts a type keyword or a scale spec map with :type, optional :domain, and optional :breaks (explicit tick locations). On a composite pose the scale attaches to the root so every descendant leaf inherits it at plan time.
Axis channels (:x, :y) accept :linear, :log, :categorical. Continuous visual channels (:size, :alpha, :fill, :color) accept :linear and :log only – :categorical does not apply. Discrete visual channels (:shape, :group) accept :categorical only – :linear and :log do not apply to a discrete encoding. The :domain on a discrete scale gives explicit category order for the legend.
(scale pose :x :log) – log scale on x-axis (scale pose :x {:type :categorical :domain […]}) – explicit category order (scale pose :y {:type :linear :breaks [0 5 10]}) – pin tick locations (scale pose :y {:type :log :domain [1 1000]}) – log scale with explicit range (scale pose :size :log) – log-spaced point sizes (scale pose :fill :log) – log-spaced tile fill (scale pose :shape {:type :categorical :domain […]}) – shape legend order
Log scale:
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width) (pj/scale :x :log))Fixed domain:
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width) (pj/scale :x {:domain [3 9]}))Log scale on a visual channel (:size, :alpha, :fill, or :color):
(-> {:user [:a :b :c] :n [10 100 1000]}
(pj/lay-point :user :n {:size :n :x-type :categorical})
(pj/scale :size :log))Faceting
facet
[pose col]
[pose col direction]
Facet a pose by a column. Direction is :col (default, horizontal row) or :row (vertical column). Faceting is plot-level – every panel is faceted the same way. Composite poses are not supported yet (see dev-notes/facet-composite-deferral.md).
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width {:color :species})
(pj/facet :species))facet-grid
[pose col-col row-col]
Facet a pose by two columns (2D grid). Faceting is plot-level – every panel is faceted the same way. Composite poses are not supported yet (see dev-notes/facet-composite-deferral.md).
(-> (rdatasets/reshape2-tips)
(pj/lay-point :total-bill :tip {:color :sex})
(pj/facet-grid :smoker :sex))Composition
arrange
[plots]
[plots opts]
Arrange multiple leaf poses in a grid. Returns a composite pose that renders through the compositor via membrane – so :svg, :bufimg, and any other membrane target work uniformly.
Inputs must be leaf poses. Pre-rendered hiccup is not accepted; build your own [:div ...] if you need to combine already-rendered values outside the library.
Opts: :cols N explicit column count (default: min(4, n-plots)) :title STRING centered title band above the grid :width W total composite width in pixels :height H total composite height in pixels :share-scales S subset of #{:x :y} shared across cells (default: #{})
(arrange [fr-a fr-b]) – 1x2 row (arrange [fr-a fr-b fr-c] {:cols 2 :width 900}) – 2x2 grid (wraps) (arrange [[fr-a fr-b] [fr-c fr-d]]) – explicit 2x2 grid
(pj/arrange [(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width {:color :species})
(pj/options {:width 250 :height 200}))
(-> (rdatasets/datasets-iris)
(pj/lay-point :petal-length :petal-width {:color :species})
(pj/options {:width 250 :height 200}))]
{:cols 2})Rendering
plot
[pose]
[pose opts]
Render a pose to a figure. The format keyword in the pose’s :opts ({:format :svg} – default; {:format :bufimg} for raster PNG via Java2D; or any other registered backend) selects which plan->plot / membrane->plot defmethod runs.
On a composite pose, leaves are rendered individually and tiled via the layout in the resolved chrome, in the same chosen format. The pose flows through the canonical pose -> draft -> plan -> membrane -> plot pipeline for both leaf and composite shapes. (plot pose) (plot pose {:width 800 :title “My Plot”}) (plot pose {:format :bufimg}) ;; returns a BufferedImage
See the Customization notebook for options (title, theme, tooltip, brush, legend position, palette).
(-> tiny
(pj/lay-point :x :y))options
[pose opts]
Set plot-level options (title, labels, width, height, etc.). Nested maps (e.g. :theme) are deep-merged. :width and :height are coerced to long (rounded) so the plan carries integer pixel dimensions through to render. On a composite pose the options attach to the root so every descendant leaf inherits them at plan time.
Set render options on a pose:
(-> tiny
(pj/lay-point :x :y)
(pj/options {:width 400 :height 200 :title "Small Plot"}))Predicates
pose?
[x]
Return true if x is a pose-shaped plain map (a map carrying at least one of :layers or :poses).
Check whether a value is a pose (leaf or composite):
(pj/pose? (-> tiny (pj/pose :x :y) pj/lay-point))trueplan?
[x]
Return true if x is a plan (leaf or composite) – the resolved geometry returned by pj/plan.
Check whether a value is a plan (from pj/plan):
(pj/plan? (pj/plan (pj/lay-point tiny :x :y)))trueplan-layer?
[x]
Return true if x is a plan-layer (resolved geometry for one mark).
Check whether a value is a resolved plan layer:
(pj/plan-layer? (first (:layers (first (:panels (pj/plan (pj/lay-point tiny :x :y)))))))truelayer-type?
[x]
Return true if x is a layer type (mark + stat + position bundle from the registry).
Check whether a value is a registered layer-type map:
(pj/layer-type? (pj/layer-type-lookup :point))trueInspection
draft
[pose]
[pose opts]
Resolve a pose into a draft. For a leaf pose, returns a vector of flat maps – one per applicable layer, with all scope merged: data, mappings, and layer type fully determined. For a composite pose, returns a CompositeDraft carrying per-leaf drafts (each contextualized – shared-scale domains injected, suppress-* flags applied), the resolved chrome geometry, and the layout (path -> rect). The 2-arity folds opts into the pose first via pj/options, mirroring the 2-arity of pj/plan and pj/plot. (draft pose) (draft pose {:width 800 :title “Plot”})
Flatten a pose into a vector of draft layers – one per applicable layer, with all scope merged. Useful for inspecting exactly what the renderer will draw:
(-> (rdatasets/datasets-iris)
(pj/pose :sepal-length :sepal-width)
pj/lay-point
pj/draft
kind/pprint)[{:x :sepal-length,
:y :sepal-width,
:mark :point,
:stat :identity,
:layer-type :point,
:data
https://vincentarelbundock.github.io/Rdatasets/csv/datasets/iris.csv [150 6]:
| :rownames | :sepal-length | :sepal-width | :petal-length | :petal-width | :species |
|----------:|--------------:|-------------:|--------------:|-------------:|-----------|
| 1 | 5.1 | 3.5 | 1.4 | 0.2 | setosa |
| 2 | 4.9 | 3.0 | 1.4 | 0.2 | setosa |
| 3 | 4.7 | 3.2 | 1.3 | 0.2 | setosa |
| 4 | 4.6 | 3.1 | 1.5 | 0.2 | setosa |
| 5 | 5.0 | 3.6 | 1.4 | 0.2 | setosa |
| 6 | 5.4 | 3.9 | 1.7 | 0.4 | setosa |
| 7 | 4.6 | 3.4 | 1.4 | 0.3 | setosa |
| 8 | 5.0 | 3.4 | 1.5 | 0.2 | setosa |
| 9 | 4.4 | 2.9 | 1.4 | 0.2 | setosa |
| 10 | 4.9 | 3.1 | 1.5 | 0.1 | setosa |
| ... | ... | ... | ... | ... | ... |
| 140 | 6.9 | 3.1 | 5.4 | 2.1 | virginica |
| 141 | 6.7 | 3.1 | 5.6 | 2.4 | virginica |
| 142 | 6.9 | 3.1 | 5.1 | 2.3 | virginica |
| 143 | 5.8 | 2.7 | 5.1 | 1.9 | virginica |
| 144 | 6.8 | 3.2 | 5.9 | 2.3 | virginica |
| 145 | 6.7 | 3.3 | 5.7 | 2.5 | virginica |
| 146 | 6.7 | 3.0 | 5.2 | 2.3 | virginica |
| 147 | 6.3 | 2.5 | 5.0 | 1.9 | virginica |
| 148 | 6.5 | 3.0 | 5.2 | 2.0 | virginica |
| 149 | 6.2 | 3.4 | 5.4 | 2.3 | virginica |
| 150 | 5.9 | 3.0 | 5.1 | 1.8 | virginica |
,
:__panel-idx 0}]plan
[pose]
[pose opts]
Convert a pose into a plan. For a leaf pose, returns a Plan record with one panel per facet variant. For a composite pose, returns a CompositePlan record with :sub-plots tying each leaf path to its rect and sub-plan, plus :chrome carrying the resolved layout geometry (title-band, grid-rect, strip labels, shared-legend spec). (plan pose) (plan pose {:title “My Plot”})
Returns the intermediate plan data structure:
(def plan1 (-> tiny
(pj/lay-point :x :y)
pj/plan))plan1{:panels
[{:coord :cartesian,
:y-domain [0.8 5.2],
:x-scale {:type :linear},
:x-domain [0.8 5.2],
:x-ticks
{:values [1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0],
:labels ["1.0" "1.5" "2.0" "2.5" "3.0" "3.5" "4.0" "4.5" "5.0"],
:categorical? false},
:col 0,
:layers
[{:mark :point,
:style {:opacity 0.75, :radius 3.0},
:size-scale nil,
:alpha-scale nil,
:groups
[{:color [0.2 0.2 0.2 1.0], :xs #tech.v3.dataset.column<int64>[5]
:x
[1, 2, 3, 4, 5], :ys #tech.v3.dataset.column<int64>[5]
:y
[2, 4, 1, 5, 3], :row-indices #tech.v3.dataset.column<int64>[5]
:__row-idx
[0, 1, 2, 3, 4]}],
:y-domain [1 5],
:x-domain [1 5]}],
:y-scale {:type :linear},
:y-ticks
{:values [1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0],
:labels ["1.0" "1.5" "2.0" "2.5" "3.0" "3.5" "4.0" "4.5" "5.0"],
:categorical? false},
:row 0}],
:width 600,
:height 400,
:caption nil,
:total-width 600.0,
:legend-position :none,
:layout-type :single,
:layout
{:subtitle-pad 0,
:legend-w 0,
:caption-pad 0,
:y-label-pad 42.5,
:legend-h 0.0,
:title-pad 0,
:strip-h 0,
:x-label-pad 38,
:strip-w 0.0},
:grid {:rows 1, :cols 1},
:legend nil,
:panel-height 362.0,
:title nil,
:y-label "y",
:alpha-legend nil,
:x-label "x",
:subtitle nil,
:panel-width 557.5,
:size-legend nil,
:total-height 400.0,
:tooltip nil,
:margin 10}svg-summary
[svg-or-pose]
[svg-or-pose theme]
Extract structural summary from SVG hiccup for testing. Returns a map with :width, :height, :panels, :points, :lines, :polygons, :tiles, :visible-tiles, and :texts – useful for asserting plot structure. Accepts SVG hiccup or a pose (auto-renders to SVG first). (svg-summary (plot fr)) – summary of rendered SVG (svg-summary my-pose) – auto-renders pose (leaf or composite)
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width {:color :species}) pj/svg-summary){:sizes #{3.0},
:texts
["sepal width"
"sepal length"
"species"
"setosa"
"versicolor"
"virginica"
"4.5"
"5.0"
"5.5"
"6.0"
"6.5"
"7.0"
"7.5"
"8.0"
"2.0"
"2.5"
"3.0"
"3.5"
"4.0"
"4.5"],
:width 600.0,
:lines 0,
:colors #{"none" "rgb(228,26,28)" "rgb(55,126,184)" "rgb(77,175,74)"},
:points 150,
:alphas #{0.75},
:tiles 0,
:polygons 0,
:visible-tiles 0,
:panels 1,
:height 400.0,
:shapes #{:rect}}valid-plan?
[plan]
Check if a plan conforms to the Malli schema. (valid-plan? (plan pose)) – true if valid
(pj/valid-plan? plan1)trueexplain-plan
[plan]
Explain why a plan does not conform to the Malli schema. Returns nil if valid, or a Malli explanation map if invalid. (explain-plan (plan pose))
(pj/explain-plan plan1)nilPipeline
plan->membrane
[plan-data]
[plan-data opts]
Convert a plan into a membrane drawable tree. The 1-arity uses no rendering options. The 2-arity takes an opts map with optional :tooltip, :theme, :palette, etc. (plan->membrane (plan fr)) (plan->membrane (plan fr) {:tooltip true})
(def m1 (pj/plan->membrane plan1))(vector? m1)truemembrane->plot
[membrane-tree format opts]
Convert a membrane drawable tree into a figure for the given format. Dispatches on format keyword; :svg is always available. (membrane->plot (plan->membrane (plan pose)) :svg {})
(first (pj/membrane->plot m1 :svg
{:total-width (:total-width plan1)
:total-height (:total-height plan1)})):svgplan->plot
[plan format opts]
Convert a plan into a figure for the given format. Dispatches on format keyword. Each renderer is a separate namespace that registers a defmethod; :svg is always available. (plan->plot (plan fr) :svg {}) (plan->plot (plan fr) :plotly {})
(first (pj/plan->plot plan1 :svg {})):svgConfiguration
config
[]
Return the effective resolved configuration as a map. Merges: library defaults < plotje.edn < set-config! < config. Useful for inspecting which values are in effect. (config) – show current resolved config
(pj/config){:strict false,
:margin-multi 10,
:validate true,
:point-stroke "none",
:title-offset 18,
:tick-spacing-y 40,
:panel-size 200,
:min-panel-size 20,
:label-offset 38,
:label-font-size 13,
:default-color "#333",
:width 600,
:legend-header-pad 20,
:point-stroke-width 0,
:annotation-dash [4 3],
:legend-width 100,
:legend-entry-height 18,
:theme {:bg "#E8E8E8", :grid "#F5F5F5", :font-size 11},
:bin-method :sturges,
:domain-padding 0.05,
:strip-height 16,
:point-opacity 0.75,
:line-width 2.5,
:grid-stroke-width 0.6,
:tick-spacing-x 60,
:strip-font-size 10,
:title-font-size 15,
:band-opacity 0.15,
:bar-opacity 0.85,
:annotation-stroke "#333",
:height 400,
:margin 10,
:point-radius 3.0}set-config!
[m]
Set global config overrides. Persists across calls until reset. (set-config! {:palette :dark2 :theme {:bg “#FFFFFF”}}) (set-config! nil) – reset to defaults
with-config
[config-map & body]
Execute body with thread-local config overrides. Overrides take precedence over set-config! and defaults, but plot options still win. (with-config {:theme {:bg “#FFF”}} (plot …))
(pj/with-config {:palette :pastel1}
(:palette (pj/config))):pastel1Documentation Metadata
Three maps document the option keys at each scope level.
config-key-docs
Documentation metadata for configuration keys. Maps each config key to [category description]. Use with (pj/config) to build reference tables.
(count pj/config-key-docs)37plot-option-docs
Documentation for plot-level option keys. These are accepted by pj/options, pj/plan, and pj/plot but are inherently per-plot (text content or nested config override). Maps each key to [category description].
(count pj/plot-option-docs)14layer-option-docs
Documentation for layer option keys accepted by lay- functions. Maps each key to a description string.
(count pj/layer-option-docs)29Layer Type Registry
layer-type-lookup
[k]
Look up a registered layer type by keyword. Returns the layer-type map (with :mark, :stat, :position, :doc), or nil if not found. (layer-type-lookup :histogram) => {:mark :bar, :stat :bin, …}
(pj/layer-type-lookup :smooth){:mark :line,
:stat :loess,
:accepts
[:confidence-band
:bootstrap-resamples
:bandwidth
:size
:nudge-x
:nudge-y],
:doc
"Smoothed trend line — defaults to LOESS; pass {:stat :linear-model} for OLS."}registered-layer-types
[]
Return all registered layer types as a map of keyword -> layer-type map. Useful for generating documentation tables.
(count (pj/registered-layer-types))26(first (pj/registered-layer-types))[:smooth
{:mark :line,
:stat :loess,
:accepts
[:confidence-band
:bootstrap-resamples
:bandwidth
:size
:nudge-x
:nudge-y],
:doc
"Smoothed trend line — defaults to LOESS; pass {:stat :linear-model} for OLS."}]Documentation Helpers
Query the self-documenting dispatch tables for any extensible concept.
stat-doc
[k]
Return the prose description for a stat keyword. Returns “(no description)” if no [:key :doc] defmethod is registered. (stat-doc :bin) => “Bin numerical values into ranges”
(pj/stat-doc :linear-model)"Linear model — OLS regression line + optional confidence band"mark-doc
[k]
Return the prose description for a mark keyword. Returns “(no description)” if no [:key :doc] defmethod is registered. (mark-doc :point) => “Filled circle”
(pj/mark-doc :point)"Filled circle"position-doc
[k]
Return the prose description for a position keyword. Returns “(no description)” if no [:key :doc] defmethod is registered. (position-doc :dodge) => “Shift groups side-by-side within a band”
(pj/position-doc :dodge)"Shift groups side-by-side within a band"scale-doc
[k]
Return the prose description for a scale keyword. Returns “(no description)” if no [:key :doc] defmethod is registered. (scale-doc :linear) => “Continuous linear mapping”
(pj/scale-doc :linear)"Continuous linear mapping"coord-doc
[k]
Return the prose description for a coordinate type keyword. Returns “(no description)” if no [:key :doc] defmethod is registered. (coord-doc :polar) => “Radial mapping: x->angle, y->radius”
(pj/coord-doc :cartesian)"Standard x-right, y-up mapping"membrane-mark-doc
[k]
Return the prose description for how a mark renders to membrane drawables. Returns “(no description)” if no [:key :doc] defmethod is registered. (membrane-mark-doc :point) => “Translated colored rounded-rectangles”
(pj/membrane-mark-doc :point)"Translated colored rounded-rectangles"Export
save
[pose path]
[pose path opts]
Save a plot to a file. Format resolution, in precedence order: 1. :format in the pose’s :opts (or the 3-arity opts map) wins. 2. Otherwise inferred from the path extension (.svg -> :svg, .png -> :bufimg). 3. Default :svg.
When the resolved format and the path extension disagree, prints a warning – the file still gets the bytes the resolved format produces, but the extension is misleading.
pose – a pose. path – file path (string or java.io.File). opts – same options as plot (:format, :width, :height, :title, …). Tooltip and brush interactivity are not included in saved files. Returns the path. (save my-pose “plot.svg”) ;; SVG (save my-pose “plot.png”) ;; inferred PNG (save my-pose “plot.svg” {:format :bufimg}) ;; opts override (warns)
Save a plot to an SVG file:
(let [path (str (java.io.File/createTempFile "plotje-example" ".svg"))]
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width {:color :species})
(pj/save path {:title "Iris Export"}))
(.contains (slurp path) "<svg"))truesave-png
[pose path]
[pose path opts]
Save a plot as a PNG file. Convenience wrapper around pj/save that pins :format :bufimg. Equivalent to: (pj/save pose path (assoc opts :format :bufimg)) pose – a pose. path – file path (string or java.io.File). opts – same options as save (:width, :height, :title, :theme, …). Returns the path. (save-png my-pose “plot.png”) (save-png my-pose “plot.png” {:width 800 :height 600})
Save a plot to a PNG file via membrane’s Java2D backend. Returns the path:
(let [path (str (java.io.File/createTempFile "plotje-example" ".png"))]
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width {:color :species})
(pj/save-png path))
(.exists (java.io.File. ^String path)))true