From 33564f4996840e3a9d3867283abea2a085d77b3a Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Sat, 28 Sep 2019 17:37:10 +0200 Subject: analyze: Customizeable triadfreq output Add commandline args to triadfreq. --- lulua/plot.py | 44 +++++++++++++++++++++----------------------- lulua/render.py | 25 +++---------------------- lulua/stats.py | 4 ++++ lulua/util.py | 23 ++++++++++++++++++++++- 4 files changed, 50 insertions(+), 46 deletions(-) diff --git a/lulua/plot.py b/lulua/plot.py index 13077e0..453c6eb 100644 --- a/lulua/plot.py +++ b/lulua/plot.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -import sys, argparse, json, unicodedata, pickle, logging +import sys, argparse, json, unicodedata, pickle, logging, math from operator import itemgetter from bokeh.plotting import figure from bokeh.models import ColumnDataSource, LinearAxis, Range1d @@ -26,7 +26,7 @@ from bokeh.embed import json_item from .layout import * from .keyboard import defaultKeyboards -from .util import limit +from .util import limit, displayText from .writer import Writer from .carpalx import Carpalx, model01 @@ -104,6 +104,17 @@ def letterfreq (args): return 0 def triadfreq (args): + """ Dump triad frequency stats to stdout """ + sorter = dict ( + weight=lambda x: x['weight'], + effort=lambda x: x['effort'].effort, + # increase impact of extremely “bad” triads using math.pow + combined=lambda x: (x['weight']/weightSum)*math.pow (x['effort'].effort, 2) + ) + def noLimit (l, n): + yield from l + limiter = limit if args.limit > 0 else noLimit + stats = pickle.load (sys.stdin.buffer) # XXX: add layout to stats? @@ -126,32 +137,19 @@ def triadfreq (args): # triads that contribute to x% of the weight topTriads = list () - topTriadsCutoff = 0.50 topTriadsWeight = 0 for data in sorted (binned.values (), key=lambda x: x['weight'], reverse=True): - if topTriadsWeight < weightSum*topTriadsCutoff: + if topTriadsWeight < weightSum*args.cutoff: topTriads.append (data) topTriadsWeight += data['weight'] - # get top triads (by weight) - print ('by weight') - for data in limit (sorted (binned.values (), key=lambda x: x['weight'], reverse=True), 20): - print (data['textTriad'], data['weight'], data['effort'].effort) - - logging.info (f'{len (topTriads)}/{len (stats["triads"].triads)} triads contribute to {topTriadsCutoff*100}% of the typing') - - print ('by effort') - # only base layer - includeBaseLayer = iter (topTriads) - sortByEffort = sorted (includeBaseLayer, key=lambda x: x['effort'].effort, reverse=True) - for data in limit (sortByEffort, 20): - print (data['textTriad'], data['weight'], data['effort'].effort) - - print ('by effort and weight') - includeBaseLayer = iter (topTriads) - sortByEffortWeight = sorted (includeBaseLayer, key=lambda x: (x['weight']/weightSum)*x['effort'].effort, reverse=True) - for data in limit (sortByEffortWeight, 20): - print (data['textTriad'], data['weight'], data['effort'].effort) + logging.info (f'{len (topTriads)}/{len (stats["triads"].triads)} triads ' + 'contribute to {args.cutoff*100}% of the typing') + + # final output + sortByEffort = sorted (iter (topTriads), key=sorter[args.sort], reverse=args.reverse) + for data in limiter (sortByEffort, args.limit): + print (''.join (map (displayText, data['textTriad'])), data['weight'], data['effort'].effort) return 0 diff --git a/lulua/render.py b/lulua/render.py index cbe553b..16dc543 100644 --- a/lulua/render.py +++ b/lulua/render.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -import argparse, sys, unicodedata, logging +import argparse, sys, logging from collections import namedtuple, defaultdict from operator import attrgetter from datetime import datetime @@ -30,7 +30,7 @@ import yaml from .layout import LITTLE, RING, MIDDLE, INDEX, THUMB, GenericLayout, defaultLayouts from .writer import Writer from .keyboard import defaultKeyboards -from .util import first +from .util import first, displayText RendererSettings = namedtuple ('RendererSetting', ['buttonMargin', 'middleGap', 'buttonWidth', 'rounded', 'shadowOffset']) @@ -99,25 +99,6 @@ class Renderer: return btn.width * self.settings.buttonWidth def _addButton (self, btn): - def toDisplayText (text): - if text is None: - return text - if len (text) == 1 and unicodedata.combining (text) != 0: - # add circle if combining - return '\u25cc' + text - invMap = { - '\t': '⭾', - '\n': '↳', - ' ': '\u2423', - '\u200e': '[LRM]', # left to right mark - '\u061c': '[ALM]', # arabic letter mark - '\u202c': '[PDF]', # pop directional formatting - "\u2066": '[LRI]', # left-to-right isolate (lri) - "\u2067": '[RLI]', # right-to-left isolate (rli) - "\u2069": '[PDI]', # pop directional isolate (pdi) - } - return invMap.get (text, text) - xoff, yoff = self.cursor settings = self.settings width = self.buttonWidth (btn) @@ -137,7 +118,7 @@ class Renderer: buttonText = [layerToArrow[i]] gclass.append ('modifier') else: - buttonText = list (map (toDisplayText, self.layout.getButtonText (btn))) + buttonText = list (map (displayText, self.layout.getButtonText (btn))) # background rect if any (buttonText): diff --git a/lulua/stats.py b/lulua/stats.py index fc2b202..6689616 100644 --- a/lulua/stats.py +++ b/lulua/stats.py @@ -234,6 +234,10 @@ def main (): sp = subparsers.add_parser('letterfreq') sp.set_defaults (func=letterfreq) sp = subparsers.add_parser('triadfreq') + sp.add_argument('-c', '--cutoff', type=float, default=0.5, help='Only include the top x% of all triads') + sp.add_argument('-r', '--reverse', action='store_true', help='Reverse sorting order') + sp.add_argument('-s', '--sort', choices={'weight', 'effort', 'combined'}, default='weight', help='Sorter') + sp.add_argument('-n', '--limit', type=int, default=0, help='Sorter') sp.set_defaults (func=triadfreq) logging.basicConfig (level=logging.INFO) diff --git a/lulua/util.py b/lulua/util.py index dd35c23..1e2156d 100644 --- a/lulua/util.py +++ b/lulua/util.py @@ -22,11 +22,12 @@ Misc utilities """ -import os, yaml, pkg_resources +import os, yaml, pkg_resources, unicodedata first = lambda x: next (iter (x)) def limit (l, n): + """ Limit the number of items drawn from iterable l to n. """ it = iter (l) for i in range (n): yield next (it) @@ -65,3 +66,23 @@ class YamlLoader: for res in pkg_resources.resource_listdir (__package__, self.defaultDir): yield self.__getitem__ (res, onlyRes=True) +def displayText (text): + """ Convert text into a string that is always renderable without combining, + control or invisible characters """ + if text is None: + return text + if len (text) == 1 and unicodedata.combining (text) != 0: + # add circle if combining + return '\u25cc' + text + invMap = { + '\t': '⭾', + '\n': '↳', + ' ': '\u2423', + '\u200e': '[LRM]', # left to right mark + '\u061c': '[ALM]', # arabic letter mark + '\u202c': '[PDF]', # pop directional formatting + "\u2066": '[LRI]', # left-to-right isolate (lri) + "\u2067": '[RLI]', # right-to-left isolate (rli) + "\u2069": '[PDI]', # pop directional isolate (pdi) + } + return invMap.get (text, text) -- cgit v1.2.3