A Nix-friendly SQLite-enhanced fork of Flitter, a speedrunning split timer for Unix-style terminals
Révision | 11b06680b1e995070318ab534081c7cd1598e21d (tree) |
---|---|
l'heure | 2022-12-21 08:06:10 |
Auteur | Corbin <cds@corb...> |
Commiter | Corbin |
Factor out Welford's algorithm.
@@ -7,33 +7,16 @@ type t = { | ||
7 | 7 | splits : (string * Duration.t list) array; |
8 | 8 | } |
9 | 9 | |
10 | -type welford = { n : int; m1 : float; m2 : float } | |
11 | - | |
12 | -let do_welford = | |
13 | - List.fold | |
14 | - ~f:(fun { n; m1; m2 } d -> | |
15 | - let delta = Float.of_int d -. m1 in | |
16 | - let dn = delta /. Float.of_int (n + 1) in | |
17 | - let t = delta *. dn *. Float.of_int n in | |
18 | - { n = n + 1; m1 = m1 +. dn; m2 = m2 +. t }) | |
19 | - ~init:{ n = 0; m1 = 0.0; m2 = 0.0 } | |
20 | - | |
21 | -let mean { m1; _ } = m1 | |
22 | - | |
23 | -let variance { n; m2; _ } = | |
24 | - if n < 2 then None else Some (m2 /. Float.of_int (n - 1)) | |
25 | - | |
26 | -let stddev s = Option.value_map (variance s) ~default:0.0 ~f:Float.sqrt | |
27 | 10 | let best = List.fold ~f:Int.min ~init:Int.max_value |
28 | 11 | |
29 | 12 | let print_summary_segment width segment times = |
30 | - let w = do_welford times in | |
13 | + let w = Welford.summarize_list times in | |
31 | 14 | (* NB: 12 digits is a reasonable width for durations; it would take over a |
32 | 15 | week for a segment to surpass it! *) |
33 | 16 | Printf.printf "%*s: %12s %12s %12s (best/mean/stddev)\n" (width + 2) segment |
34 | 17 | (Duration.to_string (best times) 3) |
35 | - (Duration.to_string (Float.to_int (mean w)) 3) | |
36 | - (Duration.to_string (Float.to_int (stddev w)) 3) | |
18 | + (Duration.to_string (Float.to_int (Welford.mean w)) 3) | |
19 | + (Duration.to_string (Float.to_int (Welford.stddev w)) 3) | |
37 | 20 | |
38 | 21 | let print_route (route : Route.t) = |
39 | 22 | Printf.printf "Title: %s Category: %s" route.title route.category; |
@@ -0,0 +1,20 @@ | ||
1 | +open Core | |
2 | + | |
3 | +type t = { n : int; m1 : float; m2 : float } | |
4 | + | |
5 | +let empty = { n = 0; m1 = 0.0; m2 = 0.0 } | |
6 | + | |
7 | +let observe { n; m1; m2 } d = | |
8 | + let delta = Float.of_int d -. m1 in | |
9 | + let dn = delta /. Float.of_int (n + 1) in | |
10 | + let t = delta *. dn *. Float.of_int n in | |
11 | + { n = n + 1; m1 = m1 +. dn; m2 = m2 +. t } | |
12 | + | |
13 | +let summarize_list = List.fold ~f:observe ~init:empty | |
14 | +let summarize_array = Array.fold ~f:observe ~init:empty | |
15 | +let mean { m1 } = m1 | |
16 | + | |
17 | +let variance { n; m2 } = | |
18 | + if n < 2 then None else Some (m2 /. Float.of_int (n - 1)) | |
19 | + | |
20 | +let stddev s = Option.value_map (variance s) ~default:0.0 ~f:Float.sqrt |
@@ -0,0 +1,28 @@ | ||
1 | +(* Welford's algorithm takes the mean and variance of a stream of inputs. This | |
2 | + type holds a summary of observed inputs. *) | |
3 | +type t | |
4 | + | |
5 | +(* A summary with no observations. *) | |
6 | +val empty : t | |
7 | + | |
8 | +(* Observe an input. *) | |
9 | +val observe : t -> int -> t | |
10 | + | |
11 | +(* Observe a list of inputs. *) | |
12 | +val summarize_list : int list -> t | |
13 | + | |
14 | +(* Observe an array of inputs. *) | |
15 | +val summarize_array : int array -> t | |
16 | + | |
17 | +(* The mean value, or expected value, or average, of a stream. *) | |
18 | +val mean : t -> float | |
19 | + | |
20 | +(* The variance of a stream. Note that streams with very few samples do not | |
21 | + have a meaningful variance. *) | |
22 | +val variance : t -> float option | |
23 | + | |
24 | +(* The standard deviation of a stream. When a stream is normally distributed, | |
25 | + or as enough samples are taken for the Central Limit Theorem to apply, then | |
26 | + this indicates how samples are distributed around the mean. When variance is | |
27 | + undefined, the standard deviation is zero. *) | |
28 | +val stddev : t -> float |