6666; ; plotting in Clojure. They were a decent compromise—pragmatic, functional,
6767; ; good enough to be useful. Better designs have been waiting to be explored.
6868; ;
69- ; ; ### Learning from Our Users
69+ ; ; ### Learning from the community
70+
71+ ; ; This post is inspired and affected by past conversations with a few Scicloj-community friends -- in particular:
72+ ; ; Cvetomir Dimov, who helped idevelop Tableplot, Jon Anthony, who created Hanami,
73+ ; ; Kira Howe, who initiated the Scicloj exploration of Grammar-of-Graphics a couple of years ago,
74+ ; ; Harold Hausman, Teodor Heggelond, and most recently, Timoty Pratley, who have had very thoughtful comments about
75+ ; ; the current Tableplot APIs and internals. Many others have affected out thinking about plotting
7076; ;
7177; ; The feedback from Tableplot users has been invaluable. **Thank you** to everyone
7278; ; who took the time to file issues, ask questions, share use cases, and push the
373379 {:=y :bill-depth-mm :=alpha 0.5 })
374380
375381; ; This is why `=*` (our composition operator) can use standard `merge` internally.
382+ ; ; More importantly, it hopefully allows library users to process the structures
383+ ; ; a bit more easily using standard Clojure functions, when they need something
384+ ; ; a bit more flexible than what the library functions offer.
376385
377386; ; # Design Overview
378387; ;
@@ -2477,7 +2486,9 @@ iris
24772486 (mapping :bill-length-mm :bill-depth-mm )
24782487 (scatter )))
24792488
2480- (kind/test-last [#(vector? %)])
2489+ (kind/test-last [#(and (vector? %)
2490+ (string? (first %))
2491+ (clojure.string/includes? (first %) " <svg" ))])
24812492
24822493; ; **Threading macro style (equivalent):**
24832494
@@ -2499,7 +2510,9 @@ iris
24992510 (scatter )
25002511 plot)
25012512
2502- (kind/test-last [#(vector? %)])
2513+ (kind/test-last [#(and (vector? %)
2514+ (string? (first %))
2515+ (clojure.string/includes? (first %) " <svg" ))])
25032516
25042517; ; Both forms produce identical results. The threading style is often more
25052518; ; natural in Clojure, while the `=*` style makes the composition operator explicit.
@@ -2547,7 +2560,9 @@ iris
25472560 (mapping :x :y )
25482561 (scatter )))
25492562
2550- (kind/test-last [#(vector? %)])
2563+ (kind/test-last [#(and (vector? %)
2564+ (string? (first %))
2565+ (clojure.string/includes? (first %) " <svg" ))])
25512566
25522567; ; **Vector of maps** (row-oriented data):
25532568; ; Inspect the spec:
@@ -2575,7 +2590,9 @@ iris
25752590 (mapping :x :y )
25762591 (scatter )))
25772592
2578- (kind/test-last [#(vector? %)])
2593+ (kind/test-last [#(and (vector? %)
2594+ (string? (first %))
2595+ (clojure.string/includes? (first %) " <svg" ))])
25792596
25802597; ; **What happens here**:
25812598
@@ -2612,7 +2629,9 @@ iris
26122629 (scatter )
26132630 plot)
26142631
2615- (kind/test-last [#(vector? %)])
2632+ (kind/test-last [#(and (vector? %)
2633+ (string? (first %))
2634+ (clojure.string/includes? (first %) " <svg" ))])
26162635
26172636; ; **With color aesthetic**:
26182637; ; Inspect the spec:
@@ -2632,7 +2651,9 @@ iris
26322651 (scatter )
26332652 plot)
26342653
2635- (kind/test-last [#(vector? %)])
2654+ (kind/test-last [#(and (vector? %)
2655+ (string? (first %))
2656+ (clojure.string/includes? (first %) " <svg" ))])
26362657
26372658; ; ### 🧪 Example 2c: Semi-Transparent Points
26382659; ;
@@ -2656,7 +2677,9 @@ iris
26562677 (scatter {:alpha 0.5 })
26572678 plot)
26582679
2659- (kind/test-last [#(vector? %)])
2680+ (kind/test-last [#(and (vector? %)
2681+ (string? (first %))
2682+ (clojure.string/includes? (first %) " <svg" ))])
26602683
26612684; ; **Combining attributes with aesthetics**:
26622685; ; Inspect the spec:
@@ -2676,7 +2699,9 @@ iris
26762699 (scatter {:alpha 0.7 })
26772700 plot)
26782701
2679- (kind/test-last [#(vector? %)])
2702+ (kind/test-last [#(and (vector? %)
2703+ (string? (first %))
2704+ (clojure.string/includes? (first %) " <svg" ))])
26802705
26812706; ; ### 🧪 Example 2d: Scale Customization
26822707; ;
@@ -2702,7 +2727,9 @@ iris
27022727 (scale :y {:domain [12 23 ]})
27032728 plot)
27042729
2705- (kind/test-last [#(vector? %)])
2730+ (kind/test-last [#(and (vector? %)
2731+ (string? (first %))
2732+ (clojure.string/includes? (first %) " <svg" ))])
27062733
27072734; ; **Works with plain data too**:
27082735; ; Inspect the spec:
@@ -2724,7 +2751,9 @@ iris
27242751 (scatter )
27252752 plot)
27262753
2727- (kind/test-last [#(vector? %)])
2754+ (kind/test-last [#(and (vector? %)
2755+ (string? (first %))
2756+ (clojure.string/includes? (first %) " <svg" ))])
27282757
27292758; ; **What's happening under the hood**:
27302759; ;
@@ -3019,7 +3048,9 @@ iris
30193048 (linear ))
30203049 plot)
30213050
3022- (kind/test-last [#(vector? %)])
3051+ (kind/test-last [#(and (vector? %)
3052+ (string? (first %))
3053+ (clojure.string/includes? (first %) " <svg" ))])
30233054
30243055; ; ### 🧪 Example 5: Grouped Linear Regression (Color Aesthetic)
30253056
@@ -3049,7 +3080,9 @@ iris
30493080 (linear ))
30503081 plot)
30513082
3052- (kind/test-last [#(vector? %)])
3083+ (kind/test-last [#(and (vector? %)
3084+ (string? (first %))
3085+ (clojure.string/includes? (first %) " <svg" ))])
30533086
30543087; ; **What happens here**:
30553088
@@ -3327,7 +3360,9 @@ iris
33273360 (histogram {:bins 15 })
33283361 plot)
33293362
3330- (kind/test-last [#(vector? %)])
3363+ (kind/test-last [#(and (vector? %)
3364+ (string? (first %))
3365+ (clojure.string/includes? (first %) " <svg" ))])
33313366
33323367; ; ### 🧪 Histogram Binning Methods
33333368
@@ -3349,7 +3384,9 @@ iris
33493384 (histogram {:bins :sqrt })
33503385 plot)
33513386
3352- (kind/test-last [#(vector? %)])
3387+ (kind/test-last [#(and (vector? %)
3388+ (string? (first %))
3389+ (clojure.string/includes? (first %) " <svg" ))])
33533390
33543391; ; # Grouping & Color
33553392; ;
@@ -3381,7 +3418,9 @@ iris
33813418 (linear ))
33823419 plot)
33833420
3384- (kind/test-last [#(vector? %)])
3421+ (kind/test-last [#(and (vector? %)
3422+ (string? (first %))
3423+ (clojure.string/includes? (first %) " <svg" ))])
33853424
33863425; ; **What happens here**:
33873426
@@ -3411,7 +3450,9 @@ iris
34113450 (histogram )
34123451 plot)
34133452
3414- (kind/test-last [#(vector? %)])
3453+ (kind/test-last [#(and (vector? %)
3454+ (string? (first %))
3455+ (clojure.string/includes? (first %) " <svg" ))])
34153456
34163457; ; **What happens here**:
34173458
@@ -3447,7 +3488,9 @@ iris
34473488 (linear ))
34483489 plot)
34493490
3450- (kind/test-last [#(vector? %)])
3491+ (kind/test-last [#(and (vector? %)
3492+ (string? (first %))
3493+ (clojure.string/includes? (first %) " <svg" ))])
34513494
34523495; ; **What happens here**:
34533496
@@ -3487,7 +3530,9 @@ iris
34873530 (linear ))
34883531 plot)
34893532
3490- (kind/test-last [#(vector? %)])
3533+ (kind/test-last [#(and (vector? %)
3534+ (string? (first %))
3535+ (clojure.string/includes? (first %) " <svg" ))])
34913536
34923537; ; **What happens here**:
34933538
@@ -3517,7 +3562,9 @@ iris
35173562 (linear ))
35183563 plot)
35193564
3520- (kind/test-last [#(vector? %)])
3565+ (kind/test-last [#(and (vector? %)
3566+ (string? (first %))
3567+ (clojure.string/includes? (first %) " <svg" ))])
35213568
35223569; ; **What happens here**:
35233570
@@ -3660,7 +3707,9 @@ iris
36603707 (scatter )
36613708 (facet {:col :species })))
36623709
3663- (kind/test-last [#(vector? %)])
3710+ (kind/test-last [#(and (vector? %)
3711+ (string? (first %))
3712+ (clojure.string/includes? (first %) " <svg" ))])
36643713
36653714; ; Faceted histogram - per-species histograms with shared scales:
36663715
@@ -3692,7 +3741,9 @@ iris
36923741 (scatter )
36933742 (facet {:row :species })))
36943743
3695- (kind/test-last [#(vector? %)])
3744+ (kind/test-last [#(and (vector? %)
3745+ (string? (first %))
3746+ (clojure.string/includes? (first %) " <svg" ))])
36963747
36973748; ; ### 🧪 Example 12: Row × Column Grid Faceting
36983749; ;
@@ -3718,7 +3769,9 @@ iris
37183769 (scatter )
37193770 (facet {:row :island :col :sex })))
37203771
3721- (kind/test-last [#(vector? %)])
3772+ (kind/test-last [#(and (vector? %)
3773+ (string? (first %))
3774+ (clojure.string/includes? (first %) " <svg" ))])
37223775
37233776; ; **What happens here**:
37243777
@@ -3771,7 +3824,9 @@ iris
37713824 (facet {:col :island })
37723825 plot)
37733826
3774- (kind/test-last [#(vector? %)])
3827+ (kind/test-last [#(and (vector? %)
3828+ (string? (first %))
3829+ (clojure.string/includes? (first %) " <svg" ))])
37753830
37763831; ; **What happens here**:
37773832; ;
@@ -3808,7 +3863,9 @@ iris
38083863 (scatter )
38093864 (scale :y {:domain [0 40 ]})))
38103865
3811- (kind/test-last [#(vector? %)])
3866+ (kind/test-last [#(and (vector? %)
3867+ (string? (first %))
3868+ (clojure.string/includes? (first %) " <svg" ))])
38123869
38133870; ; **What happens here**:
38143871
@@ -4378,7 +4435,9 @@ iris
43784435 (target :vl )
43794436 plot)
43804437
4381- (kind/test-last [#(map? %)])
4438+ (kind/test-last [#(and (map? %)
4439+ (contains? % :layer )
4440+ (= (count (:layer %)) 2 ))])
43824441
43834442; ; **What happens here**:
43844443
@@ -4410,7 +4469,9 @@ iris
44104469 (target :vl )
44114470 plot)
44124471
4413- (kind/test-last [#(map? %)])
4472+ (kind/test-last [#(and (map? %)
4473+ (contains? % :layer )
4474+ (>= (count (:layer %)) 2 ))])
44144475
44154476; ; **What happens here**:
44164477
@@ -4457,7 +4518,9 @@ iris
44574518 (target :vl )
44584519 plot)
44594520
4460- (kind/test-last [#(map? %)])
4521+ (kind/test-last [#(and (map? %)
4522+ (contains? % :facet )
4523+ (contains? % :spec ))])
44614524
44624525; ; **What happens here**:
44634526
@@ -4534,7 +4597,8 @@ iris
45344597 (target :vl )
45354598 plot)
45364599
4537- (kind/test-last [#(map? %)])
4600+ (kind/test-last [#(and (map? %)
4601+ (contains? % :encoding ))])
45384602
45394603; ; **What happens here**:
45404604
@@ -4624,7 +4688,9 @@ iris
46244688 (target :plotly )
46254689 plot)
46264690
4627- (kind/test-last [#(map? %)])
4691+ (kind/test-last [#(and (map? %)
4692+ (contains? % :data )
4693+ (>= (count (:data %)) 2 ))])
46284694
46294695; ; **What happens here**:
46304696
@@ -4656,7 +4722,9 @@ iris
46564722 (target :plotly )
46574723 plot)
46584724
4659- (kind/test-last [#(map? %)])
4725+ (kind/test-last [#(and (map? %)
4726+ (contains? % :data )
4727+ (>= (count (:data %)) 2 ))])
46604728
46614729; ; **What happens here**:
46624730
@@ -4766,7 +4834,9 @@ iris
47664834 (target :plotly )
47674835 plot)
47684836
4769- (kind/test-last [#(map? %)])
4837+ (kind/test-last [#(and (map? %)
4838+ (contains? % :layout )
4839+ (get-in % [:layout :xaxis :range ]))])
47704840
47714841; ; **What happens here**:
47724842
@@ -4804,7 +4874,9 @@ iris
48044874 (size 800 600 )
48054875 plot)
48064876
4807- (kind/test-last [#(map? %)])
4877+ (kind/test-last [#(and (map? %)
4878+ (contains? % :facet )
4879+ (contains? % :spec ))])
48084880
48094881; ; **What happens here**:
48104882
@@ -5227,4 +5299,4 @@ iris
52275299; ; this is a design exploration—a snapshot of our process, shared in the spirit of
52285300; ; collaborative development.
52295301; ;
5230- ; ; *Feedback welcome. Let's see where this goes together.*
5302+ ; ; *Feedback welcome. Let's see where this goes together.*
0 commit comments