tags:

views:

76

answers:

3

I have a list of words and I want to find how many times they occur in a .txt file. The word list is something like as follows:

wordlist = ['cup', 'bike', 'run']

I want to be able to not only pick up these words, but also things like CUP, biker, running, Cups, etc. So I think I need a regular expression. Here is what I was thinking but it doesn't work:

len(re.findall(wordlist, filename, re.I))

Thanks in advance!

+2  A: 

You're close. But re.findall takes a pattern and a string, not a wordlist and a filename.

But, if you read your file into a string and turn your wordlist into a pattern, then you'll get it.

The pattern you need will look like this: r"cup|bike|run". You could do "|".join(wordlist) to get this.

That's a very loose way of counting all these instances. Note that if your file has the words "My truncheon has been scuppered" in it, then re.findall will find "run" and "cup" inside the bigger words. So you may want to tweak your pattern to catch the beginnings and ends of words.

To get whole words only, use this pattern: r"\b(cup|bike|run)\b". Of course, you'll need to fill in all the word varieties that you are looking for.

eksortso
Even adding the beginning and end of word tweak, there will still be words that weren't supposed to be found (e.g. "hiccup", "runt", etc). Maybe using WordNet or some other dictionaryish thing to find all the different forms of a word, then use word boundaries on both sides of the search terms...
tgray
Thanks, @tgray. I've tweaked my answer to include a pattern for matching whole words.
eksortso
Thanks eksortso! I didn't understand the pattern/string differentiation and now I see it. The word list I am actually using is made up of longer words so I shouldn't run into the weird issues that arise with cup and run, but I will certainly think more about using whole words only. Thanks!
dandyjuan
@eksortso, You're welcome! Apparently WortNet is unidirectional in it's conversion of inflected forms; however, WolframAlpha/Mathmatica does this fairly well. http://www.wolframalpha.com/input/?i=cup+Inflected+forms, http://reference.wolfram.com/mathematica/ref/WordData.html
tgray
+1 for scuppered truncheons
Dennis Williamson
+2  A: 

The regex needs work, but this should get you started:

from __future__ import with_statement # only if < 2.6
from collections import defaultdict
import re

matches = defaultdict(int)
with open(filename) as f:
    for mtch in re.findall(r'\b(cup|bike|run)', f.read(), re.I):
        matches[mtch.lower()] += 1
Adam Bernier
It's not clear if what you are doing is necessary. What you solve is "how many times *each* word occurs" when OP said "how many times they occur" - seems to me no need for individual count. Also checking only for \b word beginning is trouble - it will "find" *cup* in *cupid*, *run* in *rune*, *meat* in *meatless* and so on
Nas Banov
+1  A: 

You will have first to guess all forms of the words and that seems a PITA. But here is a simplified fn i wrote after reading http://www.theenglishspace.com/spelling/ :

def getWordForms(word):
    ''' Given an English word, return list of possible forms
    '''
    l = [word]
    if len(word)>1:
        l.extend([word + 's', word + 'ing', word + 'ed'])
        wor, d = word[:-1], word[-1:]
        if d == 'e':
            l.append(word + 'd')
            l.append(wor + 'ing')
            if wor[-1:] == 'f':
                l.append(wor[:-1] + 'ves')
        elif d == 'y':
            l.append(wor + 'ied')
            l.append(wor + 'ies')
        elif d == 'z':
            l.append(word + 'zes') # double Z
        elif d == 'f':
            l.append(wor + 'ves')
        elif d in 'shox':
            l.append(word + 'es')
        if re.match('[^aeiou][aeiou][^aeiou]', word):
            l.append(word + d + 'ing') # double consonant
            l.append(word + d + 'ed')
    return l

It is overly generous in the variants of words it guesses - but that is ok because this is not a spell checker and you will be using \b for word boundaries on both sides.

Nas Banov