126126; ; decisions. This is for Tableplot maintainers, contributors, and curious users who
127127; ; want to provide early feedback on the approach.
128128
129- ; ; # Setup
130- ; ;
131- ; ; ### 📖 Reading This Document
129+ ;
130+ ; ; # 📖 Reading This Document
132131; ;
133132; ; Throughout this document, section headers use emojis to indicate the type of content:
134133; ;
139138; ; This convention helps you navigate the document and quickly find what you're looking for:
140139; ; conceptual explanations (📖), working code (⚙️), or usage examples (🧪).
141140
141+ ; ; # Setup
142+
143+ ; ; ### ⚙️ Dependencies
142144; ;
143145; ; This notebook relies on several libraries from the Clojure data science ecosystem.
144146; ; Here's what we use and why:
185187; ; [**Fastmath**](https://github.com/generateme/fastmath) handles our statistical computations, particularly linear
186188; ; regression. It's a comprehensive math library for Clojure.
187189; ;
190+ ; ; [**Malli**](https://github.com/metosin/malli) provides schema validation for plot specifications.
191+ ; ; We define schemas for layers, aesthetics, and plot types to catch errors early
192+ ; ; and generate helpful error messages. Validation is optional but recommended.
193+ ; ;
188194; ; [**RDatasets**](https://vincentarelbundock.github.io/Rdatasets/articles/data.html) provides classic datasets (penguins, mtcars, iris) for examples.
189195; ; It is made available in Clojure through [metamorph.ml](https://github.com/scicloj/metamorph.ml).
190196
202208; ; This approach of
203209; ; ["describing a higher-level 'intent' how your tabular data should be transformed"](https://aog.makie.org/dev/tutorials/intro-i)
204210; ; aligns naturally with Clojure's functional and declarative tendencies—something
205- ; ; we've seen in libraries like Hanami, Oz , and others in the ecosystem.
211+ ; ; we've seen in libraries like Hanami, Tableplot , and others in the ecosystem.
206212; ;
207- ; ; We chose AoG because it seemed small enough to grasp and reproduce, while still being
213+ ; ; We chose AoG because it seemed well-thought, small enough to grasp and reproduce, while still being
208214; ; reasonably complete in its scope.
209215
210216; ; ### 📖 Glossary: Visualization Terminology
378384; ; **2. Two compositional operators**
379385; ; - `=*` merges layers (like Julia's `*`): `(=* data mapping geom)`
380386; ; - `=+` overlays layers (like Julia's `+`): `(=+ scatter linear)`
381- ; ; - Threading-macro friendly: `(-> data (mapping :x :y) (scatter))`
387+ ; ; - Threading-macro friendly, implicitly merging : `(-> data (mapping :x :y) (scatter))`
382388; ;
383389; ; **3. Minimal delegation strategy**
384390; ; - We compute: statistical transforms, domains (when needed)
397403; ; - Enables clear distinction between plot configuration and layer data
398404; ;
399405; ; **6. Multi-target rendering**
400- ; ; - Same plot spec works across `:geom`, `:vl`, `:plotly`
406+ ; ; - Same plot spec works across `:geom`, `:vl`, `:plotly` rendering targsts
401407; ; - Backend selection via `:=target` key
402408; ; - Consistent behavior and theming
403409
416422; ;
417423; ; **Utilities**:
418424; ; - `(plot spec)` - explicitly render (usually auto-displays)
419- ; ; - `(facet spec {:col :species})` - add faceting
420- ; ; - `(scale :x {:domain [0 100]})` - customize scales
421- ; ; - `(target :geom)`, `(size 800 600)` - set plot-level properties
425+ ; ; - `(facet {:col :species})` - add faceting (compositional)
426+ ; ; - `(scale :x {:domain [0 100]})` - customize scales (compositional)
427+ ; ; - `(target :geom)`, `(size 800 600)` - set plot-level properties (compositional)
422428; ;
423429; ; **Auto-display:** Plot specs returned by `=*`, `=+`, and constructors automatically
424430; ; display as plots in Kindly-compatible notebooks. Use `kind/pprint` to inspect
12301236 " Add faceting to a plot specification.
12311237
12321238 Args:
1233- - spec: Plot spec with :=layers
12341239 - facet-spec: Map with :row and/or :col keys specifying faceting variables
12351240
1236- Returns plot spec with faceting applied to all layers.
1241+ Returns layer spec with faceting properties.
1242+
1243+ When called with spec as first arg, merges faceting into that spec.
12371244
12381245 Examples:
1239- (facet spec {:col :species})
1240- (facet spec {:row :sex :col :island})
1246+ (facet {:col :species})
1247+ (facet {:row :sex :col :island})
12411248
12421249 Threading-friendly:
12431250 (-> penguins (mapping :x :y) (scatter) (facet {:col :species}))"
1244- [spec facet-spec]
1245- (displays-as-plot
1251+ ([facet-spec]
12461252 (let [facet-keys (update-keys facet-spec =key)]
1247- ( update spec :=layers
1248- ( fn [layers ]
1249- ( mapv #( merge % facet-keys) layers)) ))))
1253+ { :=layers [facet-keys]}))
1254+ ([spec facet-spec ]
1255+ ( =* spec ( facet facet-spec ))))
12501256
12511257(defn scale
12521258 " Specify scale properties for an aesthetic.
@@ -3136,30 +3142,30 @@ iris
31363142; ; ### 🧪 Example 10: Simple Column Faceting
31373143; ;
31383144; ; Facet a scatter plot by species - this creates 3 side-by-side plots.
3139- (facet ( =* (data penguins)
3140- (mapping :bill-length-mm :bill-depth-mm )
3141- (scatter ) )
3142- {:col :species })
3145+ (=* (data penguins)
3146+ (mapping :bill-length-mm :bill-depth-mm )
3147+ (scatter )
3148+ ( facet {:col :species }) )
31433149
31443150(kind/test-last [#(and (map? %)
31453151 (contains? % :=layers )
31463152 (= (:=col (first (:=layers %))) :species ))])
31473153
31483154; ; Faceted histogram - per-species histograms with shared scales:
31493155
3150- (facet ( -> penguins
3151- (mapping :bill-length-mm nil )
3152- (histogram ) )
3153- {:col :species })
3156+ (-> penguins
3157+ (mapping :bill-length-mm nil )
3158+ (histogram )
3159+ ( facet {:col :species }) )
31543160
31553161; ; ### 🧪 Example 11: Row Faceting
31563162; ;
31573163; ; Facet by rows creates vertically stacked panels
31583164
3159- (facet ( =* (data penguins)
3160- (mapping :bill-length-mm :bill-depth-mm )
3161- (scatter ) )
3162- {:row :species })
3165+ (=* (data penguins)
3166+ (mapping :bill-length-mm :bill-depth-mm )
3167+ (scatter )
3168+ ( facet {:row :species }) )
31633169
31643170(kind/test-last [#(and (map? %)
31653171 (contains? % :=layers )
@@ -3170,10 +3176,10 @@ iris
31703176; ; Create a 2D grid of facets.
31713177; ; This creates a 3×2 grid (3 islands × 2 sexes = 6 panels)
31723178
3173- (facet ( =* (data penguins)
3174- (mapping :bill-length-mm :bill-depth-mm )
3175- (scatter ) )
3176- {:row :island :col :sex })
3179+ (=* (data penguins)
3180+ (mapping :bill-length-mm :bill-depth-mm )
3181+ (scatter )
3182+ ( facet {:row :island :col :sex }) )
31773183
31783184(kind/test-last [#(and (map? %)
31793185 (contains? % :=layers )
@@ -3195,13 +3201,13 @@ iris
31953201; ; by species (color) AND island (facet column), computing separate
31963202; ; regressions for each (species × island) combination.
31973203
3198- (facet ( =* (data penguins)
3199- (mapping :bill-length-mm
3200- :bill-depth-mm
3201- {:color :species })
3202- (=+ (scatter {:alpha 0.5 })
3203- (linear ) ))
3204- {:col :island })
3204+ (=* (data penguins)
3205+ (mapping :bill-length-mm
3206+ :bill-depth-mm
3207+ {:color :species })
3208+ (=+ (scatter {:alpha 0.5 })
3209+ (linear ))
3210+ ( facet {:col :island }) )
32053211
32063212; ; equivalently:
32073213(-> (data penguins)
0 commit comments