This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-05-31
Channels
- # aleph (1)
- # announcements (2)
- # beginners (20)
- # calva (44)
- # cider (60)
- # clj-kondo (6)
- # clojure (27)
- # clojure-dev (2)
- # clojure-europe (8)
- # clojure-italy (18)
- # clojure-mexico (5)
- # clojure-nl (61)
- # clojure-spec (12)
- # clojure-uk (101)
- # clojurescript (82)
- # cursive (2)
- # data-science (21)
- # datomic (24)
- # fulcro (19)
- # graalvm (5)
- # hoplon (11)
- # jobs-discuss (35)
- # juxt (7)
- # keechma (6)
- # off-topic (21)
- # pedestal (5)
- # planck (2)
- # qa (43)
- # re-frame (3)
- # reagent (7)
- # reitit (4)
- # rewrite-clj (12)
- # sql (10)
- # testing (4)
- # tools-deps (6)
- # vim (23)
- # xtdb (3)
^ FWIW, We did this in semantic-csv, and it seems to work well. One ns for transducer versions, one for the classic functions (the lib was pre transducers after all).
@tsulej happy to pitch in as well as I bump into bits that I need to change/fix. I'll post what I've been doing with them as well in the next few weeks. It is mostly copypasta from your examples, but I've done some tweaks around handling palettes and keeping them consistent across multiple plots.
@U0525KG62 continuing, could you share how you've cleaned palettes/color configurations?
(def population-colors
(merge
(zipmap ["A1" "A2" "A3" "A4"]
(reverse (color/palette-presets :purples-9)))
(zipmap ["B1" "B2" "C1" "C2" "C3" "C4" "C5" "C6"]
(reverse (color/palette-presets :blues-9)))
(zipmap ["D1" "E1" "E2" "F1" "F1" "G1" "G2" "G3" "H1" "H2" "H3" "I4" "J1"]
(into (color/palette-presets :green-orange-12) (reverse (color/palette-presets :reds-3))))))
;; turn vector of {["date" "population" "A1" "A2"...]} into right
;; shape for stacking area and line charts
(defn stacked-area-data [xf maps]
(into (sorted-map)
(comp (map clean-record)
(mapcat (fn [r]
(let [ks (remove #(= "date" %) (keys r))
fs (map (fn [k]
(fn [m]
{:date (get m "date")
:category k
:value (get m k)})) ks)]
((apply juxt fs) r))))
(x/by-key :category (x/into []))
(map (fn [[k v]] [k (->> v
(map (fn [m] (dissoc m :category)))
(map (juxt :date :value))
(sort-by :date)
vec)]))
xf)
maps))
(defn stacked-area
([title domain colors data]
(let [palette (map colors domain)
legend-spec (reverse (map
(fn [d p]
[:rect d {:color p}])
domain palette))]
(-> (plotb/series [:grid]
[:sarea data {:palette palette}])
(plotb/preprocess-series)
(plotb/update-scale :y :fmt int)
(plotb/add-label :top title {:font-size 24 :font "Open Sans Bold" :margin 36})
(plotb/add-axes :bottom {:ticks {:text-angle 45 :text-align :left :font "Open Sans" :font-size 12}})
(plotb/add-axes :left {:ticks {:font "Open Sans" :font-size 12}})
(plotb/add-label :bottom "Date" {:font-size 24 :margin 48 :font "Open Sans"})
(plotb/add-label :left "Population" {:font-size 24 :margin 36 :font "Open Sans"})
(plotb/add-legend "" legend-spec)
(plotr/render-lattice {:width 1024 :height 768 :background :white}))))
([title colors data]
(stacked-area title (-> data keys) colors data)))
(plot/show (stacked-area "Historic Analysis of A and B codes - absolute numbers"
population-colors
(stacked-area-data (filter (fn [[k _]] (#{"A1" "A2" "B1" "B2"} k)))
(csv->maps "./data/population_counts.csv"))))
most of the code ripped off from and tweaked from your examples. The rest is because maps and seqs are good. 🙂
@tsulej looking at my upcoming work - 2 things I'll have to figure out how to do are categorical heatmaps (basically showing co-occurrence of a with a, a with b, etc) and Sankey charts. I can use my exsiting ways of doing them in the meantime.
categorical heatmaps are not done yet. Sankey... this is not easy one. I left it for later. Probably parallel plot will be earlier.
@tsulej yeah, I know those aren't there atm. Like I said. I've got other ways of doing them atm, but that is what is holding me back from doing it all in cljplot. Everything else has been great so far tho. thx!
that would be super helpful, and might mean that I can get away w/o needing the sankey as I don't need multiple stages for my datavis, and I can use a lattice if I do.
what’s the motivation for cljplot? What’s the advantage over solutions that bridge clojure with vega(-lite)?
like Oz for example
oh, sorry, I just saw the “why?” section
(def population-colors
(merge
(zipmap ["A1" "A2" "A3" "A4"]
(reverse (color/palette-presets :purples-9)))
(zipmap ["B1" "B2" "C1" "C2" "C3" "C4" "C5" "C6"]
(reverse (color/palette-presets :blues-9)))
(zipmap ["D1" "E1" "E2" "F1" "F1" "G1" "G2" "G3" "H1" "H2" "H3" "I4" "J1"]
(into (color/palette-presets :green-orange-12) (reverse (color/palette-presets :reds-3))))))
;; turn vector of {["date" "population" "A1" "A2"...]} into right
;; shape for stacking area and line charts
(defn stacked-area-data [xf maps]
(into (sorted-map)
(comp (map clean-record)
(mapcat (fn [r]
(let [ks (remove #(= "date" %) (keys r))
fs (map (fn [k]
(fn [m]
{:date (get m "date")
:category k
:value (get m k)})) ks)]
((apply juxt fs) r))))
(x/by-key :category (x/into []))
(map (fn [[k v]] [k (->> v
(map (fn [m] (dissoc m :category)))
(map (juxt :date :value))
(sort-by :date)
vec)]))
xf)
maps))
(defn stacked-area
([title domain colors data]
(let [palette (map colors domain)
legend-spec (reverse (map
(fn [d p]
[:rect d {:color p}])
domain palette))]
(-> (plotb/series [:grid]
[:sarea data {:palette palette}])
(plotb/preprocess-series)
(plotb/update-scale :y :fmt int)
(plotb/add-label :top title {:font-size 24 :font "Open Sans Bold" :margin 36})
(plotb/add-axes :bottom {:ticks {:text-angle 45 :text-align :left :font "Open Sans" :font-size 12}})
(plotb/add-axes :left {:ticks {:font "Open Sans" :font-size 12}})
(plotb/add-label :bottom "Date" {:font-size 24 :margin 48 :font "Open Sans"})
(plotb/add-label :left "Population" {:font-size 24 :margin 36 :font "Open Sans"})
(plotb/add-legend "" legend-spec)
(plotr/render-lattice {:width 1024 :height 768 :background :white}))))
([title colors data]
(stacked-area title (-> data keys) colors data)))
(plot/show (stacked-area "Historic Analysis of A and B codes - absolute numbers"
population-colors
(stacked-area-data (filter (fn [[k _]] (#{"A1" "A2" "B1" "B2"} k)))
(csv->maps "./data/population_counts.csv"))))
all very first-ish draft really, but got the job done
most of the code ripped off from and tweaked from your examples. The rest is because maps and seqs are good. 🙂