extracting n-grams from tweets in python

2019-08-27 01:12发布

问题:

Say that I have 100 tweets.
In those tweets, I need to extract: 1) food names, and 2) beverage names.

Example of tweet:

"Yesterday I had a coca cola, and a hot dog for lunch, and some bana split for desert. I liked the coke, but the banana in the banana split dessert was ripe"

I have to my disposal two lexicons. One with food names, and one with beverage names.

Example in food names lexicon:
"hot dog"
"banana"
"banana split"

Example in beverage names lexicon:
"coke"
"cola"
"coca cola"

What I should be able to extract:

[[["coca cola", "beverage"], ["hot dog", "food"], ["banana split", "food"]],
[["coke", "beverage"], ["banana", "food"], ["banana split", "food"]]]

The names in the lexicons can be 1-5 word(s) long. How do I go about extracting n-grams from the tweets, using my lexicons?

回答1:

Here a simple solution:

import re

def lexicon_by_word(lexicons):
    return {word:key for key in lexicons.keys() for word in lexicons[key]}



def split_sentences(st):
    sentences = re.split(r'[.?!]\s*', st)
    if sentences[-1]:
        return sentences
    else:
        return sentences[:-1]

def ngrams_finder(lexicons, text):
    lexicons_by_word = lexicon_by_word(lexicons)
    def pattern(lexicons):
        pattern = "|".join(lexicons_by_word.keys())
        pattern = re.compile(pattern)
        return pattern
    pattern = pattern(lexicons) 
    ngrams = []
    for sentence in split_sentences(text):
        try:
            ngram = []
            for result in pattern.findall(sentence):
                ngram.append([result, lexicons_by_word[result]])
            ngrams.append(ngram)
        except IndexError: #if re.findall does not find anything
            continue
    return ngrams

# You could customize it
text = "Yesterday I had a coca cola, and a hot dog for lunch, and some bana split for desert. I liked the coke, but the banana in the banana split dessert was ripe"

lexicons = {
    "food":["hot dog",
             "banana",
             "banana split"],

    "beverage":["coke",
                 "cola",
                 "coca cola"],
     }
print(ngrams_finder(lexicons, text))

split_sentences function taken from here: Splitting a sentence by ending characters



回答2:

Not sure what you have tried so far, below is a solution using ngrams in nltk and dict()

from nltk import ngrams

tweet = "Yesterday I had a coca cola, and a hot dog for lunch, and some bana split for desert. I liked the coke, but the banana in the banana split dessert was ripe"

# Your lexicons
lexicon_food = ["hot dog", "banana", "banana split"]
lexicon_beverage = ["coke", "cola", "coca cola"]
lexicon_dict = {x: [x, 'Food'] for x in lexicon_food}
lexicon_dict.update({x: [x, 'Beverage'] for x in lexicon_beverage})

# Function to extract lexicon items
def extract(g, lex):
    if ' '.join(g) in lex.keys():
        return lex.get(' '.join(g))
    elif g[0] in lex.keys():
        return lex.get(g[0])
    else:
        pass

# Your task
out = [[extract(g, lexicon_dict) for g in ngrams(sentence.split(), 2) if extract(g, lexicon_dict)] 
        for sentence in tweet.replace(',', '').lower().split('.')]
print(out)

Output:

[[['coca cola', 'Beverage'], ['cola', 'Beverage'], ['hot dog', 'Food']], 
 [['coke', 'Beverage'], ['banana', 'Food'], ['banana split', 'Food']]]

Approach 2 (Avoid "coca cola" and "cola")

def extract2(sentence, lex):
    extracted_words = []
    words = sentence.split()
    i = 0
    while i < len(words):
        if ' '.join(words[i:i+2]) in lex.keys():
            extracted_words.append(lex.get(' '.join(words[i:i+2])))
            i += 2
        elif words[i] in lex.keys():
            extracted_words.append(lex.get(words[i]))
            i += 1
        else:
            i += 1
    return extracted_words

out = [extract2(s, lexicon_dict) for s in tweet.replace(',', '').lower().split('.')]
print(out)

Output:

[[['coca cola', 'Beverage'], ['hot dog', 'Food']], 
 [['coke', 'Beverage'], ['banana', 'Food'], ['banana split', 'Food']]]

Noted that nltk is not needed here.