ckmai/lec8.ipynb
2025-02-21 10:56:34 +04:00

134 KiB
Raw Blame History

Анализ текста

Инициализация движка (модуля) для анализа текста

In [1]:
import spacy
sp = spacy.load("ru_core_news_lg")

Базовые операции над текстом

Текст для примеров

In [2]:
text = "Накануне Дня российской науки, 7 февраля, в Ульяновском государственном техническом университете открыли новую экспозицию «Они стояли у истоков ульяновской науки». Она посвящена выдающимся ученым XX  начала XXI веков, чьи достижения и изобретения сформировали научный и технологический облик Ульяновской области."

text_ner = "В рамках торжественного открытия экспозиции за активное участие в подготовке материалов были вручены благодарственные письма профессору кафедры летной эксплуатации и безопасности полетов Ульяновского института гражданской авиации имени Главного маршала авиации Б.П.Бугаева Сергею Косачевскому, начальнику управления научно-исследовательской и инновационной деятельности Ульяновского государственного педагогического университета имени И.Н.Ульянова Светлане Богатовой, руководителю пресс-службы Ульяновского государственного аграрного университета имени П.А.Столыпина Винере Насыровой, помощнику проректора по научной работе Ульяновского государственного университета Татьяне Лисовой."

Предобработка

Изменение регистра

In [3]:
print(text)
print(text.lower())
print(text.upper())
Накануне Дня российской науки, 7 февраля, в Ульяновском государственном техническом университете открыли новую экспозицию «Они стояли у истоков ульяновской науки». Она посвящена выдающимся ученым XX  начала XXI веков, чьи достижения и изобретения сформировали научный и технологический облик Ульяновской области.
накануне дня российской науки, 7 февраля, в ульяновском государственном техническом университете открыли новую экспозицию «они стояли у истоков ульяновской науки». она посвящена выдающимся ученым xx  начала xxi веков, чьи достижения и изобретения сформировали научный и технологический облик ульяновской области.
НАКАНУНЕ ДНЯ РОССИЙСКОЙ НАУКИ, 7 ФЕВРАЛЯ, В УЛЬЯНОВСКОМ ГОСУДАРСТВЕННОМ ТЕХНИЧЕСКОМ УНИВЕРСИТЕТЕ ОТКРЫЛИ НОВУЮ ЭКСПОЗИЦИЮ «ОНИ СТОЯЛИ У ИСТОКОВ УЛЬЯНОВСКОЙ НАУКИ». ОНА ПОСВЯЩЕНА ВЫДАЮЩИМСЯ УЧЕНЫМ XX  НАЧАЛА XXI ВЕКОВ, ЧЬИ ДОСТИЖЕНИЯ И ИЗОБРЕТЕНИЯ СФОРМИРОВАЛИ НАУЧНЫЙ И ТЕХНОЛОГИЧЕСКИЙ ОБЛИК УЛЬЯНОВСКОЙ ОБЛАСТИ.

Регулярные выражения

In [4]:
import re

regex = re.compile("[^a-zA-Zа-яА-Я ]")
print(text)
print(" ".join(regex.sub("", text).split()))
Накануне Дня российской науки, 7 февраля, в Ульяновском государственном техническом университете открыли новую экспозицию «Они стояли у истоков ульяновской науки». Она посвящена выдающимся ученым XX  начала XXI веков, чьи достижения и изобретения сформировали научный и технологический облик Ульяновской области.
Накануне Дня российской науки февраля в Ульяновском государственном техническом университете открыли новую экспозицию Они стояли у истоков ульяновской науки Она посвящена выдающимся ученым XX начала XXI веков чьи достижения и изобретения сформировали научный и технологический облик Ульяновской области

Эмодзи

In [5]:
import emoji

print(emoji.emojize("Пример эмодзи :thumbs_up:"))
print(emoji.demojize("Пример эмодзи 👍"))
Пример эмодзи 👍
Пример эмодзи :thumbs_up:

Диакритические знаки

In [6]:
import unicodedata

norm_text = unicodedata.normalize("NFKD", f"stävänger {text}")

res = "".join([char for char in norm_text if not unicodedata.combining(char)])

print(res)
stavanger Накануне Дня россиискои науки, 7 февраля, в Ульяновском государственном техническом университете открыли новую экспозицию «Они стояли у истоков ульяновскои науки». Она посвящена выдающимся ученым XX  начала XXI веков, чьи достижения и изобретения сформировали научныи и технологическии облик Ульяновскои области.

Преобразование числа в строку

In [7]:
times = {"7": "семь", "XX": "двадцать", "XXI": "двадцать один"}

print([times[token.text] if token.text in times else token.text for token in sp(text)])
['Накануне', 'Дня', 'российской', 'науки', ',', 'семь', 'февраля', ',', 'в', 'Ульяновском', 'государственном', 'техническом', 'университете', 'открыли', 'новую', 'экспозицию', '«', 'Они', 'стояли', 'у', 'истоков', 'ульяновской', 'науки', '»', '.', 'Она', 'посвящена', 'выдающимся', 'ученым', 'двадцать', '', 'начала', 'двадцать один', 'веков', ',', 'чьи', 'достижения', 'и', 'изобретения', 'сформировали', 'научный', 'и', 'технологический', 'облик', 'Ульяновской', 'области', '.']

Токенизация

In [8]:
doc = sp(text)

print(doc)
print(doc[4])
print(doc[4].is_sent_start)
print(doc[4].is_sent_end)
print(doc[4].is_punct)

print()

for sentence in doc.sents:
    print(sentence)

print()

for sentence in doc.sents:
    for word in sentence:
        print(word)
Накануне Дня российской науки, 7 февраля, в Ульяновском государственном техническом университете открыли новую экспозицию «Они стояли у истоков ульяновской науки». Она посвящена выдающимся ученым XX  начала XXI веков, чьи достижения и изобретения сформировали научный и технологический облик Ульяновской области.
,
False
False
True

Накануне Дня российской науки, 7 февраля, в Ульяновском государственном техническом университете открыли новую экспозицию «Они стояли у истоков ульяновской науки».
Она посвящена выдающимся ученым XX  начала XXI веков, чьи достижения и изобретения сформировали научный и технологический облик Ульяновской области.

Накануне
Дня
российской
науки
,
7
февраля
,
в
Ульяновском
государственном
техническом
университете
открыли
новую
экспозицию
«
Они
стояли
у
истоков
ульяновской
науки
»
.
Она
посвящена
выдающимся
ученым
XX

начала
XXI
веков
,
чьи
достижения
и
изобретения
сформировали
научный
и
технологический
облик
Ульяновской
области
.

Удаление стоп-слов

Список стоп-слов

In [9]:
from string import punctuation

print(sorted(sp.Defaults.stop_words))
print(list(punctuation))
['а', 'авось', 'ага', 'агу', 'аж', 'ай', 'али', 'алло', 'ау', 'ах', 'ая', 'б', 'бац', 'без', 'безусловно', 'бишь', 'благо', 'благодаря', 'ближайшие', 'близко', 'более', 'больше', 'будем', 'будет', 'будете', 'будешь', 'будто', 'буду', 'будут', 'будучи', 'будь', 'будьте', 'бы', 'бывает', 'бывала', 'бывали', 'бываю', 'бывают', 'был', 'была', 'были', 'было', 'бытует', 'быть', 'в', 'вам', 'вами', 'вас', 'ваш', 'ваша', 'ваше', 'ваши', 'вдали', 'вдобавок', 'вдруг', 'ведь', 'везде', 'вернее', 'весь', 'взаимно', 'взаправду', 'видно', 'вишь', 'включая', 'вместо', 'внакладе', 'вначале', 'вне', 'вниз', 'внизу', 'вновь', 'во', 'вовсе', 'возможно', 'воистину', 'вокруг', 'вон', 'вообще', 'вопреки', 'вот', 'вперекор', 'вплоть', 'вполне', 'вправду', 'вправе', 'впрочем', 'впрямь', 'вресноту', 'вроде', 'вряд', 'все', 'всегда', 'всего', 'всей', 'всем', 'всеми', 'всему', 'всех', 'всею', 'всея', 'всю', 'всюду', 'вся', 'всякий', 'всякого', 'всякой', 'всячески', 'всё', 'всём', 'вчеред', 'вы', 'г', 'гав', 'где', 'го', 'гораздо', 'д', 'да', 'дабы', 'давайте', 'давно', 'давным', 'даже', 'далее', 'далеко', 'дальше', 'данная', 'данного', 'данное', 'данной', 'данном', 'данному', 'данные', 'данный', 'данных', 'дану', 'данунах', 'даром', 'де', 'действительно', 'для', 'до', 'довольно', 'доколе', 'доколь', 'долго', 'должен', 'должна', 'должно', 'должны', 'должный', 'дополнительно', 'другая', 'другие', 'другим', 'другими', 'других', 'другое', 'другой', 'е', 'его', 'едва', 'едим', 'едят', 'ее', 'ежели', 'ей', 'ел', 'ела', 'еле', 'ем', 'ему', 'емъ', 'если', 'ест', 'есть', 'ешь', 'еще', 'ещё', 'ею', 'её', 'ж', 'же', 'з', 'за', 'затем', 'зато', 'зачем', 'здесь', 'значит', 'зря', 'и', 'ибо', 'из', 'или', 'иль', 'им', 'имеет', 'имел', 'имела', 'имело', 'именно', 'иметь', 'ими', 'имъ', 'иначе', 'иногда', 'иным', 'иными', 'итак', 'их', 'ишь', 'й', 'к', 'ка', 'кабы', 'каждая', 'каждое', 'каждые', 'каждый', 'кажется', 'казалась', 'казались', 'казалось', 'казался', 'казаться', 'как', 'какая', 'какие', 'каким', 'какими', 'каков', 'какого', 'какой', 'какому', 'какою', 'касательно', 'кем', 'ко', 'когда', 'кого', 'кой', 'коли', 'коль', 'ком', 'кому', 'комья', 'конечно', 'короче', 'которая', 'которого', 'которое', 'которой', 'котором', 'которому', 'которою', 'которую', 'которые', 'который', 'которым', 'которыми', 'которых', 'кроме', 'кстати', 'кто', 'ку', 'куда', 'л', 'ли', 'либо', 'лишь', 'любая', 'любого', 'любое', 'любой', 'любом', 'любую', 'любыми', 'любых', 'м', 'мало', 'меж', 'между', 'менее', 'меньше', 'меня', 'мимо', 'мне', 'многие', 'много', 'многого', 'многое', 'многом', 'многому', 'мной', 'мною', 'мог', 'моги', 'могите', 'могла', 'могли', 'могло', 'могу', 'могут', 'мое', 'моего', 'моей', 'моем', 'моему', 'моею', 'можем', 'может', 'можете', 'можешь', 'можно', 'мои', 'моим', 'моими', 'моих', 'мой', 'мол', 'мочь', 'мою', 'моя', 'моё', 'моём', 'му', 'мы', 'н', 'на', 'наверняка', 'наверху', 'навряд', 'навыворот', 'над', 'надо', 'назад', 'наиболее', 'наизворот', 'наизнанку', 'наипаче', 'накануне', 'наконец', 'нам', 'нами', 'наоборот', 'наперед', 'наперекор', 'наподобие', 'например', 'напротив', 'напрямую', 'нас', 'наса', 'насилу', 'настоящая', 'настоящее', 'настоящие', 'настоящий', 'насчет', 'нате', 'находиться', 'начала', 'начале', 'наш', 'наша', 'наше', 'нашего', 'нашей', 'нашем', 'нашему', 'нашею', 'наши', 'нашим', 'нашими', 'наших', 'нашу', 'не', 'неважно', 'негде', 'него', 'недавно', 'недалеко', 'нее', 'незачем', 'ней', 'некем', 'некогда', 'некому', 'некоторая', 'некоторые', 'некоторый', 'некоторых', 'некто', 'некуда', 'нельзя', 'нем', 'немногие', 'немногим', 'немного', 'нему', 'необходимо', 'необходимости', 'необходимые', 'необходимым', 'неоткуда', 'непрерывно', 'нередко', 'несколько', 'нет', 'нету', 'неужели', 'нечего', 'нечем', 'нечему', 'нечто', 'нешто', 'нею', 'неё', 'нибудь', 'нигде', 'ниже', 'низко', 'никак', 'никакой', 'никем', 'никогда', 'никого', 'никому', 'никто', 'никуда', 'ним', 'ними', 'ниоткуда', 'нипочем', 'них', 'ничего', 'ничем', 'ничему', 'ничто', 'но', 'ну', 'нужная', 'нужно', 'нужного', 'нужные', 'нужный', 'нужных', 'ныне', 'нынешнее', 'нынешней', 'нынешних', 'нынче', 'нём', 'о', 'об', 'оба', 'общую', 'обычно', 'ого', 'один', 'одна', 'однажды', 'однако', 'одни', 'одним', 'одними', 'одних', 'одно', 'одного', 'одной', 'одном', 'одному', 'одною', 'одну', 'ой', 'около', 'ом', 'он', 'она', 'оне', 'они', 'оно', 'оный', 'оп', 'опять', 'особенно', 'особо', 'особую', 'особые', 'от', 'откуда', 'отнелижа', 'отнелиже', 'отовсюду', 'отсюда', 'оттого', 'оттот', 'оттуда', 'отчего', 'отчему', 'ох', 'очевидно', 'очень', 'п', 'паче', 'перед', 'по', 'под', 'подавно', 'поди', 'подобная', 'подобно', 'подобного', 'подобные', 'подобный', 'подобным', 'подобных', 'поелику', 'пожалуй', 'пожалуйста', 'позже', 'поистине', 'пока', 'покамест', 'поколе', 'поколь', 'покуда', 'покудова', 'помимо', 'понеже', 'поприще', 'пор', 'пора', 'посему', 'поскольку', 'после', 'посреди', 'посредством', 'потом', 'потому', 'потомушта', 'похожем', 'почему', 'почти', 'поэтому', 'прежде', 'при', 'притом', 'причем', 'про', 'просто', 'прочего', 'прочее', 'прочему', 'прочими', 'проще', 'прям', 'пусть', 'р', 'ради', 'разве', 'ранее', 'рано', 'раньше', 'рядом', 'с', 'сам', 'сама', 'самая', 'сами', 'самим', 'самими', 'самих', 'само', 'самого', 'самое', 'самой', 'самом', 'самому', 'саму', 'самый', 'самых', 'сверх', 'свое', 'своего', 'своей', 'своем', 'своему', 'своею', 'свои', 'своим', 'своими', 'своих', 'свой', 'свою', 'своя', 'своё', 'своём', 'свыше', 'се', 'себе', 'себя', 'сего', 'сей', 'сейчас', 'сие', 'сих', 'сквозь', 'сколько', 'скорее', 'скоро', 'следует', 'слишком', 'смогут', 'сможет', 'сначала', 'снова', 'со', 'собой', 'собою', 'собственно', 'совсем', 'сперва', 'спокону', 'спустя', 'сразу', 'среди', 'сродни', 'стал', 'стала', 'стали', 'стало', 'стать', 'суть', 'сызнова', 'та', 'так', 'такая', 'также', 'таки', 'такие', 'таким', 'такими', 'таких', 'таков', 'такова', 'такого', 'такое', 'такой', 'таком', 'такому', 'такою', 'такую', 'там', 'тая', 'твои', 'твоим', 'твоих', 'твой', 'твоя', 'твоё', 'те', 'тебе', 'тебя', 'тем', 'теми', 'теперь', 'тех', 'ти', 'то', 'тобой', 'тобою', 'тогда', 'того', 'тоже', 'той', 'только', 'том', 'томах', 'тому', 'тот', 'тотчас', 'точно', 'тою', 'ту', 'туда', 'тут', 'ты', 'тьфу', 'у', 'увы', 'уж', 'уже', 'ура', 'ух', 'ую', 'ф', 'фу', 'х', 'ха', 'хе', 'хорошо', 'хотел', 'хотела', 'хотелось', 'хотеть', 'хоть', 'хотя', 'хочешь', 'хочу', 'хуже', 'ч', 'часто', 'чаще', 'чего', 'чей', 'чем', 'чему', 'через', 'что', 'чтоб', 'чтобы', 'чуть', 'чхать', 'чьим', 'чьих', 'чьё', 'чё', 'чём', 'ш', 'ша', 'щ', 'ща', 'щас', 'ы', 'ые', 'ый', 'ых', 'э', 'эдак', 'эдакий', 'эй', 'эка', 'экий', 'эта', 'этак', 'этакий', 'эти', 'этим', 'этими', 'этих', 'это', 'этого', 'этой', 'этом', 'этому', 'этот', 'этою', 'эту', 'эх', 'ю', 'я', 'явно', 'явных', 'яко', 'якобы', 'якоже']
['!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~']

Найденные в тексте стоп-слова

In [10]:
stops = [token.text for token in doc if token.is_stop or token.is_punct or token.is_digit]
print(stops)
['Накануне', ',', '7', ',', 'в', '«', 'Они', 'у', '»', '.', 'Она', '', 'начала', ',', 'и', 'и', '.']

Текст без стоп-слов

In [11]:
without_stops = [token for token in doc if not token.is_stop and not token.is_punct and not token.is_digit]
print(without_stops)
[Дня, российской, науки, февраля, Ульяновском, государственном, техническом, университете, открыли, новую, экспозицию, стояли, истоков, ульяновской, науки, посвящена, выдающимся, ученым, XX, XXI, веков, чьи, достижения, изобретения, сформировали, научный, технологический, облик, Ульяновской, области]

Стемминг

Стеммер Портера

In [12]:
from nltk.stem.porter import PorterStemmer

porter = PorterStemmer()

print(list(map(porter.stem, map(str, without_stops))))
['дня', 'российской', 'науки', 'февраля', 'ульяновском', 'государственном', 'техническом', 'университете', 'открыли', 'новую', 'экспозицию', 'стояли', 'истоков', 'ульяновской', 'науки', 'посвящена', 'выдающимся', 'ученым', 'xx', 'xxi', 'веков', 'чьи', 'достижения', 'изобретения', 'сформировали', 'научный', 'технологический', 'облик', 'ульяновской', 'области']

Стеммер Snowball

In [13]:
from nltk.stem.snowball import SnowballStemmer

snowball = SnowballStemmer(language="russian")

print(list(map(snowball.stem, map(str, without_stops))))
['дня', 'российск', 'наук', 'феврал', 'ульяновск', 'государствен', 'техническ', 'университет', 'откр', 'нов', 'экспозиц', 'стоя', 'исток', 'ульяновск', 'наук', 'посвящ', 'выда', 'учен', 'XX', 'XXI', 'век', 'чьи', 'достижен', 'изобретен', 'сформирова', 'научн', 'технологическ', 'облик', 'ульяновск', 'област']

Лемматизация

In [14]:
print([token.lemma_ for token in without_stops])
['день', 'российский', 'наука', 'февраль', 'ульяновский', 'государственный', 'технический', 'университет', 'открыть', 'новый', 'экспозиция', 'стоять', 'исток', 'ульяновский', 'наука', 'посвятить', 'выдающийся', 'учёный', 'xx', 'xxi', 'век', 'чей', 'достижение', 'изобретение', 'сформировать', 'научный', 'технологический', 'облик', 'ульяновский', 'область']

Морфологический анализ (POS tagging)

In [15]:
print([f"{token.text} {token.pos_}" for token in without_stops])
['Дня PROPN', 'российской ADJ', 'науки NOUN', 'февраля NOUN', 'Ульяновском ADJ', 'государственном ADJ', 'техническом ADJ', 'университете NOUN', 'открыли VERB', 'новую ADJ', 'экспозицию NOUN', 'стояли VERB', 'истоков NOUN', 'ульяновской ADJ', 'науки NOUN', 'посвящена VERB', 'выдающимся ADJ', 'ученым NOUN', 'XX ADJ', 'XXI ADJ', 'веков NOUN', 'чьи DET', 'достижения NOUN', 'изобретения NOUN', 'сформировали VERB', 'научный ADJ', 'технологический ADJ', 'облик NOUN', 'Ульяновской ADJ', 'области NOUN']
In [16]:
print([f"{token.text} [{token.morph}]" for token in without_stops])
['Дня [Animacy=Inan|Case=Gen|Gender=Masc|Number=Sing]', 'российской [Case=Gen|Degree=Pos|Gender=Fem|Number=Sing]', 'науки [Animacy=Inan|Case=Gen|Gender=Fem|Number=Sing]', 'февраля [Animacy=Inan|Case=Gen|Gender=Masc|Number=Sing]', 'Ульяновском [Case=Loc|Degree=Pos|Gender=Masc|Number=Sing]', 'государственном [Case=Loc|Degree=Pos|Gender=Masc|Number=Sing]', 'техническом [Case=Loc|Degree=Pos|Gender=Masc|Number=Sing]', 'университете [Animacy=Inan|Case=Loc|Gender=Masc|Number=Sing]', 'открыли [Aspect=Perf|Mood=Ind|Number=Plur|Tense=Past|VerbForm=Fin|Voice=Act]', 'новую [Case=Acc|Degree=Pos|Gender=Fem|Number=Sing]', 'экспозицию [Animacy=Inan|Case=Acc|Gender=Fem|Number=Sing]', 'стояли [Aspect=Imp|Mood=Ind|Number=Plur|Tense=Past|VerbForm=Fin|Voice=Act]', 'истоков [Animacy=Inan|Case=Gen|Gender=Masc|Number=Plur]', 'ульяновской [Case=Gen|Degree=Pos|Gender=Fem|Number=Sing]', 'науки [Animacy=Inan|Case=Gen|Gender=Fem|Number=Sing]', 'посвящена [Aspect=Perf|Gender=Fem|Number=Sing|StyleVariant=Short|Tense=Past|VerbForm=Part|Voice=Pass]', 'выдающимся [Case=Dat|Degree=Pos|Number=Plur]', 'ученым [Animacy=Anim|Case=Dat|Gender=Masc|Number=Plur]', 'XX []', 'XXI []', 'веков [Animacy=Inan|Case=Gen|Gender=Masc|Number=Plur]', 'чьи [Case=Nom|Number=Plur]', 'достижения [Animacy=Inan|Case=Nom|Gender=Neut|Number=Plur]', 'изобретения [Animacy=Inan|Case=Nom|Gender=Neut|Number=Plur]', 'сформировали [Aspect=Perf|Mood=Ind|Number=Plur|Tense=Past|VerbForm=Fin|Voice=Act]', 'научный [Animacy=Inan|Case=Acc|Degree=Pos|Gender=Masc|Number=Sing]', 'технологический [Animacy=Inan|Case=Acc|Degree=Pos|Gender=Masc|Number=Sing]', 'облик [Animacy=Inan|Case=Acc|Gender=Masc|Number=Sing]', 'Ульяновской [Case=Gen|Degree=Pos|Gender=Fem|Number=Sing]', 'области [Animacy=Inan|Case=Gen|Gender=Fem|Number=Sing]']

Синтаксический анализ (Dependency parsing)

In [17]:
print(text)
print(
    "\n".join(
        [
            f"{token.text} | {token.dep_} | {token.head.text} | {token.head.pos_} {[child.text for child in token.children]}"
            for token in sp(text.split(".")[0])
        ]
    )
)
Накануне Дня российской науки, 7 февраля, в Ульяновском государственном техническом университете открыли новую экспозицию «Они стояли у истоков ульяновской науки». Она посвящена выдающимся ученым XX  начала XXI веков, чьи достижения и изобретения сформировали научный и технологический облик Ульяновской области.
Накануне | case | Дня | PROPN []
Дня | obl | открыли | VERB ['Накануне', 'науки']
российской | amod | науки | NOUN []
науки | nmod | Дня | PROPN ['российской']
, | punct | 7 | ADJ []
7 | obl | открыли | VERB [',', 'февраля', ',']
февраля | flat | 7 | ADJ []
, | punct | 7 | ADJ []
в | case | университете | NOUN []
Ульяновском | amod | университете | NOUN []
государственном | amod | университете | NOUN []
техническом | amod | университете | NOUN []
университете | obl | открыли | VERB ['в', 'Ульяновском', 'государственном', 'техническом']
открыли | ROOT | открыли | VERB ['Дня', '7', 'университете', 'экспозицию', 'стояли']
новую | amod | экспозицию | NOUN []
экспозицию | obj | открыли | VERB ['новую']
« | punct | стояли | VERB []
Они | nsubj | стояли | VERB []
стояли | parataxis | открыли | VERB ['«', 'Они', 'истоков', '»']
у | case | истоков | NOUN []
истоков | obl | стояли | VERB ['у', 'науки']
ульяновской | amod | науки | NOUN []
науки | nmod | истоков | NOUN ['ульяновской']
» | punct | стояли | VERB []
In [18]:
from spacy import displacy

displacy.render(sp(text.split(".")[0]), style="dep")
Накануне ADP Дня PROPN российской ADJ науки, NOUN 7 ADJ февраля, NOUN в ADP Ульяновском ADJ государственном ADJ техническом ADJ университете NOUN открыли VERB новую ADJ экспозицию « NOUN Они PRON стояли VERB у ADP истоков NOUN ульяновской ADJ науки» NOUN case obl amod nmod obl flat case amod amod amod obl amod obj nsubj parataxis case obl amod nmod

Обнаружение именованных сущностей

In [19]:
print([f"{entity.lemma_} {entity.label_}" for entity in sp(text).ents])
print([f"{entity.lemma_} {entity.label_}" for entity in sp(text_ner).ents])
['ульяновский государственный технический университет ORG', 'ульяновский область LOC']
['ульяновский институт гражданский авиация имя главный маршал авиация ORG', 'б.п.бугаева PER', 'сергей косачевскому PER', 'ульяновский государственный педагогический университет имя и.н.ульянова ORG', 'светлане богатов PER', 'ульяновский государственный аграрный университет имя п.а.столыпина ORG', 'винере насыровой PER', 'ульяновский государственный университет ORG', 'татьяна лисовой PER']
In [20]:
displacy.render(sp(text_ner), style="ent")
В рамках торжественного открытия экспозиции за активное участие в подготовке материалов были вручены благодарственные письма профессору кафедры летной эксплуатации и безопасности полетов Ульяновского института гражданской авиации имени Главного маршала авиации ORG Б.П.Бугаева PER Сергею Косачевскому PER , начальнику управления научно-исследовательской и инновационной деятельности Ульяновского государственного педагогического университета имени И.Н.Ульянова ORG Светлане Богатовой PER , руководителю пресс-службы Ульяновского государственного аграрного университета имени П.А.Столыпина ORG Винере Насыровой PER , помощнику проректора по научной работе Ульяновского государственного университета ORG Татьяне Лисовой PER .

Векторизация

Мешок слов (BoW, Bag of Words)

In [21]:
import pandas as pd
from scipy import sparse
from sklearn.feature_extraction.text import CountVectorizer

counts_vectorizer = CountVectorizer()
counts_matrix = sparse.csr_matrix(counts_vectorizer.fit_transform([text, text_ner]))
counts_df = pd.DataFrame(
    counts_matrix.toarray(),
    index=["text", "text_ner"],
    columns=counts_vectorizer.get_feature_names_out(),
)
counts_df
Out[21]:
xx xxi авиации аграрного активное безопасности благодарственные богатовой бугаева были ... университета университете управления участие ученым февраля чьи эксплуатации экспозиции экспозицию
text 1 1 0 0 0 0 0 0 0 0 ... 0 1 0 0 1 1 1 0 0 1
text_ner 0 0 2 1 1 1 1 1 1 1 ... 3 0 1 1 0 0 0 1 1 0

2 rows × 87 columns

Пропуск термов, которые содержатся не менее чем в двух документах

In [22]:
counts_min_vectorizer = CountVectorizer(min_df=2)
counts_min_matrix = sparse.csr_matrix(
    counts_min_vectorizer.fit_transform(
        [text, text_ner, text, text_ner, text, text_ner]
    )
)
counts_min_df = pd.DataFrame(
    counts_min_matrix.toarray(),
    index=["text", "text_ner", "text1", "text_ner1", "text2", "text_ner2"],
    columns=counts_min_vectorizer.get_feature_names_out(),
)
counts_min_df
Out[22]:
xx xxi авиации аграрного активное безопасности благодарственные богатовой бугаева были ... университета университете управления участие ученым февраля чьи эксплуатации экспозиции экспозицию
text 1 1 0 0 0 0 0 0 0 0 ... 0 1 0 0 1 1 1 0 0 1
text_ner 0 0 2 1 1 1 1 1 1 1 ... 3 0 1 1 0 0 0 1 1 0
text1 1 1 0 0 0 0 0 0 0 0 ... 0 1 0 0 1 1 1 0 0 1
text_ner1 0 0 2 1 1 1 1 1 1 1 ... 3 0 1 1 0 0 0 1 1 0
text2 1 1 0 0 0 0 0 0 0 0 ... 0 1 0 0 1 1 1 0 0 1
text_ner2 0 0 2 1 1 1 1 1 1 1 ... 3 0 1 1 0 0 0 1 1 0

6 rows × 87 columns

Пропуск термов, которые содержатся более чем в двух документах

In [23]:
counts_max_vectorizer = CountVectorizer(max_df=2)
counts_max_matrix = sparse.csr_matrix(
    counts_max_vectorizer.fit_transform(
        [text, text_ner, text, text_ner, text, text_ner]
    )
)
counts_max_df = pd.DataFrame(
    counts_max_matrix.toarray(),
    index=["text", "text_ner", "text1", "text_ner1", "text2", "text_ner2"],
    columns=counts_max_vectorizer.get_feature_names_out(),
)
counts_max_df
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[23], line 3
      1 counts_max_vectorizer = CountVectorizer(max_df=2)
      2 counts_max_matrix = sparse.csr_matrix(
----> 3     counts_max_vectorizer.fit_transform(
      4         [text, text_ner, text, text_ner, text, text_ner]
      5     )
      6 )
      7 counts_max_df = pd.DataFrame(
      8     counts_max_matrix.toarray(),
      9     index=["text", "text_ner", "text1", "text_ner1", "text2", "text_ner2"],
     10     columns=counts_max_vectorizer.get_feature_names_out(),
     11 )
     12 counts_max_df

File ~/Projects/python/ckmai/.venv/lib/python3.12/site-packages/sklearn/base.py:1473, in _fit_context.<locals>.decorator.<locals>.wrapper(estimator, *args, **kwargs)
   1466     estimator._validate_params()
   1468 with config_context(
   1469     skip_parameter_validation=(
   1470         prefer_skip_nested_validation or global_skip_validation
   1471     )
   1472 ):
-> 1473     return fit_method(estimator, *args, **kwargs)

File ~/Projects/python/ckmai/.venv/lib/python3.12/site-packages/sklearn/feature_extraction/text.py:1385, in CountVectorizer.fit_transform(self, raw_documents, y)
   1383 if max_features is not None:
   1384     X = self._sort_features(X, vocabulary)
-> 1385 X = self._limit_features(
   1386     X, vocabulary, max_doc_count, min_doc_count, max_features
   1387 )
   1388 if max_features is None:
   1389     X = self._sort_features(X, vocabulary)

File ~/Projects/python/ckmai/.venv/lib/python3.12/site-packages/sklearn/feature_extraction/text.py:1237, in CountVectorizer._limit_features(self, X, vocabulary, high, low, limit)
   1235 kept_indices = np.where(mask)[0]
   1236 if len(kept_indices) == 0:
-> 1237     raise ValueError(
   1238         "After pruning, no terms remain. Try a lower min_df or a higher max_df."
   1239     )
   1240 return X[:, kept_indices]

ValueError: After pruning, no terms remain. Try a lower min_df or a higher max_df.

n-граммы

In [24]:
counts_ng_vectorizer = CountVectorizer(ngram_range=(1, 2))
counts_ng_matrix = sparse.csr_matrix(
    counts_ng_vectorizer.fit_transform([text, text_ner])
)
counts_ng_df = pd.DataFrame(
    counts_ng_matrix.toarray(),
    index=["text", "text_ner"],
    columns=counts_ng_vectorizer.get_feature_names_out(),
)
counts_ng_df
Out[24]:
xx xx начала xxi xxi веков авиации авиации бугаева авиации имени аграрного аграрного университета активное ... февраля февраля ульяновском чьи чьи достижения эксплуатации эксплуатации безопасности экспозиции экспозиции за экспозицию экспозицию они
text 1 1 1 1 0 0 0 0 0 0 ... 1 1 1 1 0 0 0 0 1 1
text_ner 0 0 0 0 2 1 1 1 1 1 ... 0 0 0 0 1 1 1 1 0 0

2 rows × 181 columns

Ограничение количества термов

In [25]:
counts_mf_vectorizer = CountVectorizer(max_features=15)
counts_mf_matrix = sparse.csr_matrix(
    counts_mf_vectorizer.fit_transform([text, text_ner])
)
counts_mf_df = pd.DataFrame(
    counts_mf_matrix.toarray(),
    index=["text", "text_ner"],
    columns=counts_mf_vectorizer.get_feature_names_out(),
)
counts_mf_df
Out[25]:
xx авиации государственного имени науки пресс проректора профессору работе рамках российской руководителю ульяновского ульяновской университета
text 1 0 0 0 2 0 0 0 0 0 1 0 0 2 0
text_ner 0 2 3 3 0 1 1 1 1 1 0 1 4 0 3

Частотный портрет

$tfidf(t, d) = tf(t, d) * idf(t, D)$, \ где $tf(t, d)$ - частота терма $t$ в документе $d$; \ $idf$ - обратная частота терма (мера информативности терма $t$ в рамках всей коллекции документов $D$).

$tf(t, d) = \frac { f_{td} } { \sum_{t' \in d} f_{t'd} } $, \ где $f_{td}$ - количество терма $t$ в документе $d$; \ $\sum_{t' \in d} f_{t'd}$ - количество всех термов в документе $d$, является суммой $f_{td}$ всех термов документа $d$.

$idf(t, D) = \log ( \frac { N + 1} { | { d : d \in D, t \in d } | +1 } $ ) , \ где $N$ - общее количество документов; \ $| { d : d \in D, t \in d } |$ - количество документов, в которых есть термин $t$.

При использовании TfidfVectorizer $tf(t, d) = f_{td} $ (аналогично BoW).

При включении параметра sublinear_tf=True $tf(t, d) = 1 + log ( f_{td} ) $, что уменьшает степень влияния терминов с высокой частотой на значение $tfidf(t, d)$.

In [26]:
from sklearn.feature_extraction.text import TfidfVectorizer

tfidf_vectorizer = TfidfVectorizer(sublinear_tf=True)
tfidf_matrix = sparse.csr_matrix(tfidf_vectorizer.fit_transform([text, text_ner]))
tfidf_df = pd.DataFrame(
    tfidf_matrix.toarray(),
    index=["text", "text_ner"],
    columns=tfidf_vectorizer.get_feature_names_out(),
)
tfidf_df
Out[26]:
xx xxi авиации аграрного активное безопасности благодарственные богатовой бугаева были ... университета университете управления участие ученым февраля чьи эксплуатации экспозиции экспозицию
text 0.167287 0.167287 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 ... 0.000000 0.167287 0.000000 0.000000 0.167287 0.167287 0.167287 0.000000 0.000000 0.167287
text_ner 0.000000 0.000000 0.199854 0.118037 0.118037 0.118037 0.118037 0.118037 0.118037 0.118037 ... 0.247713 0.000000 0.118037 0.118037 0.000000 0.000000 0.000000 0.118037 0.118037 0.000000

2 rows × 87 columns

Эмбединги

In [27]:
print([f"{token.text} {token.vector_norm}" for token in sp(text)])
print([f"{token.text} {token.vector_norm}" for token in sp(text_ner)])

print(sp(text).similarity(sp(text_ner)))
print(sp("Мама мыла раму").similarity(sp("Биологический родитель выполнял очистку каркаса окна")))
['Накануне 0.0', 'Дня 0.0', 'российской 6.628802299499512', 'науки 6.097537994384766', ', 0.0', '7 0.0', 'февраля 5.738062381744385', ', 0.0', 'в 5.39321756362915', 'Ульяновском 0.0', 'государственном 6.0524067878723145', 'техническом 5.820169925689697', 'университете 6.496365070343018', 'открыли 5.944686412811279', 'новую 5.83096170425415', 'экспозицию 5.348505020141602', '« 0.0', 'Они 0.0', 'стояли 5.839313507080078', 'у 5.27208137512207', 'истоков 5.396897315979004', 'ульяновской 6.163735866546631', 'науки 6.097537994384766', '» 0.0', '. 0.0', 'Она 0.0', 'посвящена 6.200736999511719', 'выдающимся 5.983486175537109', 'ученым 5.918715000152588', 'XX 0.0', ' 0.0', 'начала 5.412599086761475', 'XXI 0.0', 'веков 5.634378910064697', ', 0.0', 'чьи 5.3263936042785645', 'достижения 5.957095623016357', 'и 3.6682209968566895', 'изобретения 5.965053081512451', 'сформировали 5.379479885101318', 'научный 5.801889419555664', 'и 3.6682209968566895', 'технологический 5.863436222076416', 'облик 5.85204553604126', 'Ульяновской 0.0', 'области 6.532813549041748', '. 0.0']
['В 0.0', 'рамках 5.911857604980469', 'торжественного 5.679202556610107', 'открытия 6.117740154266357', 'экспозиции 5.659939765930176', 'за 5.40718412399292', 'активное 5.857132911682129', 'участие 6.153576374053955', 'в 5.39321756362915', 'подготовке 6.464332103729248', 'материалов 6.255794048309326', 'были 6.02486515045166', 'вручены 5.769386291503906', 'благодарственные 6.248109340667725', 'письма 6.082029342651367', 'профессору 5.6763691902160645', 'кафедры 6.288632392883301', 'летной 6.138020992279053', 'эксплуатации 6.849708557128906', 'и 3.6682209968566895', 'безопасности 6.110499382019043', 'полетов 6.38402795791626', 'Ульяновского 0.0', 'института 6.3007354736328125', 'гражданской 6.207894802093506', 'авиации 6.574370861053467', 'имени 5.757457256317139', 'Главного 0.0', 'маршала 6.116937160491943', 'авиации 6.574370861053467', 'Б.П.Бугаева 0.0', 'Сергею 0.0', 'Косачевскому 0.0', ', 0.0', 'начальнику 6.195803165435791', 'управления 6.50614070892334', 'научно 6.042668342590332', '- 0.0', 'исследовательской 5.603431224822998', 'и 3.6682209968566895', 'инновационной 6.336301803588867', 'деятельности 6.13490104675293', 'Ульяновского 0.0', 'государственного 6.192478656768799', 'педагогического 6.430647373199463', 'университета 6.620550632476807', 'имени 5.757457256317139', 'И.Н.Ульянова 0.0', 'Светлане 0.0', 'Богатовой 0.0', ', 0.0', 'руководителю 5.767575740814209', 'пресс 5.993161201477051', '- 0.0', 'службы 6.049290657043457', 'Ульяновского 0.0', 'государственного 6.192478656768799', 'аграрного 6.049352169036865', 'университета 6.620550632476807', 'имени 5.757457256317139', 'П.А.Столыпина 0.0', 'Винере 0.0', 'Насыровой 0.0', ', 0.0', 'помощнику 5.360434532165527', 'проректора 5.608104228973389', 'по 5.825357437133789', 'научной 5.987777233123779', 'работе 5.538445949554443', 'Ульяновского 0.0', 'государственного 6.192478656768799', 'университета 6.620550632476807', 'Татьяне 0.0', 'Лисовой 0.0', '. 0.0']
0.7208023892475828
0.2978442641202898

Пример анализа текстов

Загрузка данных из документов

Столбец type позволяет использовать методы обучения с учителем для построения классификатора

In [28]:
import pandas as pd
from docx import Document
import os


def read_docx(file_path):
    doc = Document(file_path)
    full_text = []
    for paragraph in doc.paragraphs:
        full_text.append(paragraph.text)
    return "\n".join(full_text)

def load_docs(dataset_path):
    df = pd.DataFrame(columns=["doc", "text"])
    for file_path in os.listdir(dataset_path):
        if file_path.startswith("~$"):
            continue
        text = read_docx(dataset_path + file_path)
        df.loc[len(df.index)] = [file_path, text]
    return df


df = load_docs("data/text/")
df["type"] = df.apply(
    lambda row: 0 if str(row["doc"]).startswith("tz_") else 1, axis=1
)
df.info()
df.sort_values(by=["doc"], inplace=True)

display(df.head(), df.tail())
<class 'pandas.core.frame.DataFrame'>
Index: 41 entries, 0 to 40
Data columns (total 3 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   doc     41 non-null     object
 1   text    41 non-null     object
 2   type    41 non-null     int64 
dtypes: int64(1), object(2)
memory usage: 1.3+ KB
doc text type
16 tz_01.docx 2.2 Техническое задание\n2.2.1 Общие сведения\... 0
19 tz_02.docx 2.2 Техническое задание\n2.2.1 Общие сведения\... 0
28 tz_03.docx 2.2. Техническое задание\nОбщие сведения:\nВ д... 0
35 tz_04.docx Техническое задание\n2.2.1 Общие сведения\nИнт... 0
38 tz_05.docx 2.2 Техническое задание\n2.2.1 Общие сведения.... 0
doc text type
25 Этапы разработки проекта2.docx Этапы разработки проекта: заключительные стади... 1
21 Этапы разработки проекта3.docx Этапы разработки проекта: определение стратеги... 1
40 Этапы разработки проекта4.docx Этапы разработки проекта: реализация, тестиров... 1
30 Этапы разработки проекта5.docx Этапы разработки проекта: стратегия и анализ\n... 1
22 Язык манипуляции данными.docx 2.1.3. Язык манипуляции данными (ЯМД)\nЯзык ма... 1

Векторизация документов в виде мешка слов

In [29]:
counts_vectorizer = CountVectorizer()
counts_matrix = sparse.csr_matrix(counts_vectorizer.fit_transform(df["text"]))
words = counts_vectorizer.get_feature_names_out()
df["vector"] = df.apply(lambda row: counts_matrix.toarray()[row.name], axis=1)

display(df.head(), df.tail())
doc text type vector
16 tz_01.docx 2.2 Техническое задание\n2.2.1 Общие сведения\... 0 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
19 tz_02.docx 2.2 Техническое задание\n2.2.1 Общие сведения\... 0 [0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, ...
28 tz_03.docx 2.2. Техническое задание\nОбщие сведения:\nВ д... 0 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
35 tz_04.docx Техническое задание\n2.2.1 Общие сведения\nИнт... 0 [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
38 tz_05.docx 2.2 Техническое задание\n2.2.1 Общие сведения.... 0 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
doc text type vector
25 Этапы разработки проекта2.docx Этапы разработки проекта: заключительные стади... 1 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
21 Этапы разработки проекта3.docx Этапы разработки проекта: определение стратеги... 1 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
40 Этапы разработки проекта4.docx Этапы разработки проекта: реализация, тестиров... 1 [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
30 Этапы разработки проекта5.docx Этапы разработки проекта: стратегия и анализ\n... 1 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
22 Язык манипуляции данными.docx 2.1.3. Язык манипуляции данными (ЯМД)\nЯзык ма... 1 [0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...

Вывод термов с частотой больше threshold для каждого документа

In [30]:
def get_terms(doc_name, words, threshold=0):
    important_words = [
        word
        for word, score in zip(words, df.iloc[doc_name]["vector"])
        if score > threshold
    ]
    return ", ".join(important_words)


df["top_terms"] = df.apply(lambda row: get_terms(row.name, words, threshold=7), axis=1)

display(df.head(), df.tail())
doc text type vector top_terms
16 tz_01.docx 2.2 Техническое задание\n2.2.1 Общие сведения\... 0 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... информации, требования
19 tz_02.docx 2.2 Техническое задание\n2.2.1 Общие сведения\... 0 [0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, ... анализа, на, по, предприятия, работ, системы, ...
28 tz_03.docx 2.2. Техническое задание\nОбщие сведения:\nВ д... 0 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... данных, для, записей, знаний, из, или, ко, мно...
35 tz_04.docx Техническое задание\n2.2.1 Общие сведения\nИнт... 0 [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... дата, для, заказ, заказа, информации, код, мат...
38 tz_05.docx 2.2 Техническое задание\n2.2.1 Общие сведения.... 0 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... apache, cgi, html, iis, java, jdbc, linux, net...
doc text type vector top_terms
25 Этапы разработки проекта2.docx Этапы разработки проекта: заключительные стади... 1 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... 2010, на, по, программы
21 Этапы разработки проекта3.docx Этапы разработки проекта: определение стратеги... 1 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... данных, для, на, ошибки, по, работ, системы, т...
40 Этапы разработки проекта4.docx Этапы разработки проекта: реализация, тестиров... 1 [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... create, doctors, sql, бд, данных, для, на, соз...
30 Этапы разработки проекта5.docx Этапы разработки проекта: стратегия и анализ\n... 1 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... быть, должна, система, системы
22 Язык манипуляции данными.docx 2.1.3. Язык манипуляции данными (ЯМД)\nЯзык ма... 1 [0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... быть, данных, для, должен, должна, должны, инф...

Четкая неиерархическая кластеризация документов

In [31]:
from sklearn.cluster import KMeans
import numpy

num_clusters = 2
kmeans = KMeans(n_clusters=num_clusters, random_state=9)
kmeans.fit(sparse.csr_matrix(list(df["vector"])))

for cluster_id in range(num_clusters):
    cluster_indices = numpy.where(kmeans.labels_ == cluster_id)[0]
    print(f"Кластер {cluster_id + 1} ({len(cluster_indices)}):")
    cluster_docs = [df.iloc[idx]["doc"] for idx in cluster_indices]
    print("; ".join(cluster_docs))
    print("--------")
Кластер 1 (30):
tz_01.docx; tz_02.docx; tz_03.docx; tz_06.docx; tz_07.docx; tz_08.docx; tz_10.docx; tz_11.docx; tz_14.docx; tz_16.docx; tz_17.docx; tz_20.docx; Архитектура, управляемая модель.docx; Введение в проектирование ИС.docx; Встроенные операторы SQL.docx; Методологии разработки программного обеспечения 2.docx; Методологии разработки программного обеспечения.docx; Методы композиции и декомпозиции.docx; Модели представления данных в СУБД.docx; Некоторые особенности проектирования.docx; Непроцедурный доступ к данным.docx; Процедурное расширение языка SQL.docx; Системные объекты базы данных.docx; Технология создания распр ИС.docx; Требования к проекту.docx; Условия целостности БД.docx; Характеристики СУБД.docx; Этапы разработки проекта1.docx; Этапы разработки проекта5.docx; Язык манипуляции данными.docx
--------
Кластер 2 (11):
tz_04.docx; tz_05.docx; tz_09.docx; tz_12.docx; tz_13.docx; tz_15.docx; tz_18.docx; tz_19.docx; Этапы разработки проекта2.docx; Этапы разработки проекта3.docx; Этапы разработки проекта4.docx
--------