Catogorising strings into random versus non-random

Christian Gollwitzer auriocus at gmx.de
Mon Dec 21 04:56:24 EST 2015


Am 21.12.15 um 09:24 schrieb Peter Otten:
> Steven D'Aprano wrote:
>
>> I have a large number of strings (originally file names) which tend to
>> fall into two groups. Some are human-meaningful, but not necessarily
>> dictionary words e.g.:
>>
>>
>> baby lions at play
>> saturday_morning12
>> Fukushima
>> ImpossibleFork
>>
>>
>> (note that some use underscores, others spaces, and some CamelCase) while
>> others are completely meaningless (or mostly so):
>>
>>
>> xy39mGWbosjY
>> 9sjz7s8198ghwt
>> rz4sdko-28dbRW00u
>>
>>
>> Let's call the second group "random" and the first "non-random", without
>> getting bogged down into arguments about whether they are really random or
>> not. I wish to process the strings and automatically determine whether
>> each string is random or not. I need to split the strings into three
>> groups:
>>
>> - those that I'm confident are random
>> - those that I'm unsure about
>> - those that I'm confident are non-random
>>
>> Ideally, I'll get some sort of numeric score so I can tweak where the
>> boundaries fall.
>>
>> Strings are *mostly* ASCII but may include a few non-ASCII characters.
>>
>> Note that false positives (detecting a meaningful non-random string as
>> random) is worse for me than false negatives (miscategorising a random
>> string as non-random).
>>
>> Does anyone have any suggestions for how to do this? Preferably something
>> already existing. I have some thoughts and/or questions:
>>
>> - I think nltk has a "language detection" function, would that be
>> suitable?
>>
>> - If not nltk, are there are suitable language detection libraries?
>>
>> - Is this the sort of problem that neural networks are good at solving?
>> Anyone know a really good tutorial for neural networks in Python?
>>
>> - How about Bayesian filters, e.g. SpamBayes?
>
> A dead simple approach -- look at the pairs in real words and calculate the
> ratio
>
> pairs-also-found-in-real-words/num-pairs

Sounds reasonable. Building on this approach, two simple improvements:
- calculate the log-likelihood instead, which also makes use of the 
frequency of the digraphs in the training set
- Use trigraphs instead of digraphs
- preprocess the string (lowercase), but more sophisticated 
preprocessing could be an option (i.e. converting under_scores and 
CamelCase to spaces)

The main reason for the low score of the baby lions is the space 
character, I think - the word list does not contain that much spaces. 
Maybe one should feed in some long wikipedia article to calculate the 
digraph/trigraph probabilities

=====================================
Apfelkiste:Tests chris$ cat score_my.py
from __future__ import division
from collections import Counter, defaultdict
from math import log
import sys
WORDLIST = "/usr/share/dict/words"

SAMPLE = """\
baby lions at play
saturday_morning12
Fukushima
ImpossibleFork
xy39mGWbosjY
9sjz7s8198ghwt
rz4sdko-28dbRW00u
""".splitlines()

def extract_pairs(text):
     for i in range(len(text)-1):
         yield text.lower()[i:i+2]
     # or len(text)-2 and i:i+3


def load_pairs():
     pairs = Counter()
     with open(WORDLIST) as f:
         for line in f:
             pairs.update(extract_pairs(line.strip()))
     # normalize to sum
     total_count = sum([pairs[x] for x in pairs])
     N = total_count+len(pairs)
     dist = defaultdict(lambda:1/N, ((x, (pairs[x]+1)/N) for x in pairs))
     return dist


def get_score(text, dist):
     ll    = 0
     for i, x in enumerate(extract_pairs(text), 1):
         ll += log(dist[x])
     return ll / i


def main():
     pair_dist = load_pairs()
     for text in sys.argv[1:] or SAMPLE:
         score = get_score(text, pair_dist)
         print("%.3g  %s" % (score, text))


if __name__ == "__main__":
     main()

Apfelkiste:Tests chris$ python score_my.py
-8.74  baby lions at play
-7.63  saturday_morning12
-6.38  Fukushima
-5.72  ImpossibleFork
-10.6  xy39mGWbosjY
-12.9  9sjz7s8198ghwt
-12.1  rz4sdko-28dbRW00u
Apfelkiste:Tests chris$ python score_my.py 'bnsip atl ayba loy'
-9.43  bnsip atl ayba loy
Apfelkiste:Tests chris$

and using trigraphs:

Apfelkiste:Tests chris$ python score_my.py 'bnsip atl ayba loy'
-12.5  bnsip atl ayba loy
Apfelkiste:Tests chris$ python score_my.py
-11.5  baby lions at play
-9.88  saturday_morning12
-9.85  Fukushima
-7.68  ImpossibleFork
-13.4  xy39mGWbosjY
-14.2  9sjz7s8198ghwt
-14.2  rz4sdko-28dbRW00u
==============================




More information about the Python-list mailing list