This is a draft from Stockholm Clojure Café, May 20th 2026.
(ns gis.pedestrian-counts
(:require [tablecloth.api :as tc]
[tablecloth.column.api :as tcc]
[scicloj.plotje.api :as pj]
[tech.v3.datatype.datetime :as datetime]
[scicloj.kindly.v4.kind :as kind]
[clojure.string :as str]))
(def data-path
"data/kaggle/nordcauc-urban-mobility-intelligence/pedestrian_flow_counters.csv.gz")
(def ped-data
(-> data-path
(tc/dataset {:key-fn keyword})))
data/kaggle/nordcauc-urban-mobility-intelligence/pedestrian_flow_counters.csv.gz [60000 23]:
| PD-ST-0001 |
Stockholm |
Sweden |
Norrmalm |
bridge |
infrared_beam |
2023-01-05 18:00:00 |
2023-01-05 |
18 |
Thursday |
1 |
Winter |
False |
59.332037 |
18.049224 |
520 |
331 |
189 |
-1.7 |
0.4 |
low |
False |
good |
| PD-ST-0001 |
Stockholm |
Sweden |
Norrmalm |
bridge |
infrared_beam |
2023-01-06 03:00:00 |
2023-01-06 |
3 |
Friday |
1 |
Winter |
False |
59.332037 |
18.049224 |
68 |
30 |
38 |
-4.5 |
0.0 |
low |
False |
good |
| PD-ST-0001 |
Stockholm |
Sweden |
Norrmalm |
bridge |
infrared_beam |
2023-01-06 04:00:00 |
2023-01-06 |
4 |
Friday |
1 |
Winter |
False |
59.332037 |
18.049224 |
52 |
29 |
23 |
-1.7 |
0.0 |
low |
False |
good |
| PD-ST-0001 |
Stockholm |
Sweden |
Norrmalm |
bridge |
infrared_beam |
2023-01-06 22:00:00 |
2023-01-06 |
22 |
Friday |
1 |
Winter |
False |
59.332037 |
18.049224 |
60 |
35 |
25 |
-3.3 |
0.0 |
low |
False |
good |
| PD-ST-0001 |
Stockholm |
Sweden |
Norrmalm |
bridge |
infrared_beam |
2023-01-08 12:00:00 |
2023-01-08 |
12 |
Sunday |
1 |
Winter |
True |
59.332037 |
18.049224 |
477 |
247 |
230 |
-4.0 |
1.4 |
low |
False |
good |
| PD-ST-0001 |
Stockholm |
Sweden |
Norrmalm |
bridge |
infrared_beam |
2023-01-14 17:00:00 |
2023-01-14 |
17 |
Saturday |
1 |
Winter |
True |
59.332037 |
18.049224 |
415 |
219 |
196 |
-2.6 |
0.0 |
low |
False |
good |
| PD-ST-0001 |
Stockholm |
Sweden |
Norrmalm |
bridge |
infrared_beam |
2023-01-15 01:00:00 |
2023-01-15 |
1 |
Sunday |
1 |
Winter |
True |
59.332037 |
18.049224 |
287 |
170 |
117 |
-4.7 |
0.0 |
low |
False |
good |
| PD-ST-0001 |
Stockholm |
Sweden |
Norrmalm |
bridge |
infrared_beam |
2023-01-15 18:00:00 |
2023-01-15 |
18 |
Sunday |
1 |
Winter |
True |
59.332037 |
18.049224 |
377 |
166 |
211 |
-2.6 |
0.0 |
low |
False |
good |
| PD-ST-0001 |
Stockholm |
Sweden |
Norrmalm |
bridge |
infrared_beam |
2023-01-16 07:00:00 |
2023-01-16 |
7 |
Monday |
1 |
Winter |
False |
59.332037 |
18.049224 |
227 |
124 |
103 |
-3.5 |
1.2 |
low |
False |
good |
| PD-ST-0001 |
Stockholm |
Sweden |
Norrmalm |
bridge |
infrared_beam |
2023-01-21 21:00:00 |
2023-01-21 |
21 |
Saturday |
1 |
Winter |
True |
59.332037 |
18.049224 |
319 |
175 |
144 |
-1.3 |
0.0 |
low |
False |
good |
| … |
… |
… |
… |
… |
… |
… |
… |
… |
… |
… |
… |
… |
… |
… |
… |
… |
… |
… |
… |
… |
… |
… |
| PD-BA-0110 |
Baku |
Azerbaijan |
Khatai |
residential_street |
infrared_beam |
2025-07-11 12:00:00 |
2025-07-11 |
12 |
Friday |
7 |
Summer |
False |
40.409920 |
49.849036 |
71 |
42 |
29 |
32.5 |
0.3 |
low |
False |
good |
| PD-BA-0110 |
Baku |
Azerbaijan |
Khatai |
residential_street |
infrared_beam |
2025-07-14 13:00:00 |
2025-07-14 |
13 |
Monday |
7 |
Summer |
False |
40.409920 |
49.849036 |
67 |
43 |
24 |
35.6 |
0.1 |
low |
False |
good |
| PD-BA-0110 |
Baku |
Azerbaijan |
Khatai |
residential_street |
infrared_beam |
2025-07-16 12:00:00 |
2025-07-16 |
12 |
Wednesday |
7 |
Summer |
False |
40.409920 |
49.849036 |
75 |
45 |
30 |
34.5 |
0.2 |
low |
False |
good |
| PD-BA-0110 |
Baku |
Azerbaijan |
Khatai |
residential_street |
infrared_beam |
2025-07-18 22:00:00 |
2025-07-18 |
22 |
Friday |
7 |
Summer |
False |
40.409920 |
49.849036 |
60 |
34 |
26 |
33.1 |
0.0 |
low |
False |
good |
| PD-BA-0110 |
Baku |
Azerbaijan |
Khatai |
residential_street |
infrared_beam |
2025-07-19 21:00:00 |
2025-07-19 |
21 |
Saturday |
7 |
Summer |
True |
40.409920 |
49.849036 |
334 |
132 |
202 |
35.0 |
0.0 |
low |
False |
good |
| PD-BA-0110 |
Baku |
Azerbaijan |
Khatai |
residential_street |
infrared_beam |
2025-07-22 04:00:00 |
2025-07-22 |
4 |
Tuesday |
7 |
Summer |
False |
40.409920 |
49.849036 |
62 |
41 |
21 |
32.3 |
0.1 |
low |
False |
good |
| PD-BA-0110 |
Baku |
Azerbaijan |
Khatai |
residential_street |
infrared_beam |
2025-07-30 13:00:00 |
2025-07-30 |
13 |
Wednesday |
7 |
Summer |
False |
40.409920 |
49.849036 |
66 |
39 |
27 |
31.3 |
0.0 |
low |
False |
good |
| PD-BA-0110 |
Baku |
Azerbaijan |
Khatai |
residential_street |
infrared_beam |
2025-07-31 15:00:00 |
2025-07-31 |
15 |
Thursday |
7 |
Summer |
False |
40.409920 |
49.849036 |
73 |
28 |
45 |
31.6 |
0.0 |
low |
False |
good |
| PD-BA-0110 |
Baku |
Azerbaijan |
Khatai |
residential_street |
infrared_beam |
2025-08-01 04:00:00 |
2025-08-01 |
4 |
Friday |
8 |
Summer |
False |
40.409920 |
49.849036 |
77 |
31 |
46 |
30.5 |
0.3 |
low |
False |
good |
| PD-BA-0110 |
Baku |
Azerbaijan |
Khatai |
residential_street |
infrared_beam |
2025-08-01 05:00:00 |
2025-08-01 |
5 |
Friday |
8 |
Summer |
False |
40.409920 |
49.849036 |
70 |
40 |
30 |
32.2 |
0.0 |
low |
False |
good |
| PD-BA-0110 |
Baku |
Azerbaijan |
Khatai |
residential_street |
infrared_beam |
2025-08-01 19:00:00 |
2025-08-01 |
19 |
Friday |
8 |
Summer |
False |
40.409920 |
49.849036 |
280 |
139 |
141 |
32.3 |
0.4 |
low |
False |
good |
tech.v3.dataset.impl.dataset.Dataset
(-> ped-data
(tc/select-rows (fn [row]
(-> row :counter_id (= "PD-ST-0001"))))
(tc/select-columns [:hour :total_count_hourly])
pj/pose)
(-> ped-data
(tc/select-rows (fn [row]
(-> row :counter_id (= "PD-ST-0001"))))
(tc/group-by [:hour])
(tc/aggregate {:avg-count (fn [ds]
(-> ds
:total_count_hourly
tcc/mean))})
pj/pose
pj/lay-line
pj/lay-point)
(-> ped-data
(tc/select-rows (fn [row]
(-> row :counter_id (= "PD-ST-0001"))))
(tc/group-by [:season :hour])
(tc/aggregate {:avg-count (fn [ds]
(-> ds
:total_count_hourly
tcc/mean))})
(pj/pose :hour :avg-count {:color :season})
pj/lay-line)
(-> ped-data
(tc/group-by [:counter_id] {:result-type :as-seq})
(->> (sort-by (fn [station-counts]
(-> station-counts
(tc/rows :as-maps)
first
:location_type)))
(map (fn [station-counts]
(-> station-counts
(tc/group-by [:season :hour])
(tc/aggregate {:avg-count (fn [ds]
(-> ds
:total_count_hourly
tcc/mean))})
(pj/pose :hour :avg-count {:color :season})
pj/lay-line
(pj/options {:title (-> station-counts
(tc/rows :as-maps)
first
((juxt :city :district :location_type))
(->> (str/join " ")))}))))))
(
)
(-> ped-data
(tc/group-by [:counter_id] {:result-type :as-seq})
(->> (sort-by (fn [station-counts]
(-> station-counts
(tc/rows :as-maps)
first
:location_type)))
(map (fn [station-counts]
(let [first-row (-> station-counts
(tc/rows :as-maps)
first)
center [(:latitude first-row)
(:longitude first-row)]]
(kind/fragment
[(kind/reagent
['(fn [input-center]
[:div {:style {:height "200px"}
:ref (fn [el]
(let [m (-> js/L
(.map el)
(.setView (clj->js
input-center)
14))]
(-> js/L
.-tileLayer
(.provider "Stadia.AlidadeSmooth")
(.addTo m))
(-> js/L
(.marker (clj->js input-center))
(.addTo m))))}])
center]
;; Note we need to mention the dependency:
{:html/deps [:leaflet]})
(-> station-counts
(tc/group-by [:season :hour])
(tc/aggregate {:avg-count (fn [ds]
(-> ds
:total_count_hourly
tcc/mean))})
(pj/pose :hour :avg-count {:color :season})
pj/lay-line
(pj/options {:title (->> first-row
((juxt :city :district :location_type))
(str/join " "))}))]))))
kind/fragment))