извлечение н-граммов из твитов в Python

Скажем, у меня 100 твитов.
Из этих твитов мне нужно извлечь: 1) названия блюд и 2) названия напитков.

Пример твита:

«Вчера у меня была кока-кола и хот-дог на обед, и немного банановой дроби на десерт. Мне понравилась кока-кола, но банан в десерте из банановой дробины был спелым»

В моем распоряжении два словаря. Один с названиями блюд, а другой с названиями напитков.

Пример из лексики названий продуктов питания:
"хот-дог"
"банан"
"банановый сплит"

Пример в лексике названий напитков:
«кока-кола»
«кола»
«кока-кола»

Что я могу извлечь:

[[[«кока-кола», «напиток»], [«хот-дог», «еда»], [«банановый сплит», «еда»]],
[[«кока-кола», «напиток»], ["банан", "еда"], ["банановый сплит", "еда"]]]

Имена в словарях могут состоять из 1-5 слов. Как мне извлечь н-граммы из твитов, используя свой лексикон?


person AnonX    schedule 02.03.2018    source источник


Ответы (2)


Не уверен, что вы пробовали до сих пор, ниже представлено решение, использующее ngrams в nltk и 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)

Выход:

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

Подход 2 (избегайте "кока-колы" и "колы")

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)

Выход:

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

Отметил, что nltk здесь не нужен.

person pe-perry    schedule 02.03.2018
comment
Спасибо за Ваш ответ! Мне нравится это решение, но проблема в том, что при успешном извлечении биграммы не должно быть и униграммы, извлеченной из той же части текста. Пример: я люблю кока-колу, должна быть только кока-кола. Не кока-кола и кола. Единственное решение, которое я могу придумать, - это удалить эту часть и повторно запустить оставшуюся часть, но тогда я теряю контекст исходного предложения, что также важно. Есть ли способ лучше? - person AnonX; 02.03.2018
comment
@AnonX Я отредактировал свой ответ. Как насчет второго подхода? - person pe-perry; 05.03.2018

Вот простое решение:

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, взятая отсюда: Разделение предложения конечными символами

person artona    schedule 02.03.2018
comment
Кстати, вы видели эту функцию? stackoverflow.com/questions/47663870/ - person alvas; 02.03.2018
comment
Спасибо @artona, решение regexp пока кажется лучшим, но как вы думаете, есть ли способ сделать это со словами с тегами POS в списке? Я подумываю сделать nltk.pos_tag (), но тогда я не смогу передавать целые предложения в поисковик шаблонов - person AnonX; 02.03.2018
comment
@alvas Спасибо, что показали мне, что я изменю решение после того, как задам несколько вопросов AnonX - person artona; 03.03.2018
comment
@AnonX Вы хотите сохранить в списке слова с тегами pos после поиска шаблонов? Пожалуйста, покажите мне вход и запрошенный результат такого действия в своем сообщении. - person artona; 03.03.2018
comment
@artona Спасибо за ответ. Я опубликовал обновленную версию этого, чтобы как можно лучше ответить на ваши вопросы. (stackoverflow.com/questions/49091931/ < / а>). - Пожалуйста, дайте мне знать в этом посте, если требуются дополнительные разъяснения. Спасибо за помощь! :) - person AnonX; 04.03.2018