|
18 | 18 | (def ts-elem (. js/document (getElementById "timeseries1")))
|
19 | 19 | (def ts-w (aget ts-elem "offsetWidth"))
|
20 | 20 |
|
21 |
| -#_(def ts-chart (js/Rickshaw.Graph. |
22 |
| - (clj->js {:element ts-elem |
23 |
| - :renderer "bar" |
24 |
| - :width ts-w |
25 |
| - :height 100 |
26 |
| - :series [{:color "steelblue" |
27 |
| - :name "Tweets" |
28 |
| - :data [{:x 100 :y 10} {:x 100 :y 110}]}]}))) |
29 |
| - |
30 |
| -#_(Rickshaw.Graph.Axis.Time. (clj->js {:graph ts-chart})) |
31 |
| -#_(.render ts-chart) |
32 |
| -#_(def hover-detail (Rickshaw.Graph.HoverDetail. (clj->js {:graph ts-chart}))) |
33 |
| - |
34 |
| -(defn random-data [] |
35 |
| - (let [series-data (array (array)) |
36 |
| - random (Rickshaw.Fixtures.RandomData. 150)] |
37 |
| - (dotimes [i 100] (.addData random series-data)) |
38 |
| - series-data)) |
39 |
| - |
40 |
| -;; https://gist.github.com/msgodf/8495781 |
41 | 21 | (def graph-with-legend
|
42 | 22 | (doto
|
43 |
| - (Rickshaw.Graph. (clj->js {:element ts-elem |
44 |
| - :renderer "bar" |
45 |
| - :width ts-w |
46 |
| - :height 100 |
47 |
| - :series [{:color "steelblue" |
48 |
| - :data (nth (random-data) 0) |
49 |
| - :name "Tweets"}]})) |
| 23 | + (Rickshaw.Graph. (clj->js {:element ts-elem :renderer "bar" |
| 24 | + :width ts-w :height 100 |
| 25 | + :series [{:color "steelblue" :data [{:x 1 :y 0}] :name "Tweets"}]})) |
50 | 26 | (.render)))
|
51 | 27 |
|
52 |
| -(defn floor [x] (Math/floor x)) |
| 28 | +(Rickshaw.Graph.Axis.Time. (clj->js {:graph graph-with-legend})) |
| 29 | +(def hover-detail (Rickshaw.Graph.HoverDetail. (clj->js {:graph graph-with-legend :yFormatter #(Math/round %)}))) |
53 | 30 |
|
54 | 31 | (defn date-round [s]
|
55 | 32 | "return function that rounds the provided seconds since epoch down to the nearest time interval s
|
56 | 33 | e.g. (date-round 60) creates a function that takes seconds t and rounds them to the nearest minute"
|
57 |
| - (fn [t] (* s (floor (/ t s))))) |
| 34 | + (fn [t] (* s (Math/floor (/ t s))))) |
58 | 35 |
|
59 | 36 | (def m 60)
|
| 37 | +(def qhr (* 15 m)) |
60 | 38 | (def hr (* 60 m))
|
| 39 | +(def qday (* 6 hr)) |
61 | 40 | (def day (* 24 hr))
|
62 | 41 |
|
63 |
| -(defn grouper [newest oldest] |
| 42 | +(defn grouping-interval [newest oldest] |
64 | 43 | (cond
|
65 |
| - (> (- newest oldest) (* 20 day)) (date-round (* 24 60 60)) ;round by nearest day |
66 |
| - (> (- newest oldest) (* 5 day)) (date-round (* 6 60 60)) ;round by nearest quarter day |
67 |
| - (> (- newest oldest) (* 10 hr)) (date-round (* 60 60)) ;round by nearest hour |
68 |
| - (> (- newest oldest) (* 2 hr)) (date-round (* 15 60)) ;round by nearest quarter hour |
69 |
| - :else (date-round 60))) ;round by nearest minute |
| 44 | + (> (- newest oldest) (* 20 day)) day ;round by nearest day |
| 45 | + (> (- newest oldest) (* 5 day)) qday ;round by nearest quarter day |
| 46 | + (> (- newest oldest) (* 20 hr)) hr ;round by nearest hour |
| 47 | + (> (- newest oldest) (* 4 hr)) qhr ;round by nearest quarter hour |
| 48 | + :else m)) ;round by nearest minute |
| 49 | + |
| 50 | +(defn empty-ts-map [newest oldest interval] |
| 51 | + "generates map with all rounded intervals between oldest and newest, initialized to a count of 0" |
| 52 | + (let [rounder (date-round interval) |
| 53 | + values (range (rounder oldest) (rounder newest) interval)] |
| 54 | + (apply sorted-map-by < (flatten [(interpose 0 values) 0])))) |
| 55 | + |
| 56 | +(defn count-into-map [ts-map k] |
| 57 | + "increment count for time interval" |
| 58 | + (update-in ts-map [k] inc)) |
| 59 | + |
| 60 | +(defn tweet-ts [t] |
| 61 | + "retrieve seconds since epoch from tweet using moment.js" |
| 62 | + (.unix (js/moment. (:created_at t)))) |
70 | 63 |
|
71 | 64 | (defn ts-data [app]
|
72 |
| - (let [state @app |
73 |
| - by-id (util/tweets-by-order :tweets-map :by-id) |
74 |
| - tweets-by-id (by-id state 10000) |
75 |
| - oldest (js/moment. (:created_at (last tweets-by-id))) |
76 |
| - newest (js/moment. (:created_at (first tweets-by-id))) |
77 |
| - rounder (grouper (.unix newest) (.unix oldest)) |
78 |
| - ] |
79 |
| - (print "oldest" (.toLocaleString oldest)) |
80 |
| - (print "oldest unix" (.unix oldest)) |
81 |
| - (print "oldest min rounded unix" (round-min (.unix oldest))) |
82 |
| - (print "oldest min rounded parsed" (.toLocaleString (.unix js/moment (round-min (.unix oldest))))) |
83 |
| - (print "oldest hr rounded unix" (round-hr (.unix oldest))) |
84 |
| - (print "oldest hr rounded parsed" (.toLocaleString (.unix js/moment (round-hr (.unix oldest))))) |
85 |
| - (print "oldest day rounded unix" (round-day (.unix oldest))) |
86 |
| - (print "oldest day rounded parsed" (.toLocaleString (.unix js/moment (round-day (.unix oldest))))) |
87 |
| - (print "oldest rounded unix" (rounder (.unix oldest))) |
88 |
| - (print "oldest rounded parsed" (.toLocaleString (.unix js/moment (rounder (.unix oldest))))) |
89 |
| - (print "---") |
90 |
| - (print "newest" (.toLocaleString newest)) |
91 |
| - (print "newest" (.unix newest)) |
92 |
| - (print "---") |
93 |
| - )) |
| 65 | + "perform time series analysis by counting tweets in even intervals" |
| 66 | + (let [tweets-by-id ((util/tweets-by-order :tweets-map :by-id) @app 10000) |
| 67 | + oldest (tweet-ts (last tweets-by-id)) |
| 68 | + newest (tweet-ts (first tweets-by-id)) |
| 69 | + interval (grouping-interval newest oldest) |
| 70 | + rounder (date-round interval)] |
| 71 | + (reduce count-into-map |
| 72 | + (empty-ts-map newest oldest interval) |
| 73 | + (map #(rounder (tweet-ts %)) tweets-by-id)))) |
| 74 | + |
| 75 | +(defn ts-map-vec [ts-map] |
| 76 | + "creates a vector of maps required by Rickshaw chart" |
| 77 | + (map #(zipmap [:x :y] %)(vec ts-map))) |
94 | 78 |
|
95 | 79 | (defn update [chart app]
|
96 |
| - (ts-data app) |
97 |
| - (aset graph-with-legend "series" "0" "data" (nth (random-data) 0)) |
| 80 | + "update time series chart" |
| 81 | + (aset graph-with-legend "series" "0" "data" (clj->js (ts-map-vec (ts-data app)))) |
98 | 82 | (.update chart))
|
0 commit comments