15 Change Over Time
Line charts and their variants β showing change over a sequence.
(ns plotje-book.change-over-time
(:require
;; Tablecloth -- dataset manipulation
[tablecloth.api :as tc]
;; Kindly -- notebook rendering protocol
[scicloj.kindly.v4.kind :as kind]
;; Plotje -- composable plotting
[scicloj.plotje.api :as pj]))Line
Connected line through data points.
(def wave {:x (range 30)
:y (map #(Math/sin (* % 0.3)) (range 30))})(-> wave
(pj/lay-line :x :y))Grouped Lines
Color separates multiple series.
(def waves {:x (concat (range 30) (range 30))
:y (concat (map #(Math/sin (* % 0.3)) (range 30))
(map #(Math/cos (* % 0.3)) (range 30)))
:fn (concat (repeat 30 :sin) (repeat 30 :cos))})(-> waves
(pj/lay-line :x :y {:color :fn}))Thick Line
Constant stroke width via :size.
(-> wave
(pj/lay-line :x :y {:size 4}))Line with Points
Overlay points on a grouped line plot.
(def growth
{:day [1 2 3 4 5 1 2 3 4 5]
:value [10 15 13 18 22 8 12 11 16 19]
:group [:a :a :a :a :a :b :b :b :b :b]})(-> growth
(pj/pose :day :value {:color :group})
pj/lay-line
pj/lay-point)Step
Horizontal-then-vertical connected points.
(-> {:x [1 2 3 4 5]
:y [2 4 1 5 3]}
(pj/lay-step :x :y)
pj/lay-point)Step by Group
Grouped step lines.
(-> growth
(pj/pose :day :value {:color :group})
pj/lay-step
pj/lay-point)Stacked Step
Each group stacks above the previous, with horizontal-then-vertical segments.
(-> {:x (concat (range 5) (range 5) (range 5))
:y (concat [1 2 3 4 5] [2 2 2 2 2] [3 1 2 1 2])
:group (concat (repeat 5 "A") (repeat 5 "B") (repeat 5 "C"))}
(pj/lay-step :x :y {:position :stack :color :group}))Area
Filled area under a line.
(-> {:x (range 30)
:y (map #(Math/sin (* % 0.3)) (range 30))}
(pj/lay-area :x :y))Stacked Area
Each group fills above the previous.
(-> {:x (concat (range 10) (range 10) (range 10))
:y (concat [1 2 3 4 5 4 3 2 1 0]
[2 2 2 3 3 3 2 2 2 2]
[1 1 1 1 2 2 2 1 1 1])
:group (concat (repeat 10 "A") (repeat 10 "B") (repeat 10 "C"))}
(pj/lay-area :x :y {:position :stack :color :group}))Dates on the x-axis
Real time-series usually have an actual date column, not an integer step. Plotje detects temporal columns (java.util.Date via #inst, java.time.LocalDate, LocalDateTime, Instant) and picks calendar-aware tick labels automatically.
(-> {:date [#inst "2024-01-01" #inst "2024-02-01" #inst "2024-03-01"
#inst "2024-04-01" #inst "2024-05-01" #inst "2024-06-01"]
:temperature [3 5 9 14 19 23]}
(pj/lay-line :date :temperature)
pj/lay-point)Multiple Series Over Time
Pass {:color :group} to get one line per category. Rows are drawn in their given order, so pre-sort by date if your data is not already sorted.
(def months
[#inst "2024-01-01" #inst "2024-02-01" #inst "2024-03-01"
#inst "2024-04-01" #inst "2024-05-01" #inst "2024-06-01"])(-> {:date (concat months months)
:temperature [3 5 9 14 19 23
15 17 19 22 25 28]
:city (concat (repeat 6 "Zurich")
(repeat 6 "Athens"))}
(pj/lay-line :date :temperature {:color :city})
pj/lay-point)Area Over Dates
Filled area also works on a date axis β useful for cumulative metrics where the volume below the curve carries meaning.
(-> {:date [#inst "2024-01-01" #inst "2024-02-01" #inst "2024-03-01"
#inst "2024-04-01" #inst "2024-05-01" #inst "2024-06-01"]
:sales [10 25 30 22 35 40]}
(pj/lay-area :date :sales))See Inference Rules for details on how dates are detected and formatted.
Zero-Line Baseline
Time series with positive and negative values often benefit from a horizontal reference at zero, drawn with pj/lay-rule-h. The rule takes its position from the options map β not from a data column β so itβs an annotation, not a data layer.
(-> {:t (range 12)
:delta [-3 -1 -2 0 2 4 -1 3 5 -2 1 4]}
(pj/lay-line :t :delta)
pj/lay-point
(pj/lay-rule-h {:y-intercept 0 :color "#888"}))Whatβs Next
- Timelines β events, intervals, and schedules on a time axis (Gantt charts, Marey diagrams, annotated time series)
- Relationships β heatmaps, contours, and 2D density
- Polar Coordinates β radial charts for cyclical data
- Gallery β more chart variations with side-by-side code