Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sequence matching algorithm in python

I have a list of sentences such as this:

errList = [ 'Ragu ate lunch but didnt have Water for drinks',
            'Rams ate lunch but didnt have Gatorade for drinks',
            'Saya ate lunch but didnt have :water for drinks',
            'Raghu ate lunch but didnt have water for drinks',
            'Hanu ate lunch but didnt have -water for drinks',
            'Wayu ate lunch but didnt have water for drinks',
            'Viru ate lunch but didnt have .water 4or drinks',

            'kk ate lunch & icecream but did have Water for drinks',
            'M ate lunch &and icecream but did have Gatorade for drinks',
            'Parker ate lunch icecream but didnt have :water for drinks',
            'Sassy ate lunch and icecream but didnt have water for drinks',
            'John ate lunch and icecream but didnt have -water for drinks',
            'Pokey ate lunch and icecream but didnt have Water for drinks',
            'Laila ate lunch and icecream but did have water 4or drinks',
        ]

I want to find out count of longest phrases/part (phrase must be more than 2 words) of sentences in each element of list? In following example, output will look closer to this (longest phrase as key and count as value):

{ 'ate lunch but didnt have': 7,
  'water for drinks': 7,
  'ate lunch and icecream': 4,
  'didnt have water': 3,
  'didnt have Water': 2    # case sensitives
}

Using re module is out of question since problem is close to sequence matching or perhaps using nltk or perhaps scikit-learn ? I have some familiarity with NLP and scikit but not enough to solve this? If I solve this, I will publish it here.

like image 281
NullException Avatar asked May 23 '18 18:05

NullException


People also ask

What is Difflib Python?

Source code: Lib/difflib.py. This module provides classes and functions for comparing sequences. It can be used for example, for comparing files, and can produce information about file differences in various formats, including HTML and context and unified diffs.

How does Sequence Matcher work?

SequenceMatcher FlowChartratio( ) returns the similarity score ( float in [0,1] ) between input strings. It sums the sizes of all matched sequences returned by function get_matching_blocks and calculates the ratio as: ratio = 2.0*M / T , where M = matches , T = total number of elements in both sequences.

Is Difflib standard Python?

This module in the python standard library provides classes and functions for comparing sequences like strings, lists etc.

How do you close a match in Python?

The get_close_matches() function returns a list of close matched strings that satisfy the cutoff. The order of close matched string is based on similarity score, so the most similar string comes first in the list.


2 Answers

It's not too painful with scikit-learn with a bit of numpy foo as well. A word of warning though, here I've just the defaults for preprocessing, if you're interested in the punctuation in your dataset then you will need to tweak this.

from sklearn.feature_extraction.text import CountVectorizer

# Find all the phrases >2 up to the max length 
cv = CountVectorizer(ngram_range=(3, max([len(x.split(' ')) for x in errList])))
# Get the counts of the phrases
err_counts = cv.fit_transform(errList)
# Get the sum of each of the phrases
err_counts = err_counts.sum(axis=0)
# Mess about with the types, sparsity is annoying
err_counts = np.squeeze(np.asarray(err_counts))
# Retrieve the actual phrases that we're working with
feat_names = np.array(cv.get_feature_names())

# We don't have to sort here, but it's nice to if you want to print anything
err_counts_sorted = err_counts.argsort()[::-1]
feat_names = feat_names[err_counts_sorted]
err_counts = err_counts[err_counts_sorted]

# This is the dictionary that you were after
err_dict = dict(zip(feat_names, err_counts))

Here's the output for the top few

11 but didnt have
10 have water for drinks
10 have water for
10 water for drinks
10 but didnt have water
10 didnt have water
9 but didnt have water for drinks
9 but didnt have water for
9 didnt have water for drinks
9 didnt have water for
like image 122
piman314 Avatar answered Oct 19 '22 23:10

piman314


If you don't want to bother with external libraries, you can get this done with just the stdlib (although it may well be slower than some alternatives):

import collections
import itertools

def gen_ngrams(sentence):
    words = sentence.split() # or re.findall('\b\w+\b'), or whatever
    n_words = len(words)
    for i in range(n_words - 2):
        for j in range(i + 3, n_words):
            yield ' '.join(words[i: j]) # Assume normalization of spaces


def count_ngrams(sentences):
    return collections.Counter(
        itertools.chain.from_iterable(
            gen_ngrams(sentence) for sentence in sentences
        )
    )

counts = count_ngrams(errList)
dict(counts.most_common(10))

Which gets you:

{'but didnt have': 11,
 'ate lunch but': 7,
 'ate lunch but didnt': 7,
 'ate lunch but didnt have': 7,
 'lunch but didnt': 7,
 'lunch but didnt have': 7,
 'icecream but didnt': 4,
 'icecream but didnt have': 4,
 'ate lunch and': 4,
 'ate lunch and icecream': 4}
like image 32
Oliver Sherouse Avatar answered Oct 20 '22 00:10

Oliver Sherouse