Implement statistics display
John Smith

John Smith commited on 2022-306 22:44:44
Showing 2 changed files, with 73 additions and 4 deletions.


The "s" option on main.lua leads it to display statistics
about the user's deck: totals, averages, and percentile values.
... ...
@@ -123,6 +123,22 @@ local function badness(pr)
123 123
     return pr.fail / (pr.fail + pr.succ) * math.log(pr.fail)
124 124
 end
125 125
 
126
+--[[
127
+    show list of selected percentile-samples into a list,
128
+    with each entry modulated by the given function
129
+]]
130
+local function showpctiles(list, pctiles, func, name)
131
+    local s = "%ile"
132
+    for _, v in ipairs(pctiles) do
133
+        s = string.format("%s\t%.1f", s, 100 * v)
134
+    end
135
+    s = s .. "\n" .. name
136
+    for _, v in ipairs(pctiles) do
137
+        s = s .. "\t" .. func(list[math.floor(v * #list)])
138
+    end
139
+    return s .. "\n"
140
+end
141
+
126 142
 local actions = {
127 143
     a = {
128 144
         desc = "(a)dd card",
... ...
@@ -305,9 +321,58 @@ local actions = {
305 321
 
306 322
     s = {
307 323
         desc = "(s)tatistics",
308
-        nd = false,
309
-        fn = function()
310
-            io.write("Not yet implemented ...\n")
324
+        nd = true,
325
+        fn = function(config, deck, deckfname)
326
+            local tsc = 0
327
+            local tfc = 0
328
+            local expdeck = {}
329
+            for _, card in ipairs(deck) do
330
+                for _, pr in ipairs(card) do
331
+                    table.insert(expdeck, pr)
332
+                    tsc = tsc + pr.succ
333
+                    tfc = tfc + pr.fail
334
+                end
335
+            end
336
+            io.write(string.format(
337
+                "%s has %d cards and %d total prompts\n",
338
+                deckfname, #deck, #expdeck
339
+            ))
340
+            io.write(string.format(
341
+                "(average %.2f prompts per card).\n",
342
+                #expdeck / #deck
343
+            ))
344
+            io.write(string.format(
345
+                "The deck's prompts have been reviewed a total of %d times\n",
346
+                tsc + tfc
347
+            ))
348
+            io.write(string.format(
349
+                "(average %.2f reviews per prompt), overall retention %.2f%%.\n",
350
+                (tsc + tfc) / #expdeck, 100 * tsc / (tsc + tfc)
351
+            ))
352
+            table.sort(expdeck, function(a, b)
353
+                return a.ease < b.ease
354
+            end)
355
+            io.write("\n" .. showpctiles(expdeck, { 0.01, 0.05, 0.1, 0.25, 0.5, 0.8, 0.95 }, function(pr)
356
+                return string.format("%.2f", pr.ease)
357
+            end, "Ease"))
358
+            table.sort(expdeck, function(a, b)
359
+                return a:retention() < b:retention()
360
+            end)
361
+            io.write("\n" .. showpctiles(expdeck, { 0.01, 0.05, 0.1, 0.15, 0.2, 0.25, 0.4 }, function(pr)
362
+                return math.floor(100 * pr:retention())
363
+            end, "Ret. %"))
364
+            table.sort(expdeck, function(a, b)
365
+                return a.succ + a.fail < b.succ + b.fail
366
+            end)
367
+            io.write("\n" .. showpctiles(expdeck, { 0.25, 0.5, 0.7, 0.85, 0.95, 0.99, 0.995 }, function(pr)
368
+                return pr.succ + pr.fail
369
+            end, "Reviews"))
370
+            table.sort(expdeck, function(a, b)
371
+                return (a.created or 99^98) > (b.created or 99^99)
372
+            end)
373
+            io.write("\n" .. showpctiles(expdeck, { 0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 0.99 }, function(pr)
374
+                return disptime(os.time() - pr.created)
375
+            end, "Age"))
311 376
         end
312 377
     },
313 378
 
... ...
@@ -23,7 +23,7 @@ function Prompt:new(pt, ca, rts, rd, sc, fc, ct, ez)
23 23
     o.delay = tonumber(rd)
24 24
     o.succ = tonumber(sc)
25 25
     o.fail = tonumber(fc)
26
-    o.created = ct
26
+    o.created = tonumber(ct)
27 27
     o.ease = tonumber(ez)
28 28
     return o
29 29
 end
... ...
@@ -37,6 +37,10 @@ function Prompt:__tostring()
37 37
     )
38 38
 end
39 39
 
40
+function Prompt:retention()
41
+    return self.succ / (self.succ + self.fail)
42
+end
43
+
40 44
 --[[
41 45
     derive the `PromptText` used to create this `Prompt`
42 46
 ]]
43 47