Skip to content

Commit 655bf2d

Browse files
committed
Add further revisions
1 parent b083733 commit 655bf2d

1 file changed

Lines changed: 25 additions & 28 deletions

File tree

src/ezmiller/relaunching_tablecloth_time.clj

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
^{:kindly/hide-code true
2-
:clay {:title "Relaunch tablecloth.time: Composability over Abstraction"
2+
:clay {:title "Relaunching tablecloth.time: Composability over Abstraction"
33
:quarto {:author [:ezmiller]
44
:description "A composable approach to time series analysis in Clojure"
55
:draft false
@@ -22,14 +22,12 @@
2222
<figcaption>Half-hourly electricity demand in Victoria, Australia (2012–2014). Each line is one day, phased over the time of day (0 = midnight, 1 = midnight). Colors indicate year.</figcaption>
2323
</figure>")
2424

25-
;; I recently relaunched an old Scicloj project called [tablecloth.time](https://github.com/scicloj/tablecloth.time). The goal of this project was to build a composable
26-
;; extension for time series analysis built on top of
27-
;; [tablecloth](https://scicloj.github.io/tablecloth/). Originally, we
28-
;; had built this project around a dataset index mechanism that was
29-
;; built into tech.ml.dataset, but after that feature was removed in
30-
;; v7, the project required a rethink. This post walks through that
31-
;; rethink and the core primitives today, using the
32-
;; Victoria electricity demand dataset.
25+
;; I recently relaunched the experimental time series processing
26+
;; library [tablecloth.time](https://github.com/scicloj/tablecloth.time)
27+
;; — this time without an index. Turns out that's a feature, not a
28+
;; limitation. Here's why, and a walkthrough of the composable
29+
;; primitives that replace it, using the Victoria electricity demand
30+
;; dataset.
3331

3432
;; ## Why No Index?
3533
;;
@@ -58,13 +56,10 @@
5856
;; The pipeline reads like what it does. This aligns with Clojure's broader preference
5957
;; for explicit, composable operations over hidden magic.
6058
;;
61-
;; The simplicity isn't a compromise. It's the feature.
62-
;;
6359
;; For the full discussion of this design shift, see
6460
;; [Composability Over Abstraction](https://humanscodes.com/tablecloth-time-relaunch)
6561
;; on humanscodes.
66-
67-
;; Now let's dig into this library's primitives and basic functionality.
62+
;;
6863
;; Throughout these examples, `tc` refers to `tablecloth.api`,
6964
;; `tct` refers to `tablecloth.time.api`, and `tctc` refers to
7065
;; `tablecloth.time.column.api`.
@@ -92,11 +87,14 @@
9287
;; manipulation happens, built on dtype-next's vectorized operations.
9388
;;
9489
;; Why does this matter? Because manipulating time data is notoriously fiddly.
95-
;; Java's `java.time` package is powerful but verbose. Working with columns
96-
;; of timestamps — converting, extracting, flooring — typically means writing
97-
;; loops or mapping functions over sequences. tablecloth.time's column API
98-
;; gives you operations that work on entire columns at once, using the same
99-
;; fast, primitive-backed machinery as the rest of tech.ml.dataset.
90+
;; Clojure has excellent time libraries —
91+
;; [tick](https://github.com/juxt/tick),
92+
;; [cljc.java-time](https://github.com/henryw374/cljc.java-time) —
93+
;; that tame java.time's verbosity. But they operate on scalars. Working with
94+
;; columns of timestamps still means mapping functions over sequences.
95+
;; tablecloth.time's column API gives you operations that work on entire
96+
;; columns at once, using the same fast, primitive-backed machinery as the
97+
;; rest of tech.ml.dataset.
10098
;;
10199
;; The building blocks fall into three categories:
102100
;;
@@ -119,16 +117,15 @@
119117
;; Floor timestamps to hour buckets:
120118
(tctc/down-to-nearest (:Time vic-elec) 1 :hours {:zone "UTC"})
121119

122-
;; The key thing to notice: no Clojure seqs, no explicit loops. These
123-
;; operations work on primitive arrays under the hood, just like dtype-next's
124-
;; numeric operations. The result is a column that can be added directly
125-
;; to a dataset.
120+
;; The key thing to notice: these operations work on primitive arrays
121+
;; under the hood, just like dtype-next's numeric operations. The
122+
;; result is a column that can be added directly to a dataset.
126123

127124
;; ## Building Up: add-time-columns
128125
;;
129-
;; With these column-level tools in hand, the dataset-level API is just
130-
;; convenience. `add-time-columns` — the function that most users reach
131-
;; for first — is actually a thin wrapper around the extractors we just saw.
126+
;; With these column-level tools in hand, the dataset-level API is
127+
;; just convenience. A core example is `add-time-columns`. It is just
128+
;; a thin wrapper around the extractors we just saw.
132129
;;
133130
;; Here's what it does internally:
134131
;;
@@ -179,7 +176,7 @@
179176
;; The same pattern scales to different granularities. Here are daily and
180177
;; monthly averages:
181178

182-
;; Daily averages:
179+
;; Daily averages (first 10 days):
183180
(-> vic-elec
184181
(tct/add-time-columns :Time [:year :month :day])
185182
(tc/group-by [:year :month :day])
@@ -188,13 +185,13 @@
188185
(tc/order-by [:year :month :day])
189186
(tc/head 10))
190187

191-
;; Monthly averages:
188+
;; Monthly averages — each bar is a month, colored by year:
192189
(-> vic-elec
193190
(tct/add-time-columns :Time [:year :month])
194191
(tc/group-by [:year :month])
195192
(tc/aggregate {:Demand #(dfn/mean (:Demand %))})
196193
(tc/order-by [:year :month])
197-
(plotly/layer-bar {:=x :month :=y :Demand :=color :year}))
194+
(plotly/layer-bar {:=x :month :=y :Demand :=color :year :=color-type :nominal}))
198195

199196
;; Note that tablecloth.time is just a light layer here. You could do this
200197
;; with tablecloth alone by manually extracting datetime components.

0 commit comments

Comments
 (0)