-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtransformation-context.ls
212 lines (176 loc) · 6.14 KB
/
transformation-context.ls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
require! \moment
# prelude
{Obj, average, concat-map, drop, each, filter, find, fold, foldr1, gcd, id, keys, map, maximum,
minimum, obj-to-pairs, sort, sum, tail, take, unique, mod, round, sort-by, group-by, floor,
ceiling, mean, sqrt} = require \prelude-ls
{transpose} = require \prelude-extension
Rx = require \rx
io = require \socket.io-client
R = require \ramda
# [Number] -> {mean, sigma, median, length}
summary-statistics = (xs) ->
ys = sort xs
length = ys.length
length1 = length - 1
median = if ys.length % 2 == 0 then mean [ys[floor <| ys.length1/2], ys[ceiling <| ys.length1/2]] else ys[ys.length1/2]
{x, x2} = xs |> fold do
(acc, x) -> {
x: acc.x + x
x2: acc.x2 + x * x
index: acc.index + 1
}
{x: 0, x2: 0, index: 0}
avg = x / xs.length
sigma = sqrt (x2 / xs.length - avg * avg)
{
avg
sigma
median: median
length: xs.length
}
# parse-date :: String -> Date
parse-date = (s) -> new Date s
# today :: a -> Date
today = -> ((moment!start-of \day .format "YYYY-MM-DDT00:00:00.000") + \Z) |> parse-date
# round1 :: Int -> Number -> Number
round1 = (n, x) -->
if n == 0 then Math.round x else (Math.round x * (n = Math.pow 10, n)) / n
# find-precision :: Number -> Int
find-precision = (n) ->
f = (p) ->
if (round1 p, n) == n then
p
else if p > 15 then 16 else f (p + 1)
f 0
# bucketize :: Number -> [Number] -> [Int]
bucketize = (size) ->
s2 = size / 2
p = find-precision size
map (round1 p) . (-> it - it `mod` size) . (+ s2)
# (k -> v -> kv) -> Map k v -> [kv]
fold-obj-to-list = (merger, object) -->
[merger key, value for key, value of object]
# fill-intervals-ints :: [[Number ,Number]] -> Int? [[Number, Number]]
fill-intervals-ints = (list, default-value = 0) ->
x-scale = list |> map (.0)
fill-range do
list
minimum x-scale
maximum x-scale
null
default-value
# fill-range :: [[Number, Number]], Number, Number, Number, Number -> [[Number, Number]]
fill-range = (list, min-x-scale, max-x-scale, x-step, default-value = 0) ->
x-step = x-step or (list
|> map (.0)
|> foldr1 gcd)
[0 to (max-x-scale - min-x-scale) / x-step] |> map (i) ->
x-value = min-x-scale + x-step * i
[, y-value]? = list |> find ([x]) -> x == x-value
[x-value, y-value ? default-value]
# fill-intervals :: [[Number, Number]], Int? -> [[Number, Number]]
fill-intervals = (list, default-value = 0) ->
precision = Math.pow 10, (list |> map find-precision . (.0) |> maximum)
list
|> map -> [(round it.0 * precision), it.1]
|> -> fill-intervals-ints it, default-value
|> map -> [it.0 / precision, it.1]
# to-stacked-area :: (a -> String) -> (a -> Number) -> (a -> Number) -> [a] -> [{key :: String, total :: Number, values :: [[Number, Number, a]]]
# map a result array to the input of stacked-area
to-stacked-area = (key-f, x, y, result) -->
result
|> group-by key-f
|> fold-obj-to-list (key, values) ->
values := values |> map (v) ->
[(x v), (y v), v]
|> sort-by (.0)
key: key
values: values
total: sum . map (.1) <| values
|> sort-by (.total * -1)
# from-web-socket :: String -> Observer -> Subject
from-web-socket = (address, open-observer) ->
if !!window.socket
window.socket.close!
window.socket.destroy!
socket = io do
address
reconnection: true
force-new: true
window.socket = socket
# Rx.Observable.create :: Observer -> (a -> Void)
# observable :: Observable
observable = Rx.Observable.create (observer) ->
if !!open-observer
socket.on \connect, ->
open-observer.on-next!
open-observer.on-completed!
socket.io.on \packet, ({data}?) ->
if !!data
observer.on-next do
name: data.0
data: data.1
socket.on \error, (err) ->
observer.on-error err
socket.on \reconnect_error, (err) ->
observer.on-error err
socket.on \reconnect_failed, ->
observer.on-error new Error 'reconnection failed'
socket.io.on \close, ->
observer.on-completed!
!->
socket.close!
socket.destroy!
observer = Rx.Observer.create (event) ->
if !!socket.connected
socket.emit event.name, event.data
Rx.Subject.create observer, observable
# date-from-object-id :: ObjectId -> Date, where ObjectId :: String
date-from-object-id = (object-id) -> new Date (parse-int (object-id.substring 0, 8), 16) * 1000
# object-id-from-date :: Date -> ObjectId, where ObjectId :: String
object-id-from-date = (date) -> ((floor date.getTime! / 1000).to-string 16) + "0000000000000000"
# credit: https://gist.github.com/Gozala/1697037
# tail-call-optimization :: Function -> Function
tail-call-optimization = (fn) ->
active = null
next-args = null
->
args = null
result = null
next-args := arguments
if not active
active := true
while next-args
args := next-args
next-args := null
result := fn.apply this, args
active := false
result
# all functions defined here are accessible by the transformation code
module.exports = ->
{
bucketize
date-from-object-id
day-to-timestamp: -> it * 86400000
moment
fill-intervals
fill-intervals-ints
fill-range
find-precision
fold-obj-to-list
from-web-socket
object-id-from-date
parse-date
today: today!
to-timestamp: (s) -> (moment (new Date s)).unix! * 1000
transpose
round1
summary-statistics
tco: tail-call-optimization
to-stacked-area
} <<< @window <<< (require \prelude-ls) <<<
highland: require \highland
JSONStream: require \JSONStream
Rx: Rx
R: R
stream: require \stream