Skip to content

Commit d1781d2

Browse files
Merge pull request #282 from ClojureCivitas/column-combos
add labeled matrix
2 parents 5443482 + d6cc31e commit d1781d2

File tree

1 file changed

+81
-3
lines changed

1 file changed

+81
-3
lines changed

src/data_visualization/aog/column_combinations.clj

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)