repeated phrases in the text Python

后端 未结 4 656
借酒劲吻你
借酒劲吻你 2021-01-21 02:22

I have a problem and I have no idea how to solve it. Please, give a piece of advice.

I have a text. Big, big text. The task is to find all the repeated phrases which len

4条回答
  •  情话喂你
    2021-01-21 02:32

    You have, it seems to me, two problems.

    The first is coming up with an efficient way of normalizing the input. You say you want to find all of the three-word phrases in the input, but what constitutes a phrase? For instance, are the black dog and The black, dog? the same phrase?

    A way of doing this, as marcog suggests, is by using something like re.findall. But this is pretty inefficient: it traverses your entire input and copies the words into a list, and then you have to process that list. If your input text is very long, that's going to be wasteful of both time and space.

    A better approach would be to treat the input as a stream, and build a generator that pulls off one word at a time. Here's an example, which uses spaces as the delimiter between words, then strips non-alpha characters out of the words and converts them to lower case:

    >>> def words(text):
           pattern = re.compile(r"[^\s]+")
           non_alpha = re.compile(r"[^a-z]", re.IGNORECASE)
           for match in pattern.finditer(text):
               nxt = non_alpha.sub("", match.group()).lower()
               if nxt:  # skip blank, non-alpha words
                   yield nxt
    
    
    >>> text
    "O'er the bright blue sea, for Sir Joseph Porter K.C.B."
    >>> list(words(text))
    ['oer', 'the', 'bright', 'blue', 'sea', 'for', 'sir', 'joseph', 'porter', 'kcb']
    

    The second problem is grouping the normalized words into three-word phrases. Again, here is a place where a generator will perform efficiently:

    >>> def phrases(words):
            phrase = []
            for word in words:
                phrase.append(word)
                if len(phrase) > 3:
                    phrase.remove(phrase[0])
                if len(phrase) == 3:
                    yield tuple(phrase)
    
    >>> list(phrases(words(text)))
    [('oer', 'the', 'bright'), ('the', 'bright', 'blue'), ('bright', 'blue', 'sea'), ('blue', 'sea', 'for'), ('sea', 'for', 'sir'), ('for', 'sir', 'joseph'), ('sir', 'joseph', 'porter'), ('joseph', 'porter', 'kcb')]
    

    There's almost certainly a simpler version of that function possible, but this one's efficient, and it's not hard to understand.

    Significantly, chaining the generators together only traverses the list once, and it doesn't build any large temporary data structures in memory. You can use the result to build a defaultdict keyed by phrase:

    >>> import collections
    >>> counts = collections.defaultdict(int)
    >>> for phrase in phrases(words(text)):
            counts[phrase] += 1
    

    This makes a single pass over text as it counts the phrases. When it's done, find every entry in the dictionary whose value is greater than one.

提交回复
热议问题