Как сделать лемматизацию с помощью NLTK или pywsd - PullRequest
0 голосов
/ 26 марта 2020

Я знаю, что мои объяснения довольно длинные, но я посчитал это необходимым. Надеюсь, что кто-то терпелив и полезен для души :) Я занимаюсь анализом настроений в проекте и застрял в подготовительной части. Я сделал импорт файла CSV, превратил его в фрейм данных, преобразовал переменные / столбцы в нужные типы данных. Затем я проделал токенизацию следующим образом: я выбрал переменную, которую хотел токенизировать (твитнуть содержимое) в фрейме данных (df_tweet1):

# Tokenization
tknzr = TweetTokenizer()
tokenized_sents = [tknzr.tokenize(str(i)) for i in df_tweet1['Tweet Content']]
for i in tokenized_sents:
    print(i)

В результате получается список со словами (токенами).

Затем я выполняю удаление стоп-слов:

# Stop word removal
from nltk.corpus import stopwords

stop_words = set(stopwords.words("english"))
#add words that aren't in the NLTK stopwords list
new_stopwords = ['!', ',', ':', '&', '%', '.', '’']
new_stopwords_list = stop_words.union(new_stopwords)

clean_sents = []
for m in tokenized_sents:
    stop_m = [i for i in m if str(i).lower() not in new_stopwords_list]
    clean_sents.append(stop_m)

Вывод такой же, но без стоп-слов

Следующие два шага сбивают меня с толку (часть маркировка речи и лемматизация). Я попробовал две вещи:

1) Преобразовать предыдущий вывод в список строк

new_test = [' '.join(x) for x in clean_sents]

, так как я думал, что это позволит мне использовать этот код для выполнения обоих шагов в одном:

from pywsd.utils import lemmatize_sentence

text = new_test
lemm_text = lemmatize_sentence(text, keepWordPOS=True)

Я получил эту ошибку: TypeError: ожидаемая строка или байтовоподобный объект

2) Выполнить POS и лемматизировать отдельно. Первый POS с использованием clean_sents в качестве входных данных:

# PART-OF-SPEECH        
def process_content(clean_sents):
    try:
        tagged_list = []  
        for lst in clean_sents[:500]: 
            for item in lst:
                words = nltk.word_tokenize(item)
                tagged = nltk.pos_tag(words)
                tagged_list.append(tagged)
        return tagged_list

    except Exception as e:
        print(str(e))

output_POS_clean_sents = process_content(clean_sents)

Выходными данными является список списков со словами с прикрепленным тегом. Затем я хочу лемматизировать этот вывод, но как? Я попробовал два модуля, но оба дали мне ошибку:

from pywsd.utils import lemmatize_sentence

lemmatized= [[lemmatize_sentence(output_POS_clean_sents) for word in s]
              for s in output_POS_clean_sents]

# AND

from nltk.stem.wordnet import WordNetLemmatizer

lmtzr = WordNetLemmatizer()
lemmatized = [[lmtzr.lemmatize(word) for word in s]
              for s in output_POS_clean_sents]
print(lemmatized)

Ошибки были соответственно:

TypeError: ожидаемая строка или байтовоподобный объект

AttributeError: 'tuple 'объект не имеет атрибута' заканчивается с

Ответы [ 2 ]

0 голосов
/ 26 марта 2020

Если вы используете фрейм данных, я предлагаю вам сохранить результаты предварительных шагов обработки в новом столбце. Таким образом, вы всегда можете проверить выходные данные, и вы всегда можете создать список списков для использования в качестве входных данных для модели в строке кода после слов. Еще одним преимуществом этого подхода является то, что вы можете легко визуализировать строку предварительной обработки и добавлять другие этапы, где вам нужно, не путаясь.

Относительно вашего кода, он может быть оптимизирован (например, вы можете выполнить удаление слов остановки и в то же время) и я вижу некоторую путаницу в отношении шагов, которые вы выполнили. Например, вы выполняете лемматизацию несколько раз, используя также разные библиотеки, и в этом нет никакого смысла. На мой взгляд, nltk работает просто отлично, лично я использую другие библиотеки для предварительной обработки твитов только для работы с эмодзи, URL-адресами и хэштегами - все это связано с твитами.

# I won't write all the imports, you get them from your code
# define new column to store the processed tweets
df_tweet1['Tweet Content Clean'] = pd.Series(index=df_tweet1.index)

tknzr = TweetTokenizer()
lmtzr = WordNetLemmatizer()

stop_words = set(stopwords.words("english"))
new_stopwords = ['!', ',', ':', '&', '%', '.', '’']
new_stopwords_list = stop_words.union(new_stopwords)

# iterate through each tweet
for ind, row in df_tweet1.iterrows():

    # get initial tweet: ['This is the initial tweet']
    tweet = row['Tweet Content']

    # tokenisation, stopwords removal and lemmatisation all at once
    # out: ['initial', 'tweet']
    tweet = [lmtzr.lemmatize(i) for i in tknzr.tokenize(tweet) if i.lower() not in new_stopwords_list]

    # pos tag, no need to lemmatise again after.
    # out: [('initial', 'JJ'), ('tweet', 'NN')]
    tweet = nltk.pos_tag(tweet)

    # save processed tweet into the new column
    df_tweet1.loc[ind, 'Tweet Content Clean'] = tweet

Итак, в общем, все, что вам нужно - это 4 строки, одна для получения строки твита, две для предварительной обработки текста, другая для хранения твита. Вы можете добавить дополнительный шаг обработки, обращая внимание на вывод каждого шага (например, токенизация возвращает список строк, pos-теги возвращают список кортежей, причину возникновения проблем).

Если хотите, вы можете создать список списков, содержащий все твиты в кадре данных:

# out: [[('initial', 'JJ'), ('tweet', 'NN')], [second tweet], [third tweet]]
all_tweets = [tweet for tweet in df_tweet1['Tweet Content Clean']]
0 голосов
/ 26 марта 2020

В первой части new_test список строк. lemmatize_sentence ожидает строку, поэтому передача new_test вызовет ошибку, подобную той, которую вы получили. Вам нужно будет передать каждую строку отдельно, а затем создать список из каждой лемматизированной строки. Итак:

text = new_test
lemm_text = [lemmatize_sentence(sentence, keepWordPOS=True) for sentence in text]

должен создать список лемматизированных предложений.

Я действительно когда-то делал проект, который похож на то, что вы делаете. Я сделал следующую функцию для лемматизации строк:

import lemmy, re

def remove_stopwords(lst):
    with open('stopwords.txt', 'r') as sw:
        #read the stopwords file 
        stopwords = sw.read().split('\n')
        return [word for word in lst if not word in stopwords]

def lemmatize_strings(body_text, language = 'da', remove_stopwords_ = True):
    """Function to lemmatize a string or a list of strings, i.e. remove prefixes. Also removes punctuations.

    -- body_text: string or list of strings
    -- language: language of the passed string(s), e.g. 'en', 'da' etc.
    """

    if isinstance(body_text, str):
        body_text = [body_text] #Convert whatever passed to a list to support passing of single string

    if not hasattr(body_text, '__iter__'):
        raise TypeError('Passed argument should be a sequence.')

    lemmatizer = lemmy.load(language) #load lemmatizing dictionary

    lemma_list = [] #list to store each lemmatized string 

    word_regex = re.compile('[a-zA-Z0-9æøåÆØÅ]+') #All charachters and digits i.e. all possible words

    for string in body_text:
        #remove punctuation and split words
        matches = word_regex.findall(string)

        #split words and lowercase them unless they are all caps
        lemmatized_string = [word.lower() if not word.isupper() else word for word in matches]

        #remove words that are in the stopwords file
        if remove_stopwords_:
            lemmatized_string = remove_stopwords(lemmatized_string)

        #lemmatize each word and choose the shortest word of suggested lemmatizations
        lemmatized_string = [min(lemmatizer.lemmatize('', word), key=len) for word in lemmatized_string]

        #remove words that are in the stopwords file
        if remove_stopwords_:
            lemmatized_string = remove_stopwords(lemmatized_string)

        lemma_list.append(' '.join(lemmatized_string))

    return lemma_list if len(lemma_list) > 1 else lemma_list[0] #return list if list was passed, else return string

Вы можете посмотреть на это, если хотите, но не чувствуете себя обязанным. Я был бы более чем рад, если это поможет вам получить какие-либо идеи, я потратил много времени, пытаясь выяснить это сам!

Дайте мне знать: -)

...