Алла Тамбовцева
pymorphy2
.¶Для работы импортируем библиотеку pandas
и загрузим несколько отрывков лекций, взятых с ПостНауки, из файла csv:
import pandas as pd
df = pd.read_csv('articles.csv', encoding= 'UTF-8')
df
text | |
---|---|
0 | Что такое сверхтекучесть? Нам известно одно ее... |
1 | Тут возникают новые вопросы о том, как вообще ... |
2 | Еще одно интересное употребление редупликации ... |
3 | Как устроены эти редупликации типа «маслице-фи... |
4 | Курс «Расстройства личности: от паранойи и ист... |
5 | Сопротивление соблазну. В экономических термин... |
6 | В этом смысле такое картирование может быть ос... |
7 | Существует широко распространенное убеждение, ... |
df.text[3] # отрывок из лекции А.Пиперски (https://postnauka.ru/video/77566)
'Как устроены эти редупликации типа «маслице-фигаслице»? Они устроены очень интересно с фонетической точки зрения. Мы берем слово и дальше, чтобы его повторить, берем эту бранную часть, скажем «фиг», а потом добавляем кусок, начиная от ударного гласного и далее ― «маслице-фигаслице». Вот несколько вполне реальных примеров из текстов в интернете: «комиссии-фигиссии», «анализы-фигализы», «тренды-фигенды», «бусики-фигусики». Правило строго соблюдается, это видно. В «комиссии» ударный гласный «и», соответственно, «комиссии-фигиссии». Это, конечно, не вполне удобно в ситуации, когда у нас ударный гласный находится, например, в конце слова. Какие-нибудь «дураки» по этому правилу ― должно было бы получиться «дураки-фиги». Так обычно не бывает, тогда добавляется еще слог перед этим, например «дураки-фигаки».'
Задание. Взять функцию normalize()
из прошлой части и создать новый столбец text_norm
с нормализованным текстом с помощью метода .apply()
.
Решение:
import string
def normalize(x):
to_remove = string.punctuation + '«»—'
translator = str.maketrans('', '', to_remove)
res = x.translate(translator)
res = res.lower()
return res
df['text_norm'] = df.text.apply(normalize)
Задание. Написать функцию split_text()
для разбиения текста (строки) по пробелу. Пусть функция разбивает строку по пробелу, удаляет лишние пробелы в получившихся словах и возвращает список слов.
С помощью .apply()
применить split_text()
к столбцу text_norm
‒ создать новый столбец words
.
Решение:
def split_text(text):
res = text.split(" ")
final = [r.strip() for r in res]
return final
df['words'] = df.text_norm.apply(split_text)
Задание. Написать функцию filter_words()
для разбиения текста на слова с помощью tokenize
из nltk
и stopwords
для фильтрации стоп-стоп. Функция должна возвращать список слов с исключенными стоп-словами.
Решение:
from nltk.tokenize import sent_tokenize, word_tokenize
from nltk.corpus import stopwords
def filter_words(text, lang = 'russian'):
wordsFiltered = []
stopWords = set(stopwords.words(lang))
words = word_tokenize(text)
for w in words:
if w not in stopWords:
wordsFiltered.append(w)
return wordsFiltered
Все эти функции нам понадобятся далее, а пока остановимся и познакомимся с библиотекой pymorphy2
. Библиотека pymorphy2
разработана М.Коробовым специально для русского и украинского языков. Импортируем библиотеку и рассмотрим несколько несложных примеров (пока не связанных с нашими текстами).
import pymorphy2
morph = pymorphy2.MorphAnalyzer() # морфологический анализатор - класс MorphAnalyzer()
p = morph.parse('стекла')[0] # индекс 0 добавлен для извлечения самого слова
p
Parse(word='стекла', tag=OpencorporaTag('NOUN,inan,neut sing,gent'), normal_form='стекло', score=0.964285, methods_stack=((<DictionaryAnalyzer>, 'стекла', 545, 1),))
Морфологический анализатор ‒ набор средств для морфологического разбора слова. Класс, указанный выше, позволит не только извлечь необходимую грамматическую информацию, но и вернуть нормальную (начальную) форму слова, поставить его в нужный падеж, согласовать с числительным и прочее. Проверим.
p.lexeme
[Parse(word='стекло', tag=OpencorporaTag('NOUN,inan,neut sing,nomn'), normal_form='стекло', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'стекло', 545, 0),)), Parse(word='стекла', tag=OpencorporaTag('NOUN,inan,neut sing,gent'), normal_form='стекло', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'стекла', 545, 1),)), Parse(word='стеклу', tag=OpencorporaTag('NOUN,inan,neut sing,datv'), normal_form='стекло', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'стеклу', 545, 2),)), Parse(word='стекло', tag=OpencorporaTag('NOUN,inan,neut sing,accs'), normal_form='стекло', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'стекло', 545, 3),)), Parse(word='стеклом', tag=OpencorporaTag('NOUN,inan,neut sing,ablt'), normal_form='стекло', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'стеклом', 545, 4),)), Parse(word='стекле', tag=OpencorporaTag('NOUN,inan,neut sing,loct'), normal_form='стекло', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'стекле', 545, 5),)), Parse(word='стёкла', tag=OpencorporaTag('NOUN,inan,neut plur,nomn'), normal_form='стекло', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'стёкла', 545, 6),)), Parse(word='стёкол', tag=OpencorporaTag('NOUN,inan,neut plur,gent'), normal_form='стекло', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'стёкол', 545, 7),)), Parse(word='стёклам', tag=OpencorporaTag('NOUN,inan,neut plur,datv'), normal_form='стекло', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'стёклам', 545, 8),)), Parse(word='стёкла', tag=OpencorporaTag('NOUN,inan,neut plur,accs'), normal_form='стекло', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'стёкла', 545, 9),)), Parse(word='стёклами', tag=OpencorporaTag('NOUN,inan,neut plur,ablt'), normal_form='стекло', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'стёклами', 545, 10),)), Parse(word='стёклах', tag=OpencorporaTag('NOUN,inan,neut plur,loct'), normal_form='стекло', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'стёклах', 545, 11),))]
Лексема ‒ слово как абстрактная единица. Проще говоря, это начальная форма слова и все его возможные грамматические формы (лингвисты называют такой набор форм парадигмой). В случае существительных в парадигму входят все падежные формы слова в единственном и множественном числе, что мы здесь и видим. На последнем месте в tag
указан падеж:
Можно запросить нормальную (начальную) форму слова, то есть привести слово к тому виду, с каким мы уже сталкивались после лемматизации.
p.normal_form
'стекло'
Можно вывести все грамматические тэги, то есть все грамматические характеристики слова:
p.tag
OpencorporaTag('NOUN,inan,neut sing,gent')
Или не все, а конкретные (POS
‒ Part of Speech, часть речи):
p.tag.POS
'NOUN'
Можно поставить слово в форму конкретного падежа:
p.inflect({'ablt'}) # творительный
Parse(word='стеклом', tag=OpencorporaTag('NOUN,inan,neut sing,ablt'), normal_form='стекло', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'стеклом', 545, 4),))
p.inflect({'ablt'}).word # само слово
'стеклом'
Приятно то, что в анализатор pymorphy2
встроен алгоритм, который позволяет предсказывать грамматические характеристики неизвестного, несловарного слова. В официальной документации приведено очаровательное слово "бутявка" (кто не в курсе, откуда она взялась, посмотрите лингвистические сказки Людмилы Петрушевской).
butyavka = morph.parse('бутявка')[0]
butyavka
Parse(word='бутявка', tag=OpencorporaTag('NOUN,inan,femn sing,nomn'), normal_form='бутявка', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'явка', 8, 0), (<UnknownPrefixAnalyzer>, 'бут')))
Анализатор сообщит, что с наибольшей вероятностью это слово является существительным женского рода, стоящее в именительном падеже. Более того, он даже сможет согласовывать это слово с числительными. Например, с числом 3:
butyavka.make_agree_with_number(3).word # три бутявки
'бутявки'
Кстати, о вероятностях. Анализатор построен на механизмах машинного обучения и статистических моделях. На основе большого массива размеченных текстов (см. подробнее в документации) построена модель, которая позволяет оценивать вероятность, с какой слово можно отнести к определенной части речи с некоторыми грамматическими характеристиками. Рассмотрим слово стекло:
morph.parse('стекло')
[Parse(word='стекло', tag=OpencorporaTag('NOUN,inan,neut sing,nomn'), normal_form='стекло', score=0.75, methods_stack=((<DictionaryAnalyzer>, 'стекло', 545, 0),)), Parse(word='стекло', tag=OpencorporaTag('NOUN,inan,neut sing,accs'), normal_form='стекло', score=0.1875, methods_stack=((<DictionaryAnalyzer>, 'стекло', 545, 3),)), Parse(word='стекло', tag=OpencorporaTag('VERB,perf,intr neut,sing,past,indc'), normal_form='стечь', score=0.0625, methods_stack=((<DictionaryAnalyzer>, 'стекло', 968, 3),))]
На первом месте стоит вариант, score (вероятность варианта) которого наибольший. В нашем случае слово стекло c 75% уверенностью можно считать существительным в единственном числе и в именительном падеже, с уверенностью 18.75% ‒ существительным в единственном числе и в винительном падеже, с уверенностью 6.25% ‒ глаголом (форма стечь).
Вернемся к нашим текстам. Давайте попробуем составить небольшой список ключевых слов и выбирать среди текстов те, в которых встречаются эти слова. Тут возможно два пути: привести все слова к нормальнойй форме (лемматизация) и искать среди них или составить список, включающий все формы ключевых слов. Мы пойдем по второму пути. Он не всегда рациональный (особенно, если ключевых слов много), но тоже по-своему может быть полезен.
Для простоты возьмем одно слово, слово сопротивление и сгенерируем все его формы.
s = morph.parse('сопротивление')[0]
s
Parse(word='сопротивление', tag=OpencorporaTag('NOUN,inan,neut sing,nomn'), normal_form='сопротивление', score=0.666666, methods_stack=((<DictionaryAnalyzer>, 'сопротивление', 76, 0),))
lexemes = [s.lexeme[i].word for i in range(len(s.lexeme))]
lexemes # и даже с разным написанием
['сопротивление', 'сопротивленье', 'сопротивления', 'сопротивленья', 'сопротивлению', 'сопротивленью', 'сопротивление', 'сопротивленье', 'сопротивлением', 'сопротивленьем', 'сопротивлении', 'сопротивленье', 'сопротивленьи', 'сопротивления', 'сопротивленья', 'сопротивлений', 'сопротивлениям', 'сопротивленьям', 'сопротивления', 'сопротивленья', 'сопротивлениями', 'сопротивленьями', 'сопротивлениях', 'сопротивленьях']
Задание. Напишите код, который выбирает из df
только те строки, где в words
встречаются слова из списка lexemes
.
Решение:
with_keys = []
for i in range(0, len(df)):
for w in df.iloc[i].words:
if w in lexemes:
with_keys.append(df.iloc[i].text)
with_keys
['Что такое сверхтекучесть? Нам известно одно ее проявление — способность вещества течь без трения. Если вы возьмете ведро гелия и поместите его в любую емкость, вы можете двигать его вверх и вниз, а между стенками емкости не возникнет никакого трения. Сверхтекучесть была открыта в гелии-4 и объяснена при помощи конденсата Бозе — Эйнштейна, потому что атомы гелия-4 существуют в форме бозонов и могут формировать микроскопическое квантовое состояние — конденсат Бозе — Эйнштейна. Обычно конденсаты Бозе — Эйнштейна — это и есть сверхтекучие материалы. Тем не менее их не следует сводить друг к другу. Например, в сверхтекучем гелии-4 сила взаимодействия намного больше, чем в других сверхтекучих материалах. Поэтому общая картина немного более сложная.В целом в состоянии сверхтекучести частицы складываются в микроскопическое квантовое состояние и действуют коллективно. Если они наталкиваются на препятствие, они не рассеиваются. Если смотреть на это с точки зрения энергии, то можно понять, что любые возбуждения вне этих микроскопических объектов энергетически невозможны, потому что соотношение между энергией и импульсом изменяется таким образом, что возбуждения не возникает. Этим объясняется тот факт, что сверхтекучая жидкость течет без сопротивления.', 'Сопротивление соблазну. В экономических терминах поведение группы характеризовалось динамической несогласованностью. Сначала А было предпочтительным относительно Б, а затем приоритет перешел к Б. Динамическая несогласованность — нередкое явление. Субботним утром люди заявляют, что лучше займутся физкультурой, чем будут сидеть перед телевизором. К полудню они все еще дома на диване, смотрят футбол. Чем объяснить такое поведение?\nВ случае с кешью действуют два фактора: искушение и бездумность. Понятие искушения занимает человеческие умы по меньшей мере со времен Адама и Евы. Для теории подталкивания оно также важно. Что мы подразумеваем, когда говорим о соблазнительном, искушающем?', 'В этом смысле такое картирование может быть осуществлено любым механизмом, например биологической нейронной сетью, аналоговой или цифровой сетью или даже физическим контуром. Такими физическими контурами могут быть и опорно-двигательный аппарат живых существ, и различные механические структуры. Например, в экспериментах в моей лаборатории продемонстрировано, что роботизированный аналог копыта горного козла может иметь во много раз более высокое сопротивление скольжению за счет правильной мягкости основных связок по сравнению с таким же копытом, в котором суставы менее подвижны. Мы также заметили, что этот эффект меняется в зависимости от разных условий местности. Это пример того, как механический контур тела становится важным физическим свойством, в зависимости от условий среды определяющим состояния от скольжения (количественные характеристики) до сопротивления скольжению (количественные характеристики в другом пространстве).', 'В этом смысле такое картирование может быть осуществлено любым механизмом, например биологической нейронной сетью, аналоговой или цифровой сетью или даже физическим контуром. Такими физическими контурами могут быть и опорно-двигательный аппарат живых существ, и различные механические структуры. Например, в экспериментах в моей лаборатории продемонстрировано, что роботизированный аналог копыта горного козла может иметь во много раз более высокое сопротивление скольжению за счет правильной мягкости основных связок по сравнению с таким же копытом, в котором суставы менее подвижны. Мы также заметили, что этот эффект меняется в зависимости от разных условий местности. Это пример того, как механический контур тела становится важным физическим свойством, в зависимости от условий среды определяющим состояния от скольжения (количественные характеристики) до сопротивления скольжению (количественные характеристики в другом пространстве).']