28 Exploring Plans
The Architecture chapter introduced the five-stage pipeline that turns a pose into a rendered plot. This notebook zooms into one of those stages β the plan β walking through it step by step to build intuition for what pj/plan produces for different poses.
You would explore plans when:
Debugging β a plot looks wrong and you want to see what data the renderer received (domains, groups, tick values)
Building a custom renderer β you need to understand the plan structure to consume it
Validating plans β you want to assert plan properties in tests
(ns plotje-book.exploring-plans
(:require
;; Rdatasets -- standard datasets
[scicloj.metamorph.ml.rdatasets :as rdatasets]
;; Kindly -- notebook rendering protocol
[scicloj.kindly.v4.kind :as kind]
;; Plotje -- composable plotting
[scicloj.plotje.api :as pj]
;; Layer-type registry -- lookup mark/stat/position by keyword
[scicloj.plotje.layer-type :as layer-type]))A Minimal Scatter Plot
Letβs start with the simplest possible plot: 5 points, no color, no title.
(def tiny {:x [1 2 3 4 5]
:y [2 4 1 5 3]})Here is the rendered plot:
(-> tiny
(pj/lay-point :x :y))And here is the plan β the data structure that drives the rendering. Weβll use pj/plan with the same pose:
(def tiny-plan (-> tiny
(pj/lay-point :x :y)
pj/plan))Whatβs in a plan?
At the top level, a plan describes dimensions and layout. Here is the entire plan β a plain Clojure map:
tiny-plan{: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}Notice:
- Dimensions are 600x400 with a 10-pixel margin
- Labels
"x"and"y"are inferred from column names - No legend (we didnβt map a column to color)
- One panel with
:x-domain,:y-domain, ticks, and layers
The panel
The plan contains one or more panels. A simple plot has one panel; faceting and SPLOM (scatter plot matrix) produce multiple. Each panel holds its own data space:
(def tiny-panel (first (:panels tiny-plan)))(keys tiny-panel)(:coord
:y-domain
:x-scale
:x-domain
:x-ticks
:col
:layers
:y-scale
:y-ticks
:row)Domains β the numeric range of the data, with a small padding:
(:x-domain tiny-panel)[0.8 5.2](:y-domain tiny-panel)[0.8 5.2]Scale specs β what kind of scale to use:
(:x-scale tiny-panel){:type :linear}Ticks β pre-computed tick positions and their text labels:
(:x-ticks tiny-panel){: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}These are the actual numbers that will appear on the axis. They are in data space β not pixel positions.
The layer
Each layer in the pose produces one plan-level layer entry. Our scatter has a single point layer:
(def tiny-layer (first (:layers tiny-panel)))tiny-layer{: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]}The style gives rendering hints (opacity, radius) but the geometry is in the groups. Without a color mapping, there is one group:
(count (:groups tiny-layer))1The group contains the actual data β x/y coordinates in data space, plus a resolved RGBA color:
(first (:groups tiny-layer)){: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]}These are the original data values β not pixel positions. The renderer maps them through scales to get pixel coordinates.
In other words, the plan describes geometry in data space.
Adding Color
When we map a column to color, the plan splits data into groups and adds a legend.
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width {:color :species}))(def iris-plan (-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width {:color :species})
pj/plan))Here is the full plan β notice the legend and three groups:
iris-plan{:panels
[{:coord :cartesian,
:y-domain [1.88 4.5200000000000005],
:x-scale {:type :linear},
:x-domain [4.12 8.08],
:x-ticks
{:values [4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0],
:labels ["4.5" "5.0" "5.5" "6.0" "6.5" "7.0" "7.5" "8.0"],
:categorical? false},
:col 0,
:layers
[{:mark :point,
:style {:opacity 0.75, :radius 3.0},
:size-scale nil,
:alpha-scale nil,
:groups
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
1.0],
:xs #tech.v3.dataset.column<float64>[50]
:sepal-length
[5.100, 4.900, 4.700, 4.600, 5.000, 5.400, 4.600, 5.000, 4.400, 4.900, 5.400, 4.800, 4.800, 4.300, 5.800, 5.700, 5.400, 5.100, 5.700, 5.100...],
:ys #tech.v3.dataset.column<float64>[50]
:sepal-width
[3.500, 3.000, 3.200, 3.100, 3.600, 3.900, 3.400, 3.400, 2.900, 3.100, 3.700, 3.400, 3.000, 3.000, 4.000, 4.400, 3.900, 3.500, 3.800, 3.800...],
:label "setosa",
:row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19...]}
{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
1.0],
:xs #tech.v3.dataset.column<float64>[50]
:sepal-length
[7.000, 6.400, 6.900, 5.500, 6.500, 5.700, 6.300, 4.900, 6.600, 5.200, 5.000, 5.900, 6.000, 6.100, 5.600, 6.700, 5.600, 5.800, 6.200, 5.600...],
:ys #tech.v3.dataset.column<float64>[50]
:sepal-width
[3.200, 3.200, 3.100, 2.300, 2.800, 2.800, 3.300, 2.400, 2.900, 2.700, 2.000, 3.000, 2.200, 2.900, 2.900, 3.100, 3.000, 2.700, 2.200, 2.500...],
:label "versicolor",
:row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69...]}
{:color
[0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0],
:xs #tech.v3.dataset.column<float64>[50]
:sepal-length
[6.300, 5.800, 7.100, 6.300, 6.500, 7.600, 4.900, 7.300, 6.700, 7.200, 6.500, 6.400, 6.800, 5.700, 5.800, 6.400, 6.500, 7.700, 7.700, 6.000...],
:ys #tech.v3.dataset.column<float64>[50]
:sepal-width
[3.300, 2.700, 3.000, 2.900, 3.000, 3.000, 2.500, 2.900, 2.500, 3.600, 3.200, 2.700, 3.000, 2.500, 2.800, 3.200, 3.000, 3.800, 2.600, 2.200...],
:label "virginica",
:row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119...]}],
:y-domain [2.0 4.4],
:x-domain [4.3 7.9]}],
:y-scale {:type :linear},
:y-ticks
{:values [2.0 2.5 3.0 3.5 4.0 4.5],
:labels ["2.0" "2.5" "3.0" "3.5" "4.0" "4.5"],
:categorical? false},
:row 0}],
:width 600,
:height 400,
:caption nil,
:total-width 600.0,
:legend-position :right,
:layout-type :single,
:layout
{:subtitle-pad 0,
:legend-w 102,
: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
{:title :species,
:entries
[{:label "setosa",
:color
[0.8941176470588236 0.10196078431372549 0.10980392156862745 1.0]}
{:label "versicolor",
:color
[0.21568627450980393 0.49411764705882355 0.7215686274509804 1.0]}
{:label "virginica",
:color
[0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0]}]},
:panel-height 362.0,
:title nil,
:y-label "sepal width",
:alpha-legend nil,
:x-label "sepal length",
:subtitle nil,
:panel-width 455.5,
:size-legend nil,
:total-height 400.0,
:tooltip nil,
:margin 10}Now we have three groups β one per species:
(def iris-layer (first (:layers (first (:panels iris-plan)))))(count (:groups iris-layer))3Each group has its own resolved color and a subset of the data:
(mapv (fn [g]
{:color (:color g)
:n-points (count (:xs g))})
(:groups iris-layer))[{:color
[0.8941176470588236 0.10196078431372549 0.10980392156862745 1.0],
:n-points 50}
{:color
[0.21568627450980393 0.49411764705882355 0.7215686274509804 1.0],
:n-points 50}
{:color
[0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0],
:n-points 50}]The legend describes the color mapping:
(:legend iris-plan){:title :species,
:entries
[{:label "setosa",
:color
[0.8941176470588236 0.10196078431372549 0.10980392156862745 1.0]}
{:label "versicolor",
:color
[0.21568627450980393 0.49411764705882355 0.7215686274509804 1.0]}
{:label "virginica",
:color
[0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0]}]}Colors are resolved to [r g b a] vectors β no symbolic references. The same color appears in both the layer groups and the legend entries.
Continuous Color
When :color maps to a numeric column, the plan stores per-point colors and a continuous gradient legend.
(def cont-plan (-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width {:color :petal-length})
pj/plan))(:legend cont-plan){:title :petal-length,
:type :continuous,
:min 1.0,
:max 6.9,
:color-scale nil,
:stops
[{:t 0.0,
:color
[0.07450980392156863 0.16862745098039217 0.2627450980392157 1.0]}
{:t 0.05263157894736842,
:color
[0.08833849329205366 0.19628482972136224 0.2998968008255934 1.0]}
{:t 0.10526315789473684,
:color
[0.1021671826625387 0.22394220846233232 0.3370485036119711 1.0]}
{:t 0.15789473684210525,
:color
[0.11599587203302374 0.2515995872033024 0.3742002063983488 1.0]}
{:t 0.21052631578947367,
:color
[0.1298245614035088 0.27925696594427246 0.4113519091847265 1.0]}
{:t 0.2631578947368421,
:color
[0.14365325077399382 0.30691434468524253 0.4485036119711042 1.0]}
{:t 0.3157894736842105,
:color
[0.15748194014447883 0.33457172342621255 0.4856553147574819 1.0]}
{:t 0.3684210526315789,
:color
[0.17131062951496387 0.3622291021671826 0.5228070175438597 1.0]}
{:t 0.42105263157894735,
:color
[0.1851393188854489 0.3898864809081527 0.5599587203302373 1.0]}
{:t 0.47368421052631576,
:color
[0.19896800825593394 0.41754385964912283 0.597110423116615 1.0]}
{:t 0.5263157894736842,
:color
[0.21279669762641898 0.4452012383900929 0.6342621259029928 1.0]}
{:t 0.5789473684210527,
:color
[0.22662538699690402 0.472858617131063 0.6714138286893705 1.0]}
{:t 0.631578947368421,
:color
[0.24045407636738905 0.500515995872033 0.7085655314757482 1.0]}
{:t 0.6842105263157895,
:color
[0.25428276573787406 0.5281733746130031 0.7457172342621259 1.0]}
{:t 0.7368421052631579,
:color
[0.2681114551083591 0.5558307533539731 0.7828689370485036 1.0]}
{:t 0.7894736842105263,
:color
[0.28194014447884413 0.5834881320949432 0.8200206398348814 1.0]}
{:t 0.8421052631578947,
:color
[0.29576883384932917 0.6111455108359133 0.857172342621259 1.0]}
{:t 0.8947368421052632,
:color
[0.3095975232198142 0.6388028895768834 0.8943240454076368 1.0]}
{:t 0.9473684210526315,
:color
[0.3234262125902993 0.6664602683178534 0.9314757481940144 1.0]}
{:t 1.0,
:color
[0.33725490196078434 0.6941176470588235 0.9686274509803922 1.0]}]}The legend has pre-computed gradient stops β no functions:
(select-keys (:legend cont-plan) [:title :type :min :max :color-scale]){:title :petal-length,
:type :continuous,
:min 1.0,
:max 6.9,
:color-scale nil}Twenty evenly spaced stops store the gradient colors:
(count (:stops (:legend cont-plan)))20Histograms
A histogram computes bins from the data. The plan stores the bin edges and counts β still in data space.
(-> (rdatasets/datasets-iris)
(pj/lay-histogram :sepal-length))(def hist-plan (-> (rdatasets/datasets-iris)
(pj/lay-histogram :sepal-length)
pj/plan))hist-plan{:panels
[{:coord :cartesian,
:y-domain [-1.4000000000000001 29.4],
:x-scale {:type :linear},
:x-domain [4.12 8.08],
:x-ticks
{:values [4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0],
:labels ["4.5" "5.0" "5.5" "6.0" "6.5" "7.0" "7.5" "8.0"],
:categorical? false},
:col 0,
:layers
[{:mark :bar,
:style {:opacity 0.85},
:groups
[{:color [0.2 0.2 0.2 1.0],
:bars
[{:lo 4.3, :hi 4.7, :count 9}
{:lo 4.7, :hi 5.1, :count 23}
{:lo 5.1, :hi 5.5, :count 20}
{:lo 5.5, :hi 5.9, :count 28}
{:lo 5.9, :hi 6.300000000000001, :count 28}
{:lo 6.300000000000001, :hi 6.7, :count 14}
{:lo 6.7, :hi 7.1, :count 16}
{:lo 7.1, :hi 7.5, :count 6}
{:lo 7.5, :hi 7.9, :count 6}]}],
:y-domain [0 28],
:x-domain [4.3 7.9]}],
:y-scale {:type :linear},
:y-ticks
{:values [0.0 5.0 10.0 15.0 20.0 25.0],
:labels ["0" "5" "10" "15" "20" "25"],
: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 11.0,
: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 nil,
:alpha-legend nil,
:x-label "sepal length",
:subtitle nil,
:panel-width 589.0,
:size-legend nil,
:total-height 400.0,
:tooltip nil,
:margin 10}(def hist-layer (first (:layers (first (:panels hist-plan)))))(:mark hist-layer):barThe geometry is in :bars β each bin has a lo edge, hi edge, and count:
(let [g (first (:groups hist-layer))]
(:bars g))[{:lo 4.3, :hi 4.7, :count 9}
{:lo 4.7, :hi 5.1, :count 23}
{:lo 5.1, :hi 5.5, :count 20}
{:lo 5.5, :hi 5.9, :count 28}
{:lo 5.9, :hi 6.300000000000001, :count 28}
{:lo 6.300000000000001, :hi 6.7, :count 14}
{:lo 6.7, :hi 7.1, :count 16}
{:lo 7.1, :hi 7.5, :count 6}
{:lo 7.5, :hi 7.9, :count 6}]The renderer will draw a rectangle from (lo, 0) to (hi, count) in data space, then map through scales to pixels.
Categorical Bars
A bar chart counts occurrences of each category. The plan records the categories and counts per group.
(-> (rdatasets/palmerpenguins-penguins)
(pj/lay-bar :island {:color :species}))(def bar-plan (-> (rdatasets/palmerpenguins-penguins)
(pj/lay-bar :island {:color :species})
pj/plan))(def bar-layer (first (:layers (first (:panels bar-plan)))))The mark type is :rect and the layer knows the categories:
bar-layer{:mark :rect,
:style {:opacity 0.85},
:position :dodge,
:categories ["Torgersen" "Biscoe" "Dream"],
:groups
[{:color
[0.8941176470588236 0.10196078431372549 0.10980392156862745 1.0],
:label "Adelie",
:counts
[{:category "Torgersen", :count 52}
{:category "Biscoe", :count 44}
{:category "Dream", :count 56}],
:dodge-idx 0}
{:color
[0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0],
:label "Chinstrap",
:counts
[{:category "Torgersen", :count 0}
{:category "Biscoe", :count 0}
{:category "Dream", :count 68}],
:dodge-idx 1}
{:color
[0.21568627450980393 0.49411764705882355 0.7215686274509804 1.0],
:label "Gentoo",
:counts
[{:category "Torgersen", :count 0}
{:category "Biscoe", :count 124}
{:category "Dream", :count 0}],
:dodge-idx 2}],
:y-domain [0 124],
:x-domain ("Torgersen" "Biscoe" "Dream"),
:dodge-ctx {:n-groups 3}}Each group (one per color) has counts for every category:
(mapv (fn [g]
{:label (:label g)
:counts (:counts g)})
(:groups bar-layer))[{:label "Adelie",
:counts
[{:category "Torgersen", :count 52}
{:category "Biscoe", :count 44}
{:category "Dream", :count 56}]}
{:label "Chinstrap",
:counts
[{:category "Torgersen", :count 0}
{:category "Biscoe", :count 0}
{:category "Dream", :count 68}]}
{:label "Gentoo",
:counts
[{:category "Torgersen", :count 0}
{:category "Biscoe", :count 124}
{:category "Dream", :count 0}]}]The :position field (:dodge or :stack) tells the renderer how to arrange multiple groups within each category.
Stacked Bars
Stacking changes the position field:
(def stacked-plan (-> (rdatasets/palmerpenguins-penguins)
(pj/lay-bar :island {:position :stack :color :species})
pj/plan))(def stacked-layer (first (:layers (first (:panels stacked-plan)))))(:position stacked-layer):stackThe counts are the same β only the rendering instruction differs. The plan describes what to draw; the renderer decides how.
Regression Lines
A regression produces line segments in data space.
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width)
(pj/lay-smooth {:stat :linear-model}))(def lm-plan (-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width)
(pj/lay-smooth {:stat :linear-model})
pj/plan))Two layers β points and line:
(mapv :mark (:layers (first (:panels lm-plan))))[:point :line](def lm-layer (second (:layers (first (:panels lm-plan)))))Its group has endpoints β a line segment in data space:
(first (:groups lm-layer)){:color [0.2 0.2 0.2 1.0],
:label "",
:x1 4.3,
:y1 3.1528422048579974,
:x2 7.9,
:y2 2.930056932187078}The renderer maps these two points through scales to get a rendered line segment.
Per-Group Regression
When both points and regression have a color mapping, the line layer gets one segment per group:
(-> (rdatasets/datasets-iris)
(pj/pose :petal-length :petal-width {:color :species})
pj/lay-point
(pj/lay-smooth {:stat :linear-model}))(def grp-plan (-> (rdatasets/datasets-iris)
(pj/pose :petal-length :petal-width {:color :species})
pj/lay-point
(pj/lay-smooth {:stat :linear-model})
pj/plan))(let [line-layer (second (:layers (first (:panels grp-plan))))]
(mapv (fn [g]
{:color (:color g)
:x1 (some-> (:x1 g) (Math/round) int)
:x2 (some-> (:x2 g) (Math/round) int)})
(:groups line-layer)))[{:color
[0.8941176470588236 0.10196078431372549 0.10980392156862745 1.0],
:x1 1,
:x2 2}
{:color
[0.21568627450980393 0.49411764705882355 0.7215686274509804 1.0],
:x1 3,
:x2 5}
{:color
[0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0],
:x1 5,
:x2 7}]Three line segments, each with its own color β one per species.
Connected Lines (Polylines)
Line marks from identity data (not regression) store xs/ys vectors:
(def wave {:x (range 30)
:y (map #(Math/sin (* % 0.3)) (range 30))})(-> wave
(pj/lay-line :x :y))(def wave-plan (-> wave
(pj/lay-line :x :y)
pj/plan))(def wave-group (first (:groups (first (:layers (first (:panels wave-plan))))))){:n-points (count (:xs wave-group))
:first-x (first (:xs wave-group))
:last-x (last (:xs wave-group))}{:n-points 30, :first-x 0, :last-x 29}The renderer connects these points in order to draw a polyline.
Value Bars
Value bars map categorical x to numeric y without any counting. The plan stores the raw x/y pairs:
(def sales {:product [:widget :gadget :gizmo :doohickey]
:revenue [120 340 210 95]})(-> sales
(pj/lay-value-bar :product :revenue))(def sales-plan (-> sales
(pj/lay-value-bar :product :revenue)
pj/plan))(let [g (first (:groups (first (:layers (first (:panels sales-plan))))))]
{:xs (:xs g)
:ys (:ys g)}){:xs #tech.v3.dataset.column<string>[4]
:product
[widget, gadget, gizmo, doohickey],
:ys #tech.v3.dataset.column<int64>[4]
:revenue
[120, 340, 210, 95]}Flipped Coordinates
Setting :coord :flip swaps x and y in the planβs panel:
(def flip-plan (-> (rdatasets/datasets-iris)
(pj/lay-bar :species)
(pj/coord :flip)
pj/plan))(:coord (first (:panels flip-plan))):flipThe domains are swapped β the categorical axis is now y:
(let [p (first (:panels flip-plan))]
{:x-domain-type (if (number? (first (:x-domain p))) :numeric :categorical)
:y-domain-type (if (number? (first (:y-domain p))) :numeric :categorical)}){:x-domain-type :numeric, :y-domain-type :categorical}The layer data is unchanged β the coord type tells the renderer to swap axes during mapping.
Options Affect the Plan
Title, labels, and dimensions are recorded in the plan:
(def opts-plan (-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width)
(pj/plan {:title "My Custom Title"
:x-label "Length (cm)"
:y-label "Width (cm)"
:width 800
:height 300})))opts-plan{:panels
[{:coord :cartesian,
:y-domain [1.88 4.5200000000000005],
:x-scale {:type :linear},
:x-domain [4.12 8.08],
:x-ticks
{:values [4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0],
:labels ["4.5" "5.0" "5.5" "6.0" "6.5" "7.0" "7.5" "8.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<float64>[150]
:sepal-length
[5.100, 4.900, 4.700, 4.600, 5.000, 5.400, 4.600, 5.000, 4.400, 4.900, 5.400, 4.800, 4.800, 4.300, 5.800, 5.700, 5.400, 5.100, 5.700, 5.100...],
:ys #tech.v3.dataset.column<float64>[150]
:sepal-width
[3.500, 3.000, 3.200, 3.100, 3.600, 3.900, 3.400, 3.400, 2.900, 3.100, 3.700, 3.400, 3.000, 3.000, 4.000, 4.400, 3.900, 3.500, 3.800, 3.800...],
:row-indices #tech.v3.dataset.column<int64>[150]
:__row-idx
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19...]}],
:y-domain [2.0 4.4],
:x-domain [4.3 7.9]}],
:y-scale {:type :linear},
:y-ticks
{:values [2.0 2.5 3.0 3.5 4.0 4.5],
:labels ["2.0" "2.5" "3.0" "3.5" "4.0" "4.5"],
:categorical? false},
:row 0}],
:width 800,
:height 300,
:caption nil,
:total-width 800.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 33,
:strip-h 0,
:x-label-pad 38,
:strip-w 0.0},
:grid {:rows 1, :cols 1},
:legend nil,
:panel-height 229.0,
:title "My Custom Title",
:y-label "Width (cm)",
:alpha-legend nil,
:x-label "Length (cm)",
:subtitle nil,
:panel-width 757.5,
:size-legend nil,
:total-height 300.0,
:tooltip nil,
:margin 10}The layout records how much space to reserve for each label:
(:layout opts-plan){:subtitle-pad 0,
:legend-w 0,
:caption-pad 0,
:y-label-pad 42.5,
:legend-h 0.0,
:title-pad 33,
:strip-h 0,
:x-label-pad 38,
:strip-w 0.0}Plan vs Plot β Side by Side
pj/plan and pj/plot accept the same pose. pj/plan returns the intermediate data map; pj/plot returns the final SVG.
The plan (a plain Clojure map):
(def final-pose
(-> (rdatasets/datasets-iris)
(pj/pose :petal-length :petal-width {:color :species})
pj/lay-point
(pj/lay-smooth {:stat :linear-model})))(def final-plan (pj/plan final-pose {:title "Iris Petals"}))final-plan{:panels
[{:coord :cartesian,
:y-domain [-0.01999999999999999 2.62],
:x-scale {:type :linear},
:x-domain [0.705 7.195],
:x-ticks
{:values [1.0 2.0 3.0 4.0 5.0 6.0 7.0],
:labels ["1" "2" "3" "4" "5" "6" "7"],
:categorical? false},
:col 0,
:layers
[{:mark :point,
:style {:opacity 0.75, :radius 3.0},
:size-scale nil,
:alpha-scale nil,
:groups
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
1.0],
:xs #tech.v3.dataset.column<float64>[50]
:petal-length
[1.400, 1.400, 1.300, 1.500, 1.400, 1.700, 1.400, 1.500, 1.400, 1.500, 1.500, 1.600, 1.400, 1.100, 1.200, 1.500, 1.300, 1.400, 1.700, 1.500...],
:ys #tech.v3.dataset.column<float64>[50]
:petal-width
[0.2000, 0.2000, 0.2000, 0.2000, 0.2000, 0.4000, 0.3000, 0.2000, 0.2000, 0.1000, 0.2000, 0.2000, 0.1000, 0.1000, 0.2000, 0.4000, 0.4000, 0.3000, 0.3000, 0.3000...],
:label "setosa",
:row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19...]}
{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
1.0],
:xs #tech.v3.dataset.column<float64>[50]
:petal-length
[4.700, 4.500, 4.900, 4.000, 4.600, 4.500, 4.700, 3.300, 4.600, 3.900, 3.500, 4.200, 4.000, 4.700, 3.600, 4.400, 4.500, 4.100, 4.500, 3.900...],
:ys #tech.v3.dataset.column<float64>[50]
:petal-width
[1.400, 1.500, 1.500, 1.300, 1.500, 1.300, 1.600, 1.000, 1.300, 1.400, 1.000, 1.500, 1.000, 1.400, 1.300, 1.400, 1.500, 1.000, 1.500, 1.100...],
:label "versicolor",
:row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69...]}
{:color
[0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0],
:xs #tech.v3.dataset.column<float64>[50]
:petal-length
[6.000, 5.100, 5.900, 5.600, 5.800, 6.600, 4.500, 6.300, 5.800, 6.100, 5.100, 5.300, 5.500, 5.000, 5.100, 5.300, 5.500, 6.700, 6.900, 5.000...],
:ys #tech.v3.dataset.column<float64>[50]
:petal-width
[2.500, 1.900, 2.100, 1.800, 2.200, 2.100, 1.700, 1.800, 1.800, 2.500, 2.000, 1.900, 2.100, 2.000, 2.400, 2.300, 1.800, 2.200, 2.300, 1.500...],
:label "virginica",
:row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119...]}],
:y-domain [0.1 2.5],
:x-domain [1.0 6.9]}
{:mark :line,
:style {:stroke-width 2.5, :opacity 1.0},
:groups
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
1.0],
:label "setosa",
:x1 1.0,
:y1 0.15302476654486402,
:x2 1.9,
:y2 0.33414535119772626}
{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
1.0],
:label "versicolor",
:x1 3.0,
:y1 0.9088724584103528,
:x2 5.1,
:y2 1.6040850277264331}
{:color
[0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0],
:label "virginica",
:x1 4.5,
:y1 1.8573676029159527,
:x2 6.9,
:y2 2.2420802958833654}],
:y-domain [0.1 2.5],
:x-domain [1.0 6.9]}],
:y-scale {:type :linear},
:y-ticks
{:values [-0.0 0.5 1.0 1.5 2.0 2.5],
:labels ["0.0" "0.5" "1.0" "1.5" "2.0" "2.5"],
:categorical? false},
:row 0}],
:width 600,
:height 400,
:caption nil,
:total-width 600.0,
:legend-position :right,
:layout-type :single,
:layout
{:subtitle-pad 0,
:legend-w 102,
:caption-pad 0,
:y-label-pad 42.5,
:legend-h 0.0,
:title-pad 33,
:strip-h 0,
:x-label-pad 38,
:strip-w 0.0},
:grid {:rows 1, :cols 1},
:legend
{:title :species,
:entries
[{:label "setosa",
:color
[0.8941176470588236 0.10196078431372549 0.10980392156862745 1.0]}
{:label "versicolor",
:color
[0.21568627450980393 0.49411764705882355 0.7215686274509804 1.0]}
{:label "virginica",
:color
[0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0]}]},
:panel-height 329.0,
:title "Iris Petals",
:y-label "petal width",
:alpha-legend nil,
:x-label "petal length",
:subtitle nil,
:panel-width 455.5,
:size-legend nil,
:total-height 400.0,
:tooltip nil,
:margin 10}Layer summary:
(mapv (fn [l]
{:mark (:mark l)
:n-groups (count (:groups l))})
(:layers (first (:panels final-plan))))[{:mark :point, :n-groups 3} {:mark :line, :n-groups 3}]The rendered plot (SVG):
(-> final-pose (pj/options {:title "Iris Petals"}))Multi-Panel Plans
Faceting produces plans with multiple panels. Each panel has its own domains, ticks, and layers, plus grid positioning.
(def faceted-plan
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width {:color :species})
(pj/facet :species)
pj/plan))The grid tells us the layout:
(:grid faceted-plan){:rows 1, :cols 3}Three panels β one per species:
(count (:panels faceted-plan))3Each panel has a grid position and strip label:
(:panels faceted-plan)[{:coord :cartesian,
:y-domain [1.93 4.505000000000001],
:col-label "setosa",
:x-scale {:type :linear},
:x-domain [4.225 8.05],
:x-ticks {:values [6.0 8.0], :labels ["6" "8"], :categorical? false},
:col 0,
:layers
[{:mark :point,
:style {:opacity 0.75, :radius 3.0},
:size-scale nil,
:alpha-scale nil,
:groups
[{:color
[0.8941176470588236 0.10196078431372549 0.10980392156862745 1.0],
:xs #tech.v3.dataset.column<float64>[50]
:sepal-length
[5.100, 4.900, 4.700, 4.600, 5.000, 5.400, 4.600, 5.000, 4.400, 4.900, 5.400, 4.800, 4.800, 4.300, 5.800, 5.700, 5.400, 5.100, 5.700, 5.100...],
:ys #tech.v3.dataset.column<float64>[50]
:sepal-width
[3.500, 3.000, 3.200, 3.100, 3.600, 3.900, 3.400, 3.400, 2.900, 3.100, 3.700, 3.400, 3.000, 3.000, 4.000, 4.400, 3.900, 3.500, 3.800, 3.800...],
:label "setosa",
:row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19...]}],
:y-domain [2.3 4.4],
:x-domain [4.3 5.8]}],
:y-scale {:type :linear},
:y-ticks
{:values [2.0 2.5 3.0 3.5 4.0 4.5],
:labels ["2.0" "2.5" "3.0" "3.5" "4.0" "4.5"],
:categorical? false},
:row 0}
{:coord :cartesian,
:y-domain [1.93 4.505000000000001],
:col-label "versicolor",
:x-scale {:type :linear},
:x-domain [4.225 8.05],
:x-ticks {:values [6.0 8.0], :labels ["6" "8"], :categorical? false},
:col 1,
:layers
[{:mark :point,
:style {:opacity 0.75, :radius 3.0},
:size-scale nil,
:alpha-scale nil,
:groups
[{:color
[0.21568627450980393 0.49411764705882355 0.7215686274509804 1.0],
:xs #tech.v3.dataset.column<float64>[50]
:sepal-length
[7.000, 6.400, 6.900, 5.500, 6.500, 5.700, 6.300, 4.900, 6.600, 5.200, 5.000, 5.900, 6.000, 6.100, 5.600, 6.700, 5.600, 5.800, 6.200, 5.600...],
:ys #tech.v3.dataset.column<float64>[50]
:sepal-width
[3.200, 3.200, 3.100, 2.300, 2.800, 2.800, 3.300, 2.400, 2.900, 2.700, 2.000, 3.000, 2.200, 2.900, 2.900, 3.100, 3.000, 2.700, 2.200, 2.500...],
:label "versicolor",
:row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19...]}],
:y-domain [2.0 3.4],
:x-domain [4.9 7.0]}],
:y-scale {:type :linear},
:y-ticks
{:values [2.0 2.5 3.0 3.5 4.0 4.5],
:labels ["2.0" "2.5" "3.0" "3.5" "4.0" "4.5"],
:categorical? false},
:row 0}
{:coord :cartesian,
:y-domain [1.93 4.505000000000001],
:col-label "virginica",
:x-scale {:type :linear},
:x-domain [4.225 8.05],
:x-ticks {:values [6.0 8.0], :labels ["6" "8"], :categorical? false},
:col 2,
:layers
[{:mark :point,
:style {:opacity 0.75, :radius 3.0},
:size-scale nil,
:alpha-scale nil,
:groups
[{:color
[0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0],
:xs #tech.v3.dataset.column<float64>[50]
:sepal-length
[6.300, 5.800, 7.100, 6.300, 6.500, 7.600, 4.900, 7.300, 6.700, 7.200, 6.500, 6.400, 6.800, 5.700, 5.800, 6.400, 6.500, 7.700, 7.700, 6.000...],
:ys #tech.v3.dataset.column<float64>[50]
:sepal-width
[3.300, 2.700, 3.000, 2.900, 3.000, 3.000, 2.500, 2.900, 2.500, 3.600, 3.200, 2.700, 3.000, 2.500, 2.800, 3.200, 3.000, 3.800, 2.600, 2.200...],
:label "virginica",
:row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19...]}],
:y-domain [2.2 3.8],
:x-domain [4.9 7.9]}],
:y-scale {:type :linear},
:y-ticks
{:values [2.0 2.5 3.0 3.5 4.0 4.5],
:labels ["2.0" "2.5" "3.0" "3.5" "4.0" "4.5"],
:categorical? false},
:row 0}]Panel-level domains show the data range for each subset:
(:panels faceted-plan)[{:coord :cartesian,
:y-domain [1.93 4.505000000000001],
:col-label "setosa",
:x-scale {:type :linear},
:x-domain [4.225 8.05],
:x-ticks {:values [6.0 8.0], :labels ["6" "8"], :categorical? false},
:col 0,
:layers
[{:mark :point,
:style {:opacity 0.75, :radius 3.0},
:size-scale nil,
:alpha-scale nil,
:groups
[{:color
[0.8941176470588236 0.10196078431372549 0.10980392156862745 1.0],
:xs #tech.v3.dataset.column<float64>[50]
:sepal-length
[5.100, 4.900, 4.700, 4.600, 5.000, 5.400, 4.600, 5.000, 4.400, 4.900, 5.400, 4.800, 4.800, 4.300, 5.800, 5.700, 5.400, 5.100, 5.700, 5.100...],
:ys #tech.v3.dataset.column<float64>[50]
:sepal-width
[3.500, 3.000, 3.200, 3.100, 3.600, 3.900, 3.400, 3.400, 2.900, 3.100, 3.700, 3.400, 3.000, 3.000, 4.000, 4.400, 3.900, 3.500, 3.800, 3.800...],
:label "setosa",
:row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19...]}],
:y-domain [2.3 4.4],
:x-domain [4.3 5.8]}],
:y-scale {:type :linear},
:y-ticks
{:values [2.0 2.5 3.0 3.5 4.0 4.5],
:labels ["2.0" "2.5" "3.0" "3.5" "4.0" "4.5"],
:categorical? false},
:row 0}
{:coord :cartesian,
:y-domain [1.93 4.505000000000001],
:col-label "versicolor",
:x-scale {:type :linear},
:x-domain [4.225 8.05],
:x-ticks {:values [6.0 8.0], :labels ["6" "8"], :categorical? false},
:col 1,
:layers
[{:mark :point,
:style {:opacity 0.75, :radius 3.0},
:size-scale nil,
:alpha-scale nil,
:groups
[{:color
[0.21568627450980393 0.49411764705882355 0.7215686274509804 1.0],
:xs #tech.v3.dataset.column<float64>[50]
:sepal-length
[7.000, 6.400, 6.900, 5.500, 6.500, 5.700, 6.300, 4.900, 6.600, 5.200, 5.000, 5.900, 6.000, 6.100, 5.600, 6.700, 5.600, 5.800, 6.200, 5.600...],
:ys #tech.v3.dataset.column<float64>[50]
:sepal-width
[3.200, 3.200, 3.100, 2.300, 2.800, 2.800, 3.300, 2.400, 2.900, 2.700, 2.000, 3.000, 2.200, 2.900, 2.900, 3.100, 3.000, 2.700, 2.200, 2.500...],
:label "versicolor",
:row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19...]}],
:y-domain [2.0 3.4],
:x-domain [4.9 7.0]}],
:y-scale {:type :linear},
:y-ticks
{:values [2.0 2.5 3.0 3.5 4.0 4.5],
:labels ["2.0" "2.5" "3.0" "3.5" "4.0" "4.5"],
:categorical? false},
:row 0}
{:coord :cartesian,
:y-domain [1.93 4.505000000000001],
:col-label "virginica",
:x-scale {:type :linear},
:x-domain [4.225 8.05],
:x-ticks {:values [6.0 8.0], :labels ["6" "8"], :categorical? false},
:col 2,
:layers
[{:mark :point,
:style {:opacity 0.75, :radius 3.0},
:size-scale nil,
:alpha-scale nil,
:groups
[{:color
[0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0],
:xs #tech.v3.dataset.column<float64>[50]
:sepal-length
[6.300, 5.800, 7.100, 6.300, 6.500, 7.600, 4.900, 7.300, 6.700, 7.200, 6.500, 6.400, 6.800, 5.700, 5.800, 6.400, 6.500, 7.700, 7.700, 6.000...],
:ys #tech.v3.dataset.column<float64>[50]
:sepal-width
[3.300, 2.700, 3.000, 2.900, 3.000, 3.000, 2.500, 2.900, 2.500, 3.600, 3.200, 2.700, 3.000, 2.500, 2.800, 3.200, 3.000, 3.800, 2.600, 2.200...],
:label "virginica",
:row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19...]}],
:y-domain [2.2 3.8],
:x-domain [4.9 7.9]}],
:y-scale {:type :linear},
:y-ticks
{:values [2.0 2.5 3.0 3.5 4.0 4.5],
:labels ["2.0" "2.5" "3.0" "3.5" "4.0" "4.5"],
:categorical? false},
:row 0}]With shared scales (the default), all panels have the same domains. With :scales :free-y, each panel gets its own y-domain.
The plan also records per-panel pixel dimensions:
(select-keys faceted-plan [:layout-type :grid :total-width :total-height]){:layout-type :facet-grid,
:grid {:rows 1, :cols 3},
:total-width 600.0,
:total-height 400.0}Multi-panel plans validate against the same Malli schema:
(pj/valid-plan? faceted-plan)trueMalli Validation
Every plan conforms to a Malli schema. Validation runs automatically when pj/plan is called (default :validate true). Pass {:validate false} to skip it.
You can also check manually with pj/valid-plan?:
(pj/valid-plan? tiny-plan)true(pj/valid-plan? iris-plan)true(pj/valid-plan? hist-plan)true(pj/valid-plan? bar-plan)true(pj/valid-plan? lm-plan)true(pj/valid-plan? final-plan)trueWhen a plan is invalid, pj/explain-plan shows which part failed:
(pj/explain-plan (assoc tiny-plan :width "not-a-number")){:schema
[:or [:map [:width pos-int?] [:height pos-int?] [:margin number?] [:total-width number?] [:total-height number?] [:panel-width number?] [:panel-height number?] [:grid [:map [:rows pos-int?] [:cols pos-int?]]] [:layout-type [:enum :single :facet-grid :multi-variable]] [:title {:optional true} [:maybe string?]] [:subtitle {:optional true} [:maybe string?]] [:caption {:optional true} [:maybe string?]] [:x-label {:optional true} [:maybe string?]] [:y-label {:optional true} [:maybe string?]] [:legend {:optional true} [:maybe [:or [:map [:title [:or keyword? string?]] [:entries [:vector [:map [:label string?] [:color [:vector {:min 3, :max 4} number?]]]]]] [:map [:title [:or keyword? string?]] [:type [:= :continuous]] [:min number?] [:max number?] [:color-scale {:optional true} [:maybe [:or keyword? map?]]] [:scale-type {:optional true} [:enum :linear :log]] [:ticks {:optional true} [:vector [:map [:value number?] [:t number?]]]] [:stops [:vector [:map [:t number?] [:color [:vector {:min 3, :max 4} number?]]]]]]]]] [:size-legend {:optional true} [:maybe [:map [:title [:or keyword? string?]] [:type [:= :size]] [:min number?] [:max number?] [:entries [:vector [:map [:value number?] [:radius number?]]]]]]] [:alpha-legend {:optional true} [:maybe [:map [:title [:or keyword? string?]] [:type [:= :alpha]] [:min number?] [:max number?] [:entries [:vector [:map [:value number?] [:alpha number?]]]]]]] [:legend-position [:enum :right :bottom :top :none]] [:panels [:vector [:map [:x-domain [:vector any?]] [:y-domain [:vector any?]] [:x-scale [:map [:type [:enum :linear :log :categorical]]]] [:y-scale [:map [:type [:enum :linear :log :categorical]]]] [:coord [:enum :cartesian :flip :polar :fixed]] [:x-ticks [:map [:values [:vector any?]] [:labels [:vector string?]] [:categorical? boolean?]]] [:y-ticks [:map [:values [:vector any?]] [:labels [:vector string?]] [:categorical? boolean?]]] [:layers [:vector [:map [:mark keyword?] [:style [:map [:opacity {:optional true} number?] [:radius {:optional true} number?] [:stroke-width {:optional true} number?] [:font-size {:optional true} number?] [:box-width {:optional true} number?] [:cap-width {:optional true} number?] [:length {:optional true} number?] [:jitter {:optional true} [:or boolean? number?]]]] [:groups {:optional true} [:vector [:or [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential any?]] [:ys [:sequential any?]] [:colors {:optional true} [:sequential [:vector {:min 3, :max 4} number?]]] [:sizes {:optional true} [:sequential number?]] [:alphas {:optional true} [:sequential number?]] [:shapes {:optional true} [:vector any?]] [:row-indices {:optional true} [:sequential int?]] [:dodge-idx {:optional true} int?] [:y0s {:optional true} [:sequential number?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:bars [:vector [:map [:lo number?] [:hi number?] [:count number?]]]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:label string?] [:counts [:vector [:map [:category any?] [:count number?] [:y0 {:optional true} number?] [:y1 {:optional true} number?]]]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:label {:optional true} string?] [:xs [:sequential any?]] [:ys [:sequential number?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:x1 number?] [:y1 number?] [:x2 number?] [:y2 number?]] [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential any?]] [:ys [:sequential any?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential any?]] [:ys [:sequential any?]] [:labels {:optional true} [:vector string?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential number?]] [:ys [:sequential number?]] [:ymins [:sequential number?]] [:ymaxs [:sequential number?]]]]]] [:boxes {:optional true} [:vector [:map [:category any?] [:color [:vector {:min 3, :max 4} number?]] [:color-category {:optional true} any?] [:median number?] [:q1 number?] [:q3 number?] [:whisker-lo number?] [:whisker-hi number?] [:outliers {:optional true} [:sequential number?]] [:dodge-idx {:optional true} int?]]]] [:violins {:optional true} [:vector [:map [:category any?] [:color [:vector {:min 3, :max 4} number?]] [:color-category {:optional true} any?] [:ys [:sequential number?]] [:densities [:sequential number?]] [:dodge-idx {:optional true} int?]]]] [:tiles {:optional true} [:vector [:map [:x-lo number?] [:x-hi number?] [:y-lo number?] [:y-hi number?] [:color [:vector {:min 3, :max 4} number?]]]]] [:ridges {:optional true} [:vector [:map [:category any?] [:color [:vector {:min 3, :max 4} number?]] [:ys [:sequential number?]] [:densities [:sequential number?]]]]] [:levels {:optional true} [:vector any?]] [:ribbons {:optional true} [:vector any?]] [:color-categories {:optional true} [:maybe [:vector any?]]] [:position {:optional true} keyword?] [:dodge-ctx {:optional true} any?] [:categories {:optional true} [:vector any?]] [:side {:optional true} [:enum :x :y :both]]]]] [:row int?] [:col int?] [:annotations {:optional true} [:vector [:multi {:dispatch :mark} [:rule-h [:map [:mark [:= :rule-h]] [:y-intercept [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]] [:rule-v [:map [:mark [:= :rule-v]] [:x-intercept [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]] [:band-h [:map [:mark [:= :band-h]] [:y-min [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:y-max [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]] [:band-v [:map [:mark [:= :band-v]] [:x-min [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:x-max [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]]]]] [:row-label {:optional true} [:maybe string?]] [:col-label {:optional true} [:maybe string?]]]]] [:layout [:map [:x-label-pad number?] [:y-label-pad number?] [:title-pad number?] [:subtitle-pad number?] [:caption-pad number?] [:legend-w number?] [:legend-h number?] [:strip-h number?] [:strip-w number?]]]] [:map [:width pos-int?] [:height pos-int?] [:sub-plots [:vector [:map [:path [:vector int?]] [:rect [:vector {:min 4, :max 4} number?]] [:plan [:map [:width pos-int?] [:height pos-int?] [:margin number?] [:total-width number?] [:total-height number?] [:panel-width number?] [:panel-height number?] [:grid [:map [:rows pos-int?] [:cols pos-int?]]] [:layout-type [:enum :single :facet-grid :multi-variable]] [:title {:optional true} [:maybe string?]] [:subtitle {:optional true} [:maybe string?]] [:caption {:optional true} [:maybe string?]] [:x-label {:optional true} [:maybe string?]] [:y-label {:optional true} [:maybe string?]] [:legend {:optional true} [:maybe [:or [:map [:title [:or keyword? string?]] [:entries [:vector [:map [:label string?] [:color [:vector {:min 3, :max 4} number?]]]]]] [:map [:title [:or keyword? string?]] [:type [:= :continuous]] [:min number?] [:max number?] [:color-scale {:optional true} [:maybe [:or keyword? map?]]] [:scale-type {:optional true} [:enum :linear :log]] [:ticks {:optional true} [:vector [:map [:value number?] [:t number?]]]] [:stops [:vector [:map [:t number?] [:color [:vector {:min 3, :max 4} number?]]]]]]]]] [:size-legend {:optional true} [:maybe [:map [:title [:or keyword? string?]] [:type [:= :size]] [:min number?] [:max number?] [:entries [:vector [:map [:value number?] [:radius number?]]]]]]] [:alpha-legend {:optional true} [:maybe [:map [:title [:or keyword? string?]] [:type [:= :alpha]] [:min number?] [:max number?] [:entries [:vector [:map [:value number?] [:alpha number?]]]]]]] [:legend-position [:enum :right :bottom :top :none]] [:panels [:vector [:map [:x-domain [:vector any?]] [:y-domain [:vector any?]] [:x-scale [:map [:type [:enum :linear :log :categorical]]]] [:y-scale [:map [:type [:enum :linear :log :categorical]]]] [:coord [:enum :cartesian :flip :polar :fixed]] [:x-ticks [:map [:values [:vector any?]] [:labels [:vector string?]] [:categorical? boolean?]]] [:y-ticks [:map [:values [:vector any?]] [:labels [:vector string?]] [:categorical? boolean?]]] [:layers [:vector [:map [:mark keyword?] [:style [:map [:opacity {:optional true} number?] [:radius {:optional true} number?] [:stroke-width {:optional true} number?] [:font-size {:optional true} number?] [:box-width {:optional true} number?] [:cap-width {:optional true} number?] [:length {:optional true} number?] [:jitter {:optional true} [:or boolean? number?]]]] [:groups {:optional true} [:vector [:or [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential any?]] [:ys [:sequential any?]] [:colors {:optional true} [:sequential [:vector {:min 3, :max 4} number?]]] [:sizes {:optional true} [:sequential number?]] [:alphas {:optional true} [:sequential number?]] [:shapes {:optional true} [:vector any?]] [:row-indices {:optional true} [:sequential int?]] [:dodge-idx {:optional true} int?] [:y0s {:optional true} [:sequential number?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:bars [:vector [:map [:lo number?] [:hi number?] [:count number?]]]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:label string?] [:counts [:vector [:map [:category any?] [:count number?] [:y0 {:optional true} number?] [:y1 {:optional true} number?]]]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:label {:optional true} string?] [:xs [:sequential any?]] [:ys [:sequential number?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:x1 number?] [:y1 number?] [:x2 number?] [:y2 number?]] [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential any?]] [:ys [:sequential any?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential any?]] [:ys [:sequential any?]] [:labels {:optional true} [:vector string?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential number?]] [:ys [:sequential number?]] [:ymins [:sequential number?]] [:ymaxs [:sequential number?]]]]]] [:boxes {:optional true} [:vector [:map [:category any?] [:color [:vector {:min 3, :max 4} number?]] [:color-category {:optional true} any?] [:median number?] [:q1 number?] [:q3 number?] [:whisker-lo number?] [:whisker-hi number?] [:outliers {:optional true} [:sequential number?]] [:dodge-idx {:optional true} int?]]]] [:violins {:optional true} [:vector [:map [:category any?] [:color [:vector {:min 3, :max 4} number?]] [:color-category {:optional true} any?] [:ys [:sequential number?]] [:densities [:sequential number?]] [:dodge-idx {:optional true} int?]]]] [:tiles {:optional true} [:vector [:map [:x-lo number?] [:x-hi number?] [:y-lo number?] [:y-hi number?] [:color [:vector {:min 3, :max 4} number?]]]]] [:ridges {:optional true} [:vector [:map [:category any?] [:color [:vector {:min 3, :max 4} number?]] [:ys [:sequential number?]] [:densities [:sequential number?]]]]] [:levels {:optional true} [:vector any?]] [:ribbons {:optional true} [:vector any?]] [:color-categories {:optional true} [:maybe [:vector any?]]] [:position {:optional true} keyword?] [:dodge-ctx {:optional true} any?] [:categories {:optional true} [:vector any?]] [:side {:optional true} [:enum :x :y :both]]]]] [:row int?] [:col int?] [:annotations {:optional true} [:vector [:multi {:dispatch :mark} [:rule-h [:map [:mark [:= :rule-h]] [:y-intercept [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]] [:rule-v [:map [:mark [:= :rule-v]] [:x-intercept [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]] [:band-h [:map [:mark [:= :band-h]] [:y-min [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:y-max [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]] [:band-v [:map [:mark [:= :band-v]] [:x-min [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:x-max [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]]]]] [:row-label {:optional true} [:maybe string?]] [:col-label {:optional true} [:maybe string?]]]]] [:layout [:map [:x-label-pad number?] [:y-label-pad number?] [:title-pad number?] [:subtitle-pad number?] [:caption-pad number?] [:legend-w number?] [:legend-h number?] [:strip-h number?] [:strip-w number?]]]]]]]] [:chrome [:map [:title {:optional true} [:maybe string?]] [:title-band-h number?] [:grid-rect [:vector {:min 4, :max 4} number?]] [:legend-w number?] [:strip-h number?] [:strip-w number?] [:col-labels [:vector any?]] [:row-labels [:vector any?]] [:n-cols int?] [:n-rows int?] [:matrix? boolean?] [:shared-legend {:optional true} [:maybe [:map [:legend {:optional true} [:maybe [:or [:map [:title [:or keyword? string?]] [:entries [:vector [:map [:label string?] [:color [:vector {:min 3, :max 4} number?]]]]]] [:map [:title [:or keyword? string?]] [:type [:= :continuous]] [:min number?] [:max number?] [:color-scale {:optional true} [:maybe [:or keyword? map?]]] [:scale-type {:optional true} [:enum :linear :log]] [:ticks {:optional true} [:vector [:map [:value number?] [:t number?]]]] [:stops [:vector [:map [:t number?] [:color [:vector {:min 3, :max 4} number?]]]]]]]]] [:size-legend {:optional true} [:maybe [:map [:title [:or keyword? string?]] [:type [:= :size]] [:min number?] [:max number?] [:entries [:vector [:map [:value number?] [:radius number?]]]]]]] [:alpha-legend {:optional true} [:maybe [:map [:title [:or keyword? string?]] [:type [:= :alpha]] [:min number?] [:max number?] [:entries [:vector [:map [:value number?] [:alpha number?]]]]]]]]]] [:layout [:map-of [:vector int?] [:vector {:min 4, :max 4} number?]]]]]]],
:value
{: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 "not-a-number",
: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},
:errors
({:path [0 :width],
:in [:width],
:schema pos-int?,
:value "not-a-number"}
{:path [1 :width],
:in [:width],
:schema pos-int?,
:value "not-a-number"}
{:path [1 :sub-plots],
:in [:sub-plots],
:schema
[:map [:width pos-int?] [:height pos-int?] [:sub-plots [:vector [:map [:path [:vector int?]] [:rect [:vector {:min 4, :max 4} number?]] [:plan [:map [:width pos-int?] [:height pos-int?] [:margin number?] [:total-width number?] [:total-height number?] [:panel-width number?] [:panel-height number?] [:grid [:map [:rows pos-int?] [:cols pos-int?]]] [:layout-type [:enum :single :facet-grid :multi-variable]] [:title {:optional true} [:maybe string?]] [:subtitle {:optional true} [:maybe string?]] [:caption {:optional true} [:maybe string?]] [:x-label {:optional true} [:maybe string?]] [:y-label {:optional true} [:maybe string?]] [:legend {:optional true} [:maybe [:or [:map [:title [:or keyword? string?]] [:entries [:vector [:map [:label string?] [:color [:vector {:min 3, :max 4} number?]]]]]] [:map [:title [:or keyword? string?]] [:type [:= :continuous]] [:min number?] [:max number?] [:color-scale {:optional true} [:maybe [:or keyword? map?]]] [:scale-type {:optional true} [:enum :linear :log]] [:ticks {:optional true} [:vector [:map [:value number?] [:t number?]]]] [:stops [:vector [:map [:t number?] [:color [:vector {:min 3, :max 4} number?]]]]]]]]] [:size-legend {:optional true} [:maybe [:map [:title [:or keyword? string?]] [:type [:= :size]] [:min number?] [:max number?] [:entries [:vector [:map [:value number?] [:radius number?]]]]]]] [:alpha-legend {:optional true} [:maybe [:map [:title [:or keyword? string?]] [:type [:= :alpha]] [:min number?] [:max number?] [:entries [:vector [:map [:value number?] [:alpha number?]]]]]]] [:legend-position [:enum :right :bottom :top :none]] [:panels [:vector [:map [:x-domain [:vector any?]] [:y-domain [:vector any?]] [:x-scale [:map [:type [:enum :linear :log :categorical]]]] [:y-scale [:map [:type [:enum :linear :log :categorical]]]] [:coord [:enum :cartesian :flip :polar :fixed]] [:x-ticks [:map [:values [:vector any?]] [:labels [:vector string?]] [:categorical? boolean?]]] [:y-ticks [:map [:values [:vector any?]] [:labels [:vector string?]] [:categorical? boolean?]]] [:layers [:vector [:map [:mark keyword?] [:style [:map [:opacity {:optional true} number?] [:radius {:optional true} number?] [:stroke-width {:optional true} number?] [:font-size {:optional true} number?] [:box-width {:optional true} number?] [:cap-width {:optional true} number?] [:length {:optional true} number?] [:jitter {:optional true} [:or boolean? number?]]]] [:groups {:optional true} [:vector [:or [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential any?]] [:ys [:sequential any?]] [:colors {:optional true} [:sequential [:vector {:min 3, :max 4} number?]]] [:sizes {:optional true} [:sequential number?]] [:alphas {:optional true} [:sequential number?]] [:shapes {:optional true} [:vector any?]] [:row-indices {:optional true} [:sequential int?]] [:dodge-idx {:optional true} int?] [:y0s {:optional true} [:sequential number?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:bars [:vector [:map [:lo number?] [:hi number?] [:count number?]]]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:label string?] [:counts [:vector [:map [:category any?] [:count number?] [:y0 {:optional true} number?] [:y1 {:optional true} number?]]]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:label {:optional true} string?] [:xs [:sequential any?]] [:ys [:sequential number?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:x1 number?] [:y1 number?] [:x2 number?] [:y2 number?]] [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential any?]] [:ys [:sequential any?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential any?]] [:ys [:sequential any?]] [:labels {:optional true} [:vector string?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential number?]] [:ys [:sequential number?]] [:ymins [:sequential number?]] [:ymaxs [:sequential number?]]]]]] [:boxes {:optional true} [:vector [:map [:category any?] [:color [:vector {:min 3, :max 4} number?]] [:color-category {:optional true} any?] [:median number?] [:q1 number?] [:q3 number?] [:whisker-lo number?] [:whisker-hi number?] [:outliers {:optional true} [:sequential number?]] [:dodge-idx {:optional true} int?]]]] [:violins {:optional true} [:vector [:map [:category any?] [:color [:vector {:min 3, :max 4} number?]] [:color-category {:optional true} any?] [:ys [:sequential number?]] [:densities [:sequential number?]] [:dodge-idx {:optional true} int?]]]] [:tiles {:optional true} [:vector [:map [:x-lo number?] [:x-hi number?] [:y-lo number?] [:y-hi number?] [:color [:vector {:min 3, :max 4} number?]]]]] [:ridges {:optional true} [:vector [:map [:category any?] [:color [:vector {:min 3, :max 4} number?]] [:ys [:sequential number?]] [:densities [:sequential number?]]]]] [:levels {:optional true} [:vector any?]] [:ribbons {:optional true} [:vector any?]] [:color-categories {:optional true} [:maybe [:vector any?]]] [:position {:optional true} keyword?] [:dodge-ctx {:optional true} any?] [:categories {:optional true} [:vector any?]] [:side {:optional true} [:enum :x :y :both]]]]] [:row int?] [:col int?] [:annotations {:optional true} [:vector [:multi {:dispatch :mark} [:rule-h [:map [:mark [:= :rule-h]] [:y-intercept [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]] [:rule-v [:map [:mark [:= :rule-v]] [:x-intercept [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]] [:band-h [:map [:mark [:= :band-h]] [:y-min [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:y-max [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]] [:band-v [:map [:mark [:= :band-v]] [:x-min [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:x-max [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]]]]] [:row-label {:optional true} [:maybe string?]] [:col-label {:optional true} [:maybe string?]]]]] [:layout [:map [:x-label-pad number?] [:y-label-pad number?] [:title-pad number?] [:subtitle-pad number?] [:caption-pad number?] [:legend-w number?] [:legend-h number?] [:strip-h number?] [:strip-w number?]]]]]]]] [:chrome [:map [:title {:optional true} [:maybe string?]] [:title-band-h number?] [:grid-rect [:vector {:min 4, :max 4} number?]] [:legend-w number?] [:strip-h number?] [:strip-w number?] [:col-labels [:vector any?]] [:row-labels [:vector any?]] [:n-cols int?] [:n-rows int?] [:matrix? boolean?] [:shared-legend {:optional true} [:maybe [:map [:legend {:optional true} [:maybe [:or [:map [:title [:or keyword? string?]] [:entries [:vector [:map [:label string?] [:color [:vector {:min 3, :max 4} number?]]]]]] [:map [:title [:or keyword? string?]] [:type [:= :continuous]] [:min number?] [:max number?] [:color-scale {:optional true} [:maybe [:or keyword? map?]]] [:scale-type {:optional true} [:enum :linear :log]] [:ticks {:optional true} [:vector [:map [:value number?] [:t number?]]]] [:stops [:vector [:map [:t number?] [:color [:vector {:min 3, :max 4} number?]]]]]]]]] [:size-legend {:optional true} [:maybe [:map [:title [:or keyword? string?]] [:type [:= :size]] [:min number?] [:max number?] [:entries [:vector [:map [:value number?] [:radius number?]]]]]]] [:alpha-legend {:optional true} [:maybe [:map [:title [:or keyword? string?]] [:type [:= :alpha]] [:min number?] [:max number?] [:entries [:vector [:map [:value number?] [:alpha number?]]]]]]]]]] [:layout [:map-of [:vector int?] [:vector {:min 4, :max 4} number?]]]]]],
:value nil,
:type :malli.core/missing-key}
{:path [1 :chrome],
:in [:chrome],
:schema
[:map [:width pos-int?] [:height pos-int?] [:sub-plots [:vector [:map [:path [:vector int?]] [:rect [:vector {:min 4, :max 4} number?]] [:plan [:map [:width pos-int?] [:height pos-int?] [:margin number?] [:total-width number?] [:total-height number?] [:panel-width number?] [:panel-height number?] [:grid [:map [:rows pos-int?] [:cols pos-int?]]] [:layout-type [:enum :single :facet-grid :multi-variable]] [:title {:optional true} [:maybe string?]] [:subtitle {:optional true} [:maybe string?]] [:caption {:optional true} [:maybe string?]] [:x-label {:optional true} [:maybe string?]] [:y-label {:optional true} [:maybe string?]] [:legend {:optional true} [:maybe [:or [:map [:title [:or keyword? string?]] [:entries [:vector [:map [:label string?] [:color [:vector {:min 3, :max 4} number?]]]]]] [:map [:title [:or keyword? string?]] [:type [:= :continuous]] [:min number?] [:max number?] [:color-scale {:optional true} [:maybe [:or keyword? map?]]] [:scale-type {:optional true} [:enum :linear :log]] [:ticks {:optional true} [:vector [:map [:value number?] [:t number?]]]] [:stops [:vector [:map [:t number?] [:color [:vector {:min 3, :max 4} number?]]]]]]]]] [:size-legend {:optional true} [:maybe [:map [:title [:or keyword? string?]] [:type [:= :size]] [:min number?] [:max number?] [:entries [:vector [:map [:value number?] [:radius number?]]]]]]] [:alpha-legend {:optional true} [:maybe [:map [:title [:or keyword? string?]] [:type [:= :alpha]] [:min number?] [:max number?] [:entries [:vector [:map [:value number?] [:alpha number?]]]]]]] [:legend-position [:enum :right :bottom :top :none]] [:panels [:vector [:map [:x-domain [:vector any?]] [:y-domain [:vector any?]] [:x-scale [:map [:type [:enum :linear :log :categorical]]]] [:y-scale [:map [:type [:enum :linear :log :categorical]]]] [:coord [:enum :cartesian :flip :polar :fixed]] [:x-ticks [:map [:values [:vector any?]] [:labels [:vector string?]] [:categorical? boolean?]]] [:y-ticks [:map [:values [:vector any?]] [:labels [:vector string?]] [:categorical? boolean?]]] [:layers [:vector [:map [:mark keyword?] [:style [:map [:opacity {:optional true} number?] [:radius {:optional true} number?] [:stroke-width {:optional true} number?] [:font-size {:optional true} number?] [:box-width {:optional true} number?] [:cap-width {:optional true} number?] [:length {:optional true} number?] [:jitter {:optional true} [:or boolean? number?]]]] [:groups {:optional true} [:vector [:or [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential any?]] [:ys [:sequential any?]] [:colors {:optional true} [:sequential [:vector {:min 3, :max 4} number?]]] [:sizes {:optional true} [:sequential number?]] [:alphas {:optional true} [:sequential number?]] [:shapes {:optional true} [:vector any?]] [:row-indices {:optional true} [:sequential int?]] [:dodge-idx {:optional true} int?] [:y0s {:optional true} [:sequential number?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:bars [:vector [:map [:lo number?] [:hi number?] [:count number?]]]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:label string?] [:counts [:vector [:map [:category any?] [:count number?] [:y0 {:optional true} number?] [:y1 {:optional true} number?]]]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:label {:optional true} string?] [:xs [:sequential any?]] [:ys [:sequential number?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:x1 number?] [:y1 number?] [:x2 number?] [:y2 number?]] [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential any?]] [:ys [:sequential any?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential any?]] [:ys [:sequential any?]] [:labels {:optional true} [:vector string?]]] [:map [:color [:vector {:min 3, :max 4} number?]] [:xs [:sequential number?]] [:ys [:sequential number?]] [:ymins [:sequential number?]] [:ymaxs [:sequential number?]]]]]] [:boxes {:optional true} [:vector [:map [:category any?] [:color [:vector {:min 3, :max 4} number?]] [:color-category {:optional true} any?] [:median number?] [:q1 number?] [:q3 number?] [:whisker-lo number?] [:whisker-hi number?] [:outliers {:optional true} [:sequential number?]] [:dodge-idx {:optional true} int?]]]] [:violins {:optional true} [:vector [:map [:category any?] [:color [:vector {:min 3, :max 4} number?]] [:color-category {:optional true} any?] [:ys [:sequential number?]] [:densities [:sequential number?]] [:dodge-idx {:optional true} int?]]]] [:tiles {:optional true} [:vector [:map [:x-lo number?] [:x-hi number?] [:y-lo number?] [:y-hi number?] [:color [:vector {:min 3, :max 4} number?]]]]] [:ridges {:optional true} [:vector [:map [:category any?] [:color [:vector {:min 3, :max 4} number?]] [:ys [:sequential number?]] [:densities [:sequential number?]]]]] [:levels {:optional true} [:vector any?]] [:ribbons {:optional true} [:vector any?]] [:color-categories {:optional true} [:maybe [:vector any?]]] [:position {:optional true} keyword?] [:dodge-ctx {:optional true} any?] [:categories {:optional true} [:vector any?]] [:side {:optional true} [:enum :x :y :both]]]]] [:row int?] [:col int?] [:annotations {:optional true} [:vector [:multi {:dispatch :mark} [:rule-h [:map [:mark [:= :rule-h]] [:y-intercept [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]] [:rule-v [:map [:mark [:= :rule-v]] [:x-intercept [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]] [:band-h [:map [:mark [:= :band-h]] [:y-min [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:y-max [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]] [:band-v [:map [:mark [:= :band-v]] [:x-min [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:x-max [:and number? [:fn #:error{:message "must be finite (not NaN or Infinity)"} #object[scicloj.plotje.impl.plan_schema$fn__450187 0x3244ab16 "scicloj.plotje.impl.plan_schema$fn__450187@3244ab16"]]]] [:color {:optional true} string?] [:alpha {:optional true} number?]]]]]] [:row-label {:optional true} [:maybe string?]] [:col-label {:optional true} [:maybe string?]]]]] [:layout [:map [:x-label-pad number?] [:y-label-pad number?] [:title-pad number?] [:subtitle-pad number?] [:caption-pad number?] [:legend-w number?] [:legend-h number?] [:strip-h number?] [:strip-w number?]]]]]]]] [:chrome [:map [:title {:optional true} [:maybe string?]] [:title-band-h number?] [:grid-rect [:vector {:min 4, :max 4} number?]] [:legend-w number?] [:strip-h number?] [:strip-w number?] [:col-labels [:vector any?]] [:row-labels [:vector any?]] [:n-cols int?] [:n-rows int?] [:matrix? boolean?] [:shared-legend {:optional true} [:maybe [:map [:legend {:optional true} [:maybe [:or [:map [:title [:or keyword? string?]] [:entries [:vector [:map [:label string?] [:color [:vector {:min 3, :max 4} number?]]]]]] [:map [:title [:or keyword? string?]] [:type [:= :continuous]] [:min number?] [:max number?] [:color-scale {:optional true} [:maybe [:or keyword? map?]]] [:scale-type {:optional true} [:enum :linear :log]] [:ticks {:optional true} [:vector [:map [:value number?] [:t number?]]]] [:stops [:vector [:map [:t number?] [:color [:vector {:min 3, :max 4} number?]]]]]]]]] [:size-legend {:optional true} [:maybe [:map [:title [:or keyword? string?]] [:type [:= :size]] [:min number?] [:max number?] [:entries [:vector [:map [:value number?] [:radius number?]]]]]]] [:alpha-legend {:optional true} [:maybe [:map [:title [:or keyword? string?]] [:type [:= :alpha]] [:min number?] [:max number?] [:entries [:vector [:map [:value number?] [:alpha number?]]]]]]]]]] [:layout [:map-of [:vector int?] [:vector {:min 4, :max 4} number?]]]]]],
:value nil,
:type :malli.core/missing-key})}Data Types
Plans are plain inspectable data β maps, numbers, strings, keywords, and dtype-next buffers for numeric arrays (:xs, :ys, etc.). The buffers support nth, count, seq, and standard sequence operations.
(type (:xs (first (:groups (first (:layers (first (:panels tiny-plan))))))))tech.v3.dataset.impl.column.ColumnYou can convert any numeric buffer to a plain vector with vec:
(vec (:xs (first (:groups (first (:layers (first (:panels tiny-plan))))))))[1 2 3 4 5]Whatβs Next
- Extensibility β add custom marks, stats, and renderers