7777; ; Everything renders to [SVG](https://en.wikipedia.org/wiki/SVG) via [Hiccup](https://github.com/weavejester/hiccup). The post builds up incrementally:
7878; ; scatter plots, histograms, regression lines, bars,
7979; ; multi-panel layouts, polar coordinates, and interactivity.
80+ ; ;
8081; ; ---
8182
8283; ; ## Motivation
@@ -107,6 +108,7 @@ splom-tut/iris-splom-4x4
107108; ; Everything that the SPLOM Tutorial does — grid layout,
108109; ; scale sharing, color assignment, diagonal detection —
109110; ; should follow from the composed specification.
111+ ; ;
110112; ; ---
111113
112114; ; ## Glossary
@@ -150,6 +152,7 @@ splom-tut/iris-splom-4x4
150152; ;
151153; ; **Layout** -- how panels are arranged: a single plot,
152154; ; a scatterplot matrix, or a faceted grid.
155+ ; ;
153156; ; ---
154157
155158; ; ## Setup
201204
202205tips
203206
207+ ; ;
204208; ; ---
205209
206210; ; ## Composing Views
412416; ; > *The next sections build the rendering pipeline piece by piece.
413417; ; > To see results first and return for the implementation,
414418; ; > skip to [Scatter Plots](#scatter-plots).*
419+ ; ;
415420; ; ---
416421
417422; ; ## Theme and Colors
504509
505510(mapv fmt-name [:sepal-length :petal_width :species ])
506511
512+ ; ;
507513; ; ---
508514
509515; ; ## Inference and Defaults
658664
659665(select-keys (merge defaults {:point-radius 5 :bar-opacity 0.9 })
660666 [:point-radius :bar-opacity :line-width ])
667+ ; ;
661668; ; ---
662669
663670; ; ## Computing Statistics
742749(defmethod compute-stat :identity [view]
743750 (prepare-points view))
744751
752+ ; ;
745753; ; ---
746754
747755; ; ## Scales -- Data to Pixels
809817 {:A-position (s " A" )
810818 :B-band-info (s " B" true )
811819 :ticks (ws/ticks s)})
820+ ; ;
812821; ; ---
813822
814823; ; ## Coordinate Systems
865874(defmethod render-grid :default [_ sx sy pw ph m cfg]
866875 (render-grid :cartesian sx sy pw ph m cfg))
867876
877+ ; ;
868878; ; ---
869879
870880; ; ## Drawing Marks
928938 [:rect {:x 0 :y 0 :width 600 :height 400 :fill (:bg theme)}]
929939 marks]))
930940
941+ ; ;
931942; ; ---
932943
933944; ; ## Axes and Grid Lines
@@ -1015,6 +1026,7 @@ tips
10151026 (render-x-ticks :numeric sx pw ph m defaults )
10161027 (render-y-ticks :numeric sy pw ph m defaults )]))
10171028
1029+ ; ;
10181030; ; ---
10191031
10201032; ; ## Assembling a Panel
@@ -1160,6 +1172,7 @@ tips
11601172 (view [[:x :y ]])
11611173 (lay (point )))
11621174 600 400 25 )])
1175+ ; ;
11631176; ; ---
11641177
11651178; ; ## Rendering the Plot
@@ -1188,11 +1201,9 @@ tips
11881201
11891202(defn infer-layout [views]
11901203 (let [facet-rows (seq (remove nil? (map :facet-row views)))
1191- facet-cols (seq (remove nil? (map :facet-col views)))
1192- facet-vals (seq (remove nil? (map :facet-val views)))]
1204+ facet-cols (seq (remove nil? (map :facet-col views)))]
11931205 (cond
1194- (and facet-rows facet-cols) :facet-grid
1195- facet-vals :facet
1206+ (or facet-rows facet-cols) :facet-grid
11961207 :else (let [x-vars (distinct (map :x views))
11971208 y-vars (distinct (map :y views))]
11981209 (if (or (> (count x-vars) 1 ) (> (count y-vars) 1 ))
@@ -1296,16 +1307,13 @@ tips
12961307 layout-type (infer-layout non-ann-views)
12971308 x-vars (distinct (map :x non-ann-views))
12981309 y-vars (distinct (map :y non-ann-views))
1299- facet-vals (distinct (remove nil? (map :facet-val non-ann-views)))
13001310 facet-row-vals (distinct (remove nil? (map :facet-row non-ann-views)))
13011311 facet-col-vals (distinct (remove nil? (map :facet-col non-ann-views)))
13021312 cols (case layout-type
13031313 :facet-grid (count facet-col-vals)
1304- :facet (count facet-vals)
13051314 (count x-vars))
13061315 rows (case layout-type
13071316 :facet-grid (count facet-row-vals)
1308- :facet 1
13091317 (count y-vars))
13101318 multi? (and (= layout-type :multi-variable ) (> cols 1 ) (> rows 1 ))
13111319 m (if multi? (:margin-multi cfg) (:margin cfg))
@@ -1361,7 +1369,7 @@ tips
13611369 :shape-categories shape-categories :coord-type coord-type-main
13621370 :global-x-doms global-x-doms :global-y-doms global-y-doms
13631371 :x-vars x-vars :y-vars y-vars
1364- :facet-vals facet-vals :facet- row-vals facet-row-vals :facet-col-vals facet-col-vals
1372+ :facet-row-vals facet-row-vals :facet-col-vals facet-col-vals
13651373 :color-cols color-cols :shape-col shape-col :scale-mode scale-mode
13661374 :cfg cfg}
13671375 svg-content
@@ -1413,6 +1421,7 @@ tips
14131421 (into [:g ] (remove nil? (arrange-panels layout-type ctx)))]]]
14141422 (wrap-plot (cond-> #{} tooltip (conj :tooltip ) brush (conj :brush )) svg-content))))
14151423
1424+ ; ;
14161425; ; ---
14171426
14181427; ; ## Scatter Plots
@@ -1459,6 +1468,7 @@ tips
14591468 (lay (point {:color " steelblue" :size 6 }))
14601469 plot)
14611470
1471+ ; ;
14621472; ; ---
14631473
14641474; ; ## Histograms
@@ -1565,6 +1575,7 @@ tips
15651575 (coord :flip )
15661576 plot)
15671577
1578+ ; ;
15681579; ; ---
15691580
15701581; ; ## Line Charts
@@ -1619,6 +1630,7 @@ tips
16191630 (lay (line {:color :region }))
16201631 plot)
16211632
1633+ ; ;
16221634; ; ---
16231635
16241636; ; ## Layers
@@ -1652,6 +1664,7 @@ tips
16521664 (lay (point {:color :region }) (line {:color :region }))
16531665 plot)
16541666
1667+ ; ;
16551668; ; ---
16561669
16571670; ; ## Regression and Smooth Lines
@@ -1796,6 +1809,7 @@ tips
17961809 (loess {:color :species }))
17971810 plot)
17981811
1812+ ; ;
17991813; ; ---
18001814
18011815; ; ## Categorical Charts
@@ -2078,6 +2092,7 @@ tips
20782092 (lay (value-bar ))
20792093 plot)
20802094
2095+ ; ;
20812096; ; ---
20822097
20832098; ; ## Multi-Panel Layouts
@@ -2191,29 +2206,29 @@ tips
21912206; ; ### ⚙️ Faceting
21922207
21932208(defn facet
2194- " Split each view by a categorical column."
2195- [views col]
2196- (mapcat
2197- (fn [v]
2198- (validate-columns (:data v) :facet col)
2199- (let [groups (tc/group-by (:data v) [col] {:result-type :as-map })]
2200- (map (fn [[gk gds]]
2201- (assoc v :data gds :facet-val (get gk col)))
2202- groups)))
2203- views))
2209+ " Split each view by a categorical column.
2210+ Default layout is a horizontal row of panels.
2211+ Pass :col as direction for a vertical column of panels."
2212+ ([views col] (facet views col :row ))
2213+ ([views col direction]
2214+ (case direction
2215+ :row (facet-grid views nil col)
2216+ :col (facet-grid views col nil ))))
22042217
22052218(defn facet-grid
2206- " Split each view by two categorical columns for a row × column grid."
2219+ " Split each view by two categorical columns for a row × column grid.
2220+ Either column may be nil for a single-dimension facet."
22072221 [views row-col col-col]
22082222 (mapcat
22092223 (fn [v]
2210- (validate-columns (:data v) :facet-row row-col)
2211- (validate-columns (:data v) :facet-col col-col)
2212- (let [groups (tc/group-by (:data v) [row-col col-col] {:result-type :as-map })]
2224+ (when row-col (validate-columns (:data v) :facet-row row-col))
2225+ (when col-col (validate-columns (:data v) :facet-col col-col))
2226+ (let [group-cols (filterv some? [row-col col-col])
2227+ groups (tc/group-by (:data v) group-cols {:result-type :as-map })]
22132228 (map (fn [[gk gds]]
22142229 (assoc v :data gds
2215- :facet-row (get gk row-col)
2216- :facet-col (get gk col-col)))
2230+ :facet-row (if row-col ( get gk row-col) " _ " )
2231+ :facet-col (if col-col ( get gk col-col) " _ " )))
22172232 groups)))
22182233 views))
22192234
@@ -2264,18 +2279,6 @@ tips
22642279 :transform (str " rotate(-90," (- pw 5 ) " ," (/ ph 2 ) " )" )}
22652280 (fmt-name yv)])]))))
22662281
2267- ; ; ### ⚙️ `arrange-panels` `:facet`
2268-
2269- (defmethod arrange-panels :facet [_ ctx]
2270- (let [{:keys [non-ann-views ann-views pw ph facet-vals]} ctx]
2271- (for [[ci fv] (map-indexed vector facet-vals)
2272- :let [fviews (concat (filter #(= fv (:facet-val %)) non-ann-views)
2273- ann-views)]]
2274- [:g {:transform (str " translate(" (* ci pw) " ,0)" )}
2275- (panel-from-ctx ctx fviews :show-y? (zero? ci))
2276- [:text {:x (/ pw 2 ) :y 12 :text-anchor " middle"
2277- :font-size 10 :fill " #333" } (str fv)]])))
2278-
22792282; ; ### ⚙️ `arrange-panels` `:facet-grid`
22802283
22812284(defmethod arrange-panels :facet-grid [_ ctx]
@@ -2290,10 +2293,10 @@ tips
22902293 (panel-from-ctx ctx panel-views
22912294 :show-x? (= ri (dec rows))
22922295 :show-y? (zero? ci))
2293- (when (zero? ri)
2296+ (when (and ( zero? ri) ( not= cv " _ " ) )
22942297 [:text {:x (/ pw 2 ) :y 12 :text-anchor " middle"
22952298 :font-size 10 :fill " #333" } (str cv)])
2296- (when (= ci (dec cols))
2299+ (when (and ( = ci (dec cols)) ( not= rv " _ " ))
22972300 [:text {:x (- pw 5 ) :y (/ ph 2 ) :text-anchor " end"
22982301 :font-size 10 :fill " #333"
22992302 :transform (str " rotate(-90," (- pw 5 ) " ," (/ ph 2 ) " )" )}
@@ -2328,6 +2331,15 @@ tips
23282331 (facet :species )
23292332 plot)
23302333
2334+ ; ; ### 🧪 Vertical Facet
2335+ ; ;
2336+ ; ; Pass `:col` to stack panels vertically instead:
2337+
2338+ (-> (view iris [[:sepal-length :sepal-width ]])
2339+ (lay (point {:color :species }))
2340+ (facet :species :col )
2341+ (plot {:width 400 :height 900 }))
2342+
23312343; ; ### 🧪 Row × Column Faceting
23322344; ;
23332345; ; `facet-grid` maps two columns to rows and columns of panels:
@@ -2404,6 +2416,7 @@ tips
24042416 (facet :cyl )
24052417 plot)
24062418
2419+ ; ;
24072420; ; ---
24082421
24092422; ; ## Scales and Coordinates
@@ -2568,6 +2581,7 @@ tips
25682581 (coord :polar )
25692582 plot)
25702583
2584+ ; ;
25712585; ; ---
25722586
25732587; ; ## Annotations and Text
@@ -2683,6 +2697,7 @@ tips
26832697 (lay (text :species ))))
26842698 plot))
26852699
2700+ ; ;
26862701; ; ---
26872702
26882703; ; ## More Aesthetics
@@ -2705,6 +2720,7 @@ tips
27052720 (lay (point {:color :species :shape :species }))
27062721 plot)
27072722
2723+ ; ;
27082724; ; ---
27092725
27102726; ; ## Interactivity
@@ -2855,6 +2871,7 @@ tips
28552871 (facet :species )
28562872 (plot {:brush true }))
28572873
2874+ ; ;
28582875; ; ---
28592876
28602877; ; ## Edge Cases
@@ -2889,6 +2906,7 @@ tips
28892906 (lay (value-bar ))
28902907 plot)
28912908
2909+ ; ;
28922910; ; ---
28932911
28942912; ; ## Reflection
@@ -3050,4 +3068,5 @@ tips
30503068; ; Feedback is welcome. This work is part of the Scicloj
30513069; ; [Real-World Data dev group](https://scicloj.github.io/docs/community/groups/real-world-data/).
30523070
3071+ ; ;
30533072; ; ---
0 commit comments