@@ -139,6 +139,34 @@ penguins
139139 :fill " lightblue"
140140 :stroke " gray"
141141 :stroke-width 0.5 }]))))
142+ :heatmap (let [x-vals (remove nil? (data x))
143+ y-vals (remove nil? (data y))
144+ x-cats (distinct x-vals)
145+ y-cats (distinct y-vals)
146+ x-count (count x-cats)
147+ y-count (count y-cats)
148+ ; ; Build contingency table
149+ contingency (reduce (fn [acc i]
150+ (assoc-in acc [(data y i) (data x i)]
151+ (inc (get-in acc [(data y i) (data x i)] 0 ))))
152+ {} (range (tc/row-count data)))
153+ all-counts (for [y-cat y-cats x-cat x-cats] (get-in contingency [y-cat x-cat] 0 ))
154+ max-count (when (seq all-counts) (apply max all-counts))
155+ cell-width (/ plot-width x-count)
156+ cell-height (/ plot-height y-count)]
157+ (when (and max-count (> max-count 0 ))
158+ (for [[y-idx y-cat] (map-indexed vector y-cats)
159+ [x-idx x-cat] (map-indexed vector x-cats)]
160+ (let [count (get-in contingency [y-cat x-cat] 0 )
161+ intensity (/ count max-count)
162+ color (str " rgba(70, 130, 180, " intensity " )" )]
163+ [:rect {:x (* x-idx cell-width)
164+ :y (* y-idx cell-height)
165+ :width cell-width
166+ :height cell-height
167+ :fill color
168+ :stroke " gray"
169+ :stroke-width 0.5 }]))))
142170 :histogram (let [values (remove nil? (data x))
143171 hist-result (when (seq values) (fms/histogram values))
144172 bins (:bins-maps hist-result)]
@@ -228,14 +256,16 @@ penguins
228256 (let [type-a (column-general-type col-a)
229257 type-b (column-general-type col-b)]
230258 (cond
231- ; ; Same column (diagonal) → single column viz
232- (= col-a col-b) ( select-geometry-single col-a)
259+ ; ; Same column (diagonal) → sparse identity plot
260+ (= col-a col-b) :identity
233261 ; ; Quantitative × Quantitative → scatter plot reveals correlation
234262 (and (= :quantitative type-a) (= :quantitative type-b)) :point
235263 ; ; Temporal × Quantitative → line chart shows time series
236264 (and (= :temporal type-a) (= :quantitative type-b)) :line
237265 (and (= :quantitative type-a) (= :temporal type-b)) :line
238- ; ; Categorical × Anything → bar chart (show distribution by category)
266+ ; ; Categorical × Categorical → heatmap shows contingency
267+ (and (= :categorical type-a) (= :categorical type-b)) :heatmap
268+ ; ; Categorical × Anything else → bar chart (show distribution by category)
239269 (or (= :categorical type-a) (= :categorical type-b)) :bar
240270 ; ; Fallback
241271 :else :bar )))
@@ -410,3 +440,51 @@ penguins
410440 :geometry [geom]}])]))]]))
411441
412442(matrix penguins)
443+
444+
445+ (defn labeled-matrix
446+ " Create a scatterplot-matrix-style view with row/column labels.
447+ Each cell uses an appropriate visualization based on the column types."
448+ [ds]
449+ (let [column-names (tc/column-names ds)
450+ c (count column-names)
451+ label-width 80
452+ cell-size plot-width]
453+ ^:kind/hiccup
454+ [:svg {:width " 100%"
455+ :viewBox (str/join " " [0 0 (+ label-width (* cell-size c)) (+ label-width (* cell-size c))])
456+ :xmlns " http://www.w3.org/2000/svg"
457+ :style {:border " solid 1px gray" }}
458+ [:g
459+ ; ; Column labels (top)
460+ (for [[idx col-name] (map-indexed vector column-names)]
461+ [:g {:key (str " col-label-" idx)}
462+ [:text {:x (+ label-width (* idx cell-size) (/ cell-size 2 ))
463+ :y 20
464+ :text-anchor " middle"
465+ :font-size " 12"
466+ :fill " #333" } (name col-name)]])
467+
468+ ; ; Row labels (left)
469+ (for [[idx row-name] (map-indexed vector column-names)]
470+ [:g {:key (str " row-label-" idx)}
471+ [:text {:x 10
472+ :y (+ label-width (* idx cell-size) (/ cell-size 2 ) 4 )
473+ :font-size " 12"
474+ :fill " #333" } (name row-name)]])
475+
476+ ; ; Grid cells
477+ (for [[a-idx a] (map-indexed vector column-names)
478+ [b-idx b] (map-indexed vector column-names)]
479+ (let [col-a (ds a)
480+ col-b (ds b)
481+ geom (select-geometry-pair col-a col-b)]
482+ [:g {:key (str " cell-" a-idx " -" b-idx)
483+ :transform (str " translate(" (+ label-width (* a-idx cell-size)) " ," (+ label-width (* b-idx cell-size)) " )" )}
484+ [:rect {:x 0 :y 0 :width cell-size :height cell-size
485+ :fill " none" :stroke " gray" :stroke-width 1 }]
486+ (plot-basic [:graphic {:data ds
487+ :mappings {:x a :y b}
488+ :geometry [geom]}])]))]]))
489+
490+ (labeled-matrix penguins)
0 commit comments