@explorable-viz/fluid 0.7.85 → 0.7.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fluid/fluid/lib/convolution.fld +31 -0
- package/dist/fluid/fluid/lib/graphics.fld +221 -0
- package/dist/fluid/fluid/lib/prelude.fld +276 -0
- package/dist/fluid/fluid/lib/stats.fld +76 -0
- package/dist/fluid/shared/fluid.mjs +28691 -0
- package/dist/fluid/shared/load-figure.js +44490 -0
- package/dist/fluid/shared/website-test.js +41 -0
- package/dist/fluid/shared/webtest-lib.js +278837 -0
- package/package.json +1 -1
@@ -0,0 +1,31 @@
|
|
1
|
+
let zero m n image =
|
2
|
+
let (m_max, n_max) = dims image
|
3
|
+
in if (m >= 1) `and` (m <= m_max) `and` (n >= 1) `and` (n <= n_max)
|
4
|
+
then image!(m, n)
|
5
|
+
else 0;
|
6
|
+
|
7
|
+
let wrap m n image =
|
8
|
+
let (m_max, n_max) = dims image
|
9
|
+
in image!( ((m - 1) `mod` m_max) + 1, ((n - 1) `mod` n_max) + 1);
|
10
|
+
|
11
|
+
let extend m n image =
|
12
|
+
let (m_max, n_max) = dims image;
|
13
|
+
m' = min (max m 1) m_max;
|
14
|
+
n' = min (max n 1) n_max
|
15
|
+
in image!(m', n');
|
16
|
+
|
17
|
+
let matrixSum matr =
|
18
|
+
let (m, n) = dims matr
|
19
|
+
in sum [ matr!(i, j) | (i, j) <- range (1, 1) (m, n)];
|
20
|
+
|
21
|
+
let convolve image kernel lookup =
|
22
|
+
let ((m, n), (i, j)) = (dims image, dims kernel);
|
23
|
+
(half_i, half_j) = (i `quot` 2, j `quot` 2);
|
24
|
+
area = i * j
|
25
|
+
in [|let interMatrix = [|
|
26
|
+
let x = m' + i' - 1 - half_i;
|
27
|
+
y = n' + j' - 1 - half_j
|
28
|
+
in lookup x y image * kernel!(i', j')
|
29
|
+
| (i', j') in (i, j) |]
|
30
|
+
in matrixSum interMatrix `quot` area
|
31
|
+
| (m', n') in (m, n) |];
|
@@ -0,0 +1,221 @@
|
|
1
|
+
-- typedef Colour = Str
|
2
|
+
-- typedef Colours = List Colour
|
3
|
+
-- typedef Cat = Str
|
4
|
+
|
5
|
+
-- Group has location (0, 0) because it doesn't interfere with positioning of its children.
|
6
|
+
-- GraphicsElement -> Point
|
7
|
+
let coords (Group gs) = Point 0 0;
|
8
|
+
coords (Rect x y _ _ _) = Point x y;
|
9
|
+
coords (String x y _ _ _) = Point x y;
|
10
|
+
coords (Viewport x y _ _ _ _ _ _ _) = Point x y;
|
11
|
+
|
12
|
+
-- GraphicsElement -> Float
|
13
|
+
let get_x g = let Point x _ = coords g in x;
|
14
|
+
|
15
|
+
-- GraphicsElement -> Float
|
16
|
+
let get_y g = let Point _ y = coords g in x;
|
17
|
+
|
18
|
+
-- Want some kind of typeclass mechanism plus record accessors/updaters.
|
19
|
+
-- Float -> GraphicsElement -> GraphicsElement
|
20
|
+
let set_x x (Group gs) = error "Group has immutable coordinates";
|
21
|
+
set_x x (Rect _ y w h fill) = Rect x y w h fill;
|
22
|
+
set_x x (String _ y str anchor baseline) = String x y str anchor baseline;
|
23
|
+
set_x x (Viewport _ y w h fill margin scale translate g) = Viewport x y w h fill margin scale translate g;
|
24
|
+
|
25
|
+
-- (Point, Point) -> Point
|
26
|
+
let dimensions2 (Point x1 y1, Point x2 y2) = Point (max x1 x2) (max y1 y2);
|
27
|
+
|
28
|
+
-- For Group, dimensions are relative to implicit coords of (0, 0), since a Group's children are effectively
|
29
|
+
-- positioned relative to parent of Group. For Polymarker, will probably have to ignore the markers themselves,
|
30
|
+
-- since they are scale-invariant.
|
31
|
+
-- GraphicsElement -> Point
|
32
|
+
let
|
33
|
+
dimensions (Group gs) = foldl (curry dimensions2) (Point 0 0) (map (coords_op) gs);
|
34
|
+
dimensions (Polyline ps _ _) = foldl (curry dimensions2) (Point 0 0) ps;
|
35
|
+
dimensions (Rect _ _ w h _) = Point w h;
|
36
|
+
dimensions (String _ _ _ _ _) = Point 0 0; -- treat text like markers; scale-invariant
|
37
|
+
dimensions (Viewport _ _ w h _ _ _ _ _) = Point w h;
|
38
|
+
|
39
|
+
coords_op g =
|
40
|
+
let (Point x y, Point w h) = prod coords dimensions g in
|
41
|
+
Point (x + w) (y + h);
|
42
|
+
|
43
|
+
-- GraphicsElement -> Float
|
44
|
+
let width g = let Point w _ = dimensions g in w;
|
45
|
+
|
46
|
+
-- GraphicsElement -> Float
|
47
|
+
let height g = let Point _ h = dimensions g in h;
|
48
|
+
|
49
|
+
-- Float -> Float -> List GraphicsElement -> List GraphicsElement
|
50
|
+
let spaceRight z sep gs =
|
51
|
+
zipWith set_x (iterate (length gs) ((+) sep) z) gs;
|
52
|
+
|
53
|
+
-- Bake colour decisions into the library for the time being. Provide two palettes, so we can have two
|
54
|
+
-- different sets of categorical values (e.g. countries and energy types). Palettes from colorbrewer2.org.
|
55
|
+
let colours1 = ["#66c2a5", "#a6d854", "#ffd92f", "#e5c494", "#fc8d62", "#b3b3b3", "#8da0cb", "#e78ac3"];
|
56
|
+
let colours2 = ["#e41a1c", "#377eb8", "#4daf4a", "#984ea3", "#ff7f00", "#ffff33", "#a65628", "#f781bf"];
|
57
|
+
|
58
|
+
-- Compositionality principle: child coords/dimensions are always expressed directly using parent reference
|
59
|
+
-- frame, to avoid depending on content of child, and so are not themselves scaled. Polyline can't be scaled
|
60
|
+
-- directly because it inherits its frame of reference from its parent. For Viewport, margin will shrink the
|
61
|
+
-- available area, possibly to zero, at which point nothing will be rendered.
|
62
|
+
-- Float -> GraphicsElement -> GraphicsElement
|
63
|
+
let scaleToWidth w (Rect x y _ h fill) = Rect x y w h fill;
|
64
|
+
scaleToWidth w (Viewport x y w0 h fill margin (Scale x_scale y_scale) translate g) =
|
65
|
+
let scale = Scale (x_scale * w / w0) y_scale in
|
66
|
+
Viewport x y w h fill margin scale translate g;
|
67
|
+
|
68
|
+
-- Float -> List GraphicsElement -> List GraphicsElement
|
69
|
+
let stackRight sep gs =
|
70
|
+
map (scaleToWidth (1 - sep)) (spaceRight (sep / 2) 1 gs);
|
71
|
+
|
72
|
+
-- Float -> List GraphicsElement -> GraphicsElement
|
73
|
+
let groupRight sep gs =
|
74
|
+
Viewport 0 0 (length gs) (maximum (map height gs)) "none" 0 (Scale 1 1) (Translate 0 0) (Group (stackRight sep gs));
|
75
|
+
|
76
|
+
-- Heuristic saying how often to place a tick on an axis of length n.
|
77
|
+
-- Float -> Float
|
78
|
+
let tickEvery n =
|
79
|
+
let m = floor (logBase 10 n) in
|
80
|
+
if n <= 2 * 10 ** m
|
81
|
+
then 2 * 10 ** (m - 1)
|
82
|
+
else 10 ** m;
|
83
|
+
|
84
|
+
let axisStrokeWidth = 0.5;
|
85
|
+
axisColour = "black";
|
86
|
+
backgroundColour = "white";
|
87
|
+
defaultMargin = 24;
|
88
|
+
markerRadius = 3.5;
|
89
|
+
tickLength = 4;
|
90
|
+
|
91
|
+
-- Helpers for axis functions.
|
92
|
+
-- Orient -> Colour -> Float -> GraphicsElement
|
93
|
+
let tick Horiz colour len = Line (Point 0 0) (Point 0 (0 - len)) colour axisStrokeWidth;
|
94
|
+
tick Vert colour len = Line (Point 0 0) (Point (0 - len) 0) colour axisStrokeWidth;
|
95
|
+
|
96
|
+
-- Orient -> Float -> Float -> Str -> GraphicsElement
|
97
|
+
let label Horiz x distance str = String x (0 - distance - 4) str "middle" "hanging";
|
98
|
+
label Vert x distance str = String (0 - distance) x str "end" "central";
|
99
|
+
|
100
|
+
-- Orient -> Colour -> Float -> Str -> GraphicsElement
|
101
|
+
let labelledTick orient colour len str =
|
102
|
+
Group [tick orient colour len, label orient 0 len str];
|
103
|
+
|
104
|
+
-- Orient -> Float -> Float -> Point
|
105
|
+
let mkPoint Horiz x y = Point y x;
|
106
|
+
mkPoint Vert x y = Point x y;
|
107
|
+
|
108
|
+
-- x is position of this axis on the other axis. Returns axis and position of last tick.
|
109
|
+
-- Orient -> Float -> Float -> Float -> GraphicsElement
|
110
|
+
let axis orient x start end =
|
111
|
+
let tickSp = tickEvery (end - start);
|
112
|
+
firstTick = ceilingToNearest start tickSp;
|
113
|
+
lastTick = ceilingToNearest end tickSp;
|
114
|
+
n = floor ((end - firstTick) / tickSp) + 1;
|
115
|
+
ys = iterate n ((+) tickSp) firstTick;
|
116
|
+
-- avoid redundant start and end points
|
117
|
+
ys = match firstTick > start as {
|
118
|
+
True -> start : ys;
|
119
|
+
False -> ys
|
120
|
+
};
|
121
|
+
ys = match lastTick > end as {
|
122
|
+
True -> concat2 ys [lastTick];
|
123
|
+
False -> ys
|
124
|
+
};
|
125
|
+
ps = map (mkPoint orient x) ys;
|
126
|
+
ax = Group [
|
127
|
+
Line (head ps) (last ps) axisColour axisStrokeWidth,
|
128
|
+
Polymarkers ps (flip map ys (compose (labelledTick orient axisColour tickLength) numToStr))
|
129
|
+
]
|
130
|
+
in (ax, lastTick);
|
131
|
+
|
132
|
+
-- x is position of this axis on the other axis.
|
133
|
+
-- Orient -> Float -> List Cat -> GraphicsElement
|
134
|
+
let catAxis orient x catValues =
|
135
|
+
let ys = iterate (length catValues + 1) ((+) 1) 0;
|
136
|
+
ps = map (mkPoint orient x) ys
|
137
|
+
in Group [
|
138
|
+
Line (head ps) (last ps) axisColour axisStrokeWidth,
|
139
|
+
Polymarkers (tail ps) (map (const (tick orient axisColour tickLength)) catValues),
|
140
|
+
Polymarkers (flip map (tail ps) (fun (Point x y) -> Point (x - 0.5) y)) (map (label orient -0.5 0) catValues)
|
141
|
+
];
|
142
|
+
|
143
|
+
-- Float -> Float -> Float -> Float -> List GraphicsElement -> GraphicsElement
|
144
|
+
let viewport x_start x_finish y_finish margin gs =
|
145
|
+
Viewport 0 0 (x_finish - x_start) y_finish backgroundColour margin
|
146
|
+
(Scale 1 1) (Translate (0 - x_start) 0) (Group gs);
|
147
|
+
|
148
|
+
-- Plot a map of x values to lists of (categorical value, y value) pairs. Importantly, assume all data is uniform
|
149
|
+
-- (categorical keys are the same for each x value and are ordered the same each time).
|
150
|
+
-- Bool -> Colours -> Float -> List (Float, List (Cat, Float)) -> GraphicsElement
|
151
|
+
let lineChart withAxes colours x_start data =
|
152
|
+
let xs = map fst data;
|
153
|
+
nCat = length (snd (head data));
|
154
|
+
-- (Int, Colour) -> GraphicsElement
|
155
|
+
let plot (n, colour) =
|
156
|
+
let ps = map (fun (x, kvs) -> Point x (snd (nth n kvs))) data
|
157
|
+
in Group [
|
158
|
+
Polyline ps colour 1,
|
159
|
+
Polymarkers ps (repeat (length ps) (Circle 0 0 markerRadius colour))
|
160
|
+
];
|
161
|
+
-- List GraphicsElement
|
162
|
+
let lines = zipWith (curry plot) (iterate nCat ((+) 1) 0) colours;
|
163
|
+
x_finish = last xs;
|
164
|
+
y_finish = maximum (flip map data (fun (_, kvs) -> maximum (map snd kvs)))
|
165
|
+
in match withAxes as {
|
166
|
+
True ->
|
167
|
+
let (x_axis, x_finish) = axis Horiz 0 x_start x_finish;
|
168
|
+
(y_axis, y_finish') = axis Vert x_start 0 y_finish
|
169
|
+
in viewport x_start x_finish y_finish' defaultMargin (x_axis : y_axis : lines);
|
170
|
+
False -> viewport x_start x_finish y_finish 0 lines
|
171
|
+
};
|
172
|
+
|
173
|
+
-- Plot a chart of categorical values on the x-axis and renderings of the corresponding a-value on the y-axis.
|
174
|
+
-- (Colours -> List a -> GraphicsElement) -> Bool -> Colours -> Float -> List (Cat, a) -> GraphicsElement
|
175
|
+
let categoricalChart plotValue withAxes colours sep data =
|
176
|
+
let gs = stackRight sep (plotValue colours (map snd data));
|
177
|
+
w = length gs;
|
178
|
+
h = maximum (map height gs)
|
179
|
+
in match withAxes as {
|
180
|
+
True ->
|
181
|
+
let x_axis = catAxis Horiz 0 (map fst data);
|
182
|
+
(y_axis, h') = axis Vert 0 0 h
|
183
|
+
in viewport 0 w h' defaultMargin (concat2 gs [x_axis, y_axis]); -- axes on top
|
184
|
+
False -> viewport 0 w h 0 gs
|
185
|
+
};
|
186
|
+
|
187
|
+
-- Colours -> List a -> GraphicsElement
|
188
|
+
let rects colours ns =
|
189
|
+
zipWith (fun colour n -> Rect 0 0 1 n colour) colours ns;
|
190
|
+
|
191
|
+
-- First component of data (categorical value) currently ignored; values just mapped positionally to colors.
|
192
|
+
-- Can we use Group instead of Viewport here?
|
193
|
+
-- Colours -> List (a, Num) -> GraphicsElement
|
194
|
+
let stackedBar colours ns =
|
195
|
+
let heights = map snd ns;
|
196
|
+
subtotals = scanl1 (+) 0 heights;
|
197
|
+
dims = zip (0 : subtotals) heights;
|
198
|
+
rects = map
|
199
|
+
(fun ((y, height), colour) -> Rect 0 y 1 height colour)
|
200
|
+
(zip dims colours)
|
201
|
+
in Viewport 0 0 1 (last subtotals) "none" 0 (Scale 1 1) (Translate 0 0) (Group rects);
|
202
|
+
|
203
|
+
-- Bool -> Colours -> Float -> List (a, Float) -> GraphicsElement
|
204
|
+
let barChart = categoricalChart rects;
|
205
|
+
|
206
|
+
-- For each categorical value of type a, plot a bar chart for the corresponding b-indexed data.
|
207
|
+
-- Bool -> Colours -> Float -> List (a, List (b, Float)) -> GraphicsElement
|
208
|
+
let groupedBarChart = categoricalChart (compose map (flip (barChart False) 0));
|
209
|
+
|
210
|
+
-- See stackedBar for strong (unjustified) assumption about uniformity of data.
|
211
|
+
-- Bool -> Colours -> Num -> List (a, List (b, Num)) -> GraphicsElement
|
212
|
+
let stackedBarChart = categoricalChart (compose map stackedBar);
|
213
|
+
|
214
|
+
-- Bit of a hack, but how text fits into our model is a bit unclear at the moment.
|
215
|
+
-- Str -> GraphicsElement -> GraphicsElement
|
216
|
+
let caption str (Viewport x y w h fill margin scale translate g) =
|
217
|
+
let g' = Group [
|
218
|
+
String (x + w / 2) -2 str "middle" "hanging",
|
219
|
+
Viewport 0 0 w h fill margin scale translate g
|
220
|
+
]
|
221
|
+
in Viewport x y w h backgroundColour (defaultMargin / 2 + 4) (Scale 1 1) (Translate 0 0) g';
|
@@ -0,0 +1,276 @@
|
|
1
|
+
-- "Num" throughout means (Int + Float).
|
2
|
+
|
3
|
+
-- Bool -> Bool
|
4
|
+
let and False y = False;
|
5
|
+
and True y = y;
|
6
|
+
|
7
|
+
-- Bool -> Bool
|
8
|
+
let or True y = True;
|
9
|
+
or False y = y;
|
10
|
+
|
11
|
+
-- Bool -> Bool
|
12
|
+
let not True = False;
|
13
|
+
not False = True;
|
14
|
+
|
15
|
+
-- Int -> Int -> Ordering
|
16
|
+
let compare x y =
|
17
|
+
if x > y
|
18
|
+
then GT
|
19
|
+
else if x < y
|
20
|
+
then LT
|
21
|
+
else EQ;
|
22
|
+
|
23
|
+
-- Num -> Num
|
24
|
+
let negate = (-) 0;
|
25
|
+
|
26
|
+
-- Log of x in base y.
|
27
|
+
-- Float -> Float -> Float
|
28
|
+
let logBase x y = log y / log x;
|
29
|
+
|
30
|
+
-- Float -> Float -> Float
|
31
|
+
let ceilingToNearest n m =
|
32
|
+
ceiling (n / m) * m;
|
33
|
+
|
34
|
+
-- (b -> c) -> (a -> b) -> a -> c
|
35
|
+
-- Want infix <<<
|
36
|
+
let compose f g x = f (g x);
|
37
|
+
|
38
|
+
-- ((a, b) -> c) -> a -> b -> c
|
39
|
+
let curry f x y = f (x, y);
|
40
|
+
|
41
|
+
-- (a -> b -> c) -> (a, b) -> c
|
42
|
+
let uncurry f (x, y) = f x y;
|
43
|
+
|
44
|
+
-- a -> b -> a
|
45
|
+
let const x _ = x;
|
46
|
+
|
47
|
+
-- (a -> b) -> (a, c) -> (b, c)
|
48
|
+
let first f (a, c) = (f a, c);
|
49
|
+
|
50
|
+
-- (a, b) -> b
|
51
|
+
let snd (_, y) = y;
|
52
|
+
|
53
|
+
-- (a -> b) -> (c, a) -> (c, b)
|
54
|
+
let second f (c, a) = (c, f a);
|
55
|
+
|
56
|
+
-- (a -> b -> c) -> b -> a -> c
|
57
|
+
let flip f x y = f y x;
|
58
|
+
|
59
|
+
-- (a, b) -> a
|
60
|
+
let fst (x, _) = x;
|
61
|
+
|
62
|
+
-- a -> a
|
63
|
+
let id x = x;
|
64
|
+
|
65
|
+
-- (a -> b) -> (a -> c) -> a -> (b, c)
|
66
|
+
-- Want infix &&&
|
67
|
+
let prod f g x = (f x, g x);
|
68
|
+
|
69
|
+
-- (a, b) -> (b, a)
|
70
|
+
let swap (a, b) = (b, a);
|
71
|
+
|
72
|
+
-- List a -> a
|
73
|
+
let head [] = error "Can't take head of empty list";
|
74
|
+
head (x : _) = x;
|
75
|
+
|
76
|
+
-- List a -> List a
|
77
|
+
let tail [] = error "Can't take tail of empty list";
|
78
|
+
tail (_ : xs) = xs;
|
79
|
+
|
80
|
+
-- Eq a => a -> List a -> Bool
|
81
|
+
let elem x [] = False;
|
82
|
+
elem x (x' : xs) = (x == x') `or` (elem x xs);
|
83
|
+
|
84
|
+
-- (a -> Bool) -> List a -> Option a
|
85
|
+
let find p [] = error "not found";
|
86
|
+
find p (x : xs) = if p x then Some x else find p xs;
|
87
|
+
|
88
|
+
-- String -> String -> List Dict -> Option Dict
|
89
|
+
let findWithKey fname fval table = find (fun y -> y.[fname] == fval) table;
|
90
|
+
|
91
|
+
let fromSome option = match option as {
|
92
|
+
None -> error "Expected Some!";
|
93
|
+
Some x -> x
|
94
|
+
};
|
95
|
+
|
96
|
+
-- (a -> Bool) -> List a -> List a
|
97
|
+
let filter p [] = [];
|
98
|
+
filter p (x : xs) =
|
99
|
+
let ys = filter p xs in
|
100
|
+
if p x then x : ys else ys;
|
101
|
+
|
102
|
+
-- (a -> Option b) -> List a -> List b
|
103
|
+
let filterMap p [] = [];
|
104
|
+
filterMap p (x : xs) =
|
105
|
+
match p x as {
|
106
|
+
None -> filterMap f xs;
|
107
|
+
Some y -> y : filterMap f xs
|
108
|
+
};
|
109
|
+
|
110
|
+
-- (a -> b -> a) -> a -> List b -> a
|
111
|
+
let foldl op z [] = z;
|
112
|
+
foldl op z (x : xs) = foldl op (op z x) xs;
|
113
|
+
|
114
|
+
-- (a -> b -> a) -> List b -> a
|
115
|
+
let foldl1 op (x : xs) = foldl op x xs;
|
116
|
+
|
117
|
+
-- (a -> b -> b) -> b -> List a -> b
|
118
|
+
let foldr op z [] = z;
|
119
|
+
foldr op z (x : xs) = op x (foldr op z xs);
|
120
|
+
|
121
|
+
-- (a -> b -> b) -> List a -> b
|
122
|
+
let foldr1 op [x] = x;
|
123
|
+
foldr1 op (x : y : xs) = op x (foldr1 op (y : xs));
|
124
|
+
|
125
|
+
-- (a -> b -> a) -> a -> List b -> List a
|
126
|
+
let scanl1 op z xs =
|
127
|
+
let go x continue acc =
|
128
|
+
let next = op acc x in
|
129
|
+
next : continue next in
|
130
|
+
foldr go (const []) xs z;
|
131
|
+
|
132
|
+
-- (a -> b -> a) -> a -> List b -> List a
|
133
|
+
let scanl op z xs =
|
134
|
+
z : scanl1 op z xs;
|
135
|
+
|
136
|
+
-- (a -> b) -> List a -> List b
|
137
|
+
let map f [] = [];
|
138
|
+
map f (x : xs) = f x : map f xs;
|
139
|
+
|
140
|
+
-- (List a, List a) -> List a
|
141
|
+
let append ([], ys) = ys;
|
142
|
+
append (x : xs, ys) = x : append (xs, ys);
|
143
|
+
|
144
|
+
-- List a -> List -> List a
|
145
|
+
-- Want infix ++
|
146
|
+
let concat2 [] ys = ys;
|
147
|
+
concat2 (x : xs) ys = x : concat2 xs ys;
|
148
|
+
|
149
|
+
-- List (List a) -> List a
|
150
|
+
let concat = foldl concat2 [];
|
151
|
+
|
152
|
+
-- (a -> List b) -> List a -> List b
|
153
|
+
let concatMap f xs = concat (map f xs);
|
154
|
+
|
155
|
+
-- List a -> a -> List a
|
156
|
+
let intersperse [] _ = [];
|
157
|
+
intersperse [x] _ = [x];
|
158
|
+
intersperse (x : y : ys) sep = x : sep : intersperse (y : ys) sep;
|
159
|
+
|
160
|
+
-- Int -> (a -> a) -> a -> List a
|
161
|
+
let iterate n f z =
|
162
|
+
if n == 0 then [] else z : map f (iterate (n - 1) f z);
|
163
|
+
|
164
|
+
-- List Int -> Int
|
165
|
+
let sum = foldr (+) 0;
|
166
|
+
|
167
|
+
-- List a -> a
|
168
|
+
let last [x] = x;
|
169
|
+
last (x : y : ys) = last (y : ys);
|
170
|
+
|
171
|
+
-- List a -> Int
|
172
|
+
let length [] = 0;
|
173
|
+
length (_ : xs) = 1 + length xs;
|
174
|
+
|
175
|
+
-- List a -> List a
|
176
|
+
let reverse [] = [];
|
177
|
+
reverse (x : xs) = append (reverse xs, [x]);
|
178
|
+
|
179
|
+
-- Int -> a -> List a
|
180
|
+
let repeat = flip iterate id;
|
181
|
+
|
182
|
+
-- Int -> List a -> List a
|
183
|
+
let take n xs =
|
184
|
+
if n <= 0
|
185
|
+
then []
|
186
|
+
else match xs as {
|
187
|
+
[] -> [];
|
188
|
+
x : xs -> x : take (n - 1) xs
|
189
|
+
};
|
190
|
+
|
191
|
+
-- Int -> List a -> List a
|
192
|
+
let drop n xs =
|
193
|
+
if n <= 0
|
194
|
+
then xs
|
195
|
+
else match xs as {
|
196
|
+
[] -> [];
|
197
|
+
_ : xs -> drop (n - 1) xs
|
198
|
+
};
|
199
|
+
|
200
|
+
-- Int -> List a -> List a
|
201
|
+
let lastN n xs =
|
202
|
+
foldl (compose const (drop 1)) xs (drop n xs);
|
203
|
+
|
204
|
+
-- Expects non-negative integer as first argument and non-empty list as second argument.
|
205
|
+
-- Int -> List a -> a
|
206
|
+
let nth n (x : xs) =
|
207
|
+
if n == 0 then x else nth (n - 1) xs;
|
208
|
+
|
209
|
+
-- Matrix Int -> Int -> Int -> Int
|
210
|
+
let nth2 i j xss =
|
211
|
+
nth (j - 1) (nth (i - 1) xss);
|
212
|
+
|
213
|
+
-- Partial; requires k to be in the map.
|
214
|
+
-- Int -> List (Int, b) -> b
|
215
|
+
let lookup k [] = error "Key not found in map";
|
216
|
+
lookup k ((k', v) : kvs) =
|
217
|
+
if k == k' then v else lookup k kvs;
|
218
|
+
|
219
|
+
-- Int -> Int -> Int
|
220
|
+
let max n m =
|
221
|
+
if n > m then n else m;
|
222
|
+
|
223
|
+
-- Int -> Int -> Int
|
224
|
+
let min n m =
|
225
|
+
if n < m then n else m;
|
226
|
+
|
227
|
+
-- List Int -> Int
|
228
|
+
let maximum = foldr1 max;
|
229
|
+
|
230
|
+
-- List Int -> Int
|
231
|
+
let minimum = foldr1 min;
|
232
|
+
|
233
|
+
-- List (a, b) -> (List a, List b)
|
234
|
+
let unzip [] = ([], []);
|
235
|
+
unzip ((x, y) : zs) =
|
236
|
+
let (xs, ys) = unzip zs in
|
237
|
+
(x : xs, y : ys);
|
238
|
+
|
239
|
+
-- (a -> b -> c) -> List a -> List b -> List c
|
240
|
+
let zipWith op [] ys = [];
|
241
|
+
zipWith op (x : xs) [] = [];
|
242
|
+
zipWith op (x : xs) (y : ys) = op x y : zipWith op xs ys;
|
243
|
+
|
244
|
+
-- List a -> List b -> List (a, b)
|
245
|
+
let zip = zipWith (curry id);
|
246
|
+
|
247
|
+
-- Int -> Int -> List Int
|
248
|
+
let enumFromTo n m =
|
249
|
+
if n <= m then n : [n + 1 .. m] else [];
|
250
|
+
|
251
|
+
let range (m1, n1) (m2, n2) =
|
252
|
+
[ (i1, i2) | i1 <- [m1 .. m2], i2 <- [n1 .. n2] ];
|
253
|
+
|
254
|
+
-- Int -> Int -> Int
|
255
|
+
let abs x y =
|
256
|
+
if x - y < 0 then negate (x - y) else (x - y);
|
257
|
+
|
258
|
+
-- Eq a => [a] -> [a]
|
259
|
+
let nub xs =
|
260
|
+
let nub' [] _ = [];
|
261
|
+
nub' (x : xs) ys = if x `elem` ys then nub' xs ys else x : nub' xs (x : ys) in
|
262
|
+
nub' xs [];
|
263
|
+
|
264
|
+
-- Int -> Int -> [a] -> [a]
|
265
|
+
let slice begin end xs =
|
266
|
+
take (end - begin) (drop begin xs);
|
267
|
+
|
268
|
+
|
269
|
+
-- (a -> Boolean) -> List a -> (List a, List a)
|
270
|
+
let splitOn p data =
|
271
|
+
let go fls trs [] = (reverse fls, reverse trs);
|
272
|
+
go fls trs (x : xs) =
|
273
|
+
if p x
|
274
|
+
then go fls (x : trs) xs
|
275
|
+
else go (x : fls) trs xs
|
276
|
+
in go [] [] data;
|
@@ -0,0 +1,76 @@
|
|
1
|
+
let split [] = ([], []);
|
2
|
+
split (x : xs) =
|
3
|
+
let (ys, zs) = split xs in (x : zs, ys);
|
4
|
+
|
5
|
+
let merge xs ys =
|
6
|
+
match (xs, ys) as {
|
7
|
+
([], _) -> ys;
|
8
|
+
(x : xs', []) -> xs;
|
9
|
+
(x : xs', y : ys') ->
|
10
|
+
if x < y
|
11
|
+
then x : merge xs' ys
|
12
|
+
else y : merge xs ys'
|
13
|
+
};
|
14
|
+
|
15
|
+
let mergesort xs =
|
16
|
+
if length xs < 2
|
17
|
+
then xs
|
18
|
+
else
|
19
|
+
let (ys, zs) = split xs in
|
20
|
+
merge (mergesort ys) (mergesort zs);
|
21
|
+
|
22
|
+
-- assume data is sorted
|
23
|
+
let findQuantile q p data =
|
24
|
+
let rank = (p / q) * (length data - 1)
|
25
|
+
in if rank == floor rank
|
26
|
+
then nth rank data
|
27
|
+
else let x1 = floor rank;
|
28
|
+
x2 = ceiling rank;
|
29
|
+
left = nth x1 data;
|
30
|
+
right = nth x2 data
|
31
|
+
in left + (rank - x1) * (right - left);
|
32
|
+
|
33
|
+
let findPercentile = findQuantile 100;
|
34
|
+
|
35
|
+
let accumBins data Nil = [];
|
36
|
+
accumBins data [l] = [];
|
37
|
+
accumBins data (l : r : es) =
|
38
|
+
let (ge, le) = splitOn (fun x -> x <= r) data
|
39
|
+
in (le , r - l) : accumBins ge (r : es);
|
40
|
+
|
41
|
+
let cut data nbins =
|
42
|
+
let low = minimum data;
|
43
|
+
binwidth = (maximum data - low) / nbins;
|
44
|
+
edges = [ low + (x * binwidth) | x <- enumFromTo 0 nbins ]
|
45
|
+
in accumBins data edges;
|
46
|
+
|
47
|
+
let qcut data qs =
|
48
|
+
let low = minimum data;
|
49
|
+
high = maximum data;
|
50
|
+
edges = append (low : [ findPercentile x data | x <- qs], [high])
|
51
|
+
in accumBins data edges;
|
52
|
+
|
53
|
+
let likelihoodLE data target =
|
54
|
+
length (filter (fun x -> x <= target) data) / length data;
|
55
|
+
|
56
|
+
let likelihoodGE data target =
|
57
|
+
length (filter (fun x -> x >= target) data) / length data;
|
58
|
+
|
59
|
+
let likelihoodMap table prob = (fromSome (find (fun x -> x.prob <= prob) table)).msg;
|
60
|
+
|
61
|
+
let mkPercent num = (numToStr (num * 100)) ++ "%";
|
62
|
+
|
63
|
+
let leqP n m =
|
64
|
+
if n <= m
|
65
|
+
then "less"
|
66
|
+
else "more";
|
67
|
+
|
68
|
+
let gradedLeqP n m =
|
69
|
+
let ratio = n / m
|
70
|
+
in if ratio <= 1.0
|
71
|
+
then if ratio <=0.5
|
72
|
+
then "much less"
|
73
|
+
else "less"
|
74
|
+
else if ratio >= 2.0
|
75
|
+
then "much more"
|
76
|
+
else "more";
|