Итак, наш шаблон проверки работает. Давайте решим следующую задачу: необходимо посчитать количество адресов в файле email_base.csv в разбивке по почтовым доменам. Т. е. посчитать сколько адресов принадлежат yandex.ru, сколько - gmail.com итд. А если адрес не похож на email, то необходимо посчитать количество таких строк как 'wrong email'
Для решения этой задачи сначала выделим имя пользователя и домен. Для этого укажем эти email-адреса части в скобках шаблона регулярного выражения:
pattern = '([\w\.-]+)@([\w]+(\.ru|\.com))'
В поиске нам поможет метод search. Перебор результатов можно делать с помощью метода group, в качестве аргумента пишем номер группы в скобках:
# то что стоит в первых скобках
re.search(pattern, 'username@yandex.ru').group(1)
Результат: 'username'
# то что стоит во вторых скобках
re.search(pattern, 'username@yandex.ru').group(2)
Результат: 'yandex.ru'
# то что стоит во внутренних скобках
re.search(pattern, 'username@yandex.ru').group(3)
Результат: '.ru'
Теперь оформим проверку соответствия регулярному выражению в функцию. Если адрес не подходит под шаблон, то ставим 'wrong email':
def get_email_domain(row):
if re.match(pattern, row['email']):
return re.search(pattern, row['email']).group(2)
else:
return 'wrong email'
Применяем функцию к датафрейму email и пишем результат в столбец 'domain':
emails['domain'] = emails.apply(get_email_domain, axis = 1)
emails.head()
Теперь можем получить распределение почтовых систем для нашей базы:
%matplotlib inline
emails['domain'].hist()
Упражнение
Какое количество строк содержит некорректные email-адреса в таблице emails?
нет ответа
Выделение email-адресов из текста
Регулярные выражения дают еще одну крайне полезную возможность выделить все шаблоны в тексте. Давайте получим из следующего текста адреса электронной почты:
text = 'Андрей Марков страхование markov_chains@yandex.ru. Мария Кюри технологии mary_decay@gmail.com Петр Капица онлайн-образование study-hard@rambler.ru'
Шаблон с прошлого шага:
pattern = '([\w\.-]+)@([\w]+(\.ru|\.com))'
Для решения этой задачи используем метод findall:
re.findall(pattern, text)
Результат:
[('markov_chains', 'yandex.ru', '.ru'),
('mary_decay', 'gmail.com', '.com'),
('study-hard', 'rambler.ru', '.ru')]
Мы получили набор групп group шаблона pattern. Давайте изменим порядок скобок, чтобы не разделять имя пользователя и домен:
pattern = '([\w\.-]+@[\w]+(\.ru|\.com))'
Посмотрим как изменился результат:
re.findall(pattern, text)
Результат:
[('markov_chains@yandex.ru', '.ru'),
('mary_decay@gmail.com', '.com'),
('study-hard@rambler.ru', '.ru')]
Теперь в первом элементе каждой строки выводится искомый email-адрес. Запишем итоговый алгоритм:
for address in re.findall(pattern, text):
print(address[0])
Результат:
markov_chains@yandex.ru
mary_decay@gmail.com
study-hard@rambler.ru
Упражнение
Необходимо получить из текста названия почтовых доменов. Т. е. yandex, gmail, rambler. Как нужно изменить шаблон регулярного выражения, чтобы выделить эти названия из текста?
([\w\.-]+@([\w]+)(\.ru|\.com)) (([\w\.-]+)@[\w]+(\.ru|\.com)) это невозможно
- Этот вопрос оставлен без ответа.
Постановка задачи
Итак, давайте применим новые знания для решения следующей задачи: имеется список отзывов о ресторанах. Необходимо разделить их на положительные и отрицательные. В случае невозможности классификации ставить 'undef'.
Файл с отзывами texts_opinions.txt
Файл с отзывами имеет кодировку UTF-8 и может некорректно отображаться при открытии в браузере.
Для классификации для каждого отзыва будем использовать следующий алгоритм:
- Предварительно составляем список основ слов, которые характеризуют положительные и отрицательные отзывы. Используем SnowballStemmer библиотеки NLTK (можно было использовать Pymystem, сейчас для простоты используем NLTK).
- Разбиваем текст отзыва на отдельные слова (здесь нам заранее придется удалить все знаки препинания).
- Заменяем каждое слово на его основу с помощью SnowballStemmer библиотеки NLTK.
- Ищем основу каждого слова отзыва среди основ слов из пункта 1. Считаем каких слов получилось больше - из списка "положительных" или "отрицательных" слов. Если таких слов в отзыве не нашлось или их число совпало, то возвращаем 'undef'.
После классификации отзывов мы можем сверить наш результат с "правильной" классификацией texts_ratings.txt, которую выставляли сами пользователи, когда писали эти отзывы.
Файл texts_ratings.txt
Несколько замечаний:
- Сейчас мы не рассматриваем качество и правдивость составленных отзывов. Наша задача - научиться пользоваться инструментами для подобных задач.
- Существует множество алгоритмов решения этой задачи, в том числе с помощью машинного обучения. Абсолютное большинство из них начинаются также как и наш - со стемминга или лемматизации исходных текстов.
- Аналогичный подход может сильно помочь вам в задачах фильтрации данных по словам.
Разбивка на слова
Итак, сначала составим список основ слов, которые характерны для положительных и отрицательных отзывов. Чтобы не учитывать их многочисленные формы используем стеммер NLTK. Например, найдем основу слова "благодарны":
from nltk.stem import SnowballStemmer
snowball_stemmer = SnowballStemmer("russian")
snowball_stemmer.stem('благодарны')
Результат:
'благодарн'
Аналогичную основу будут иметь многие вариации этого слова:
for word in ['благодарность', 'благодарностью', 'благодарны']:
print(snowball_stemmer.stem(word))
Результат:
благодарн
благодарн
благодарн
Мы взяли несколько простых вариантов "положительных" и "отрицательных" наборов слов, чтобы получить самый простой классификатор. Запишем их в файл params.yaml.
Основы слов params.yaml
Теперь напишем алгоритм классификатора. Сначала импортируем список слов в переменную params:
from yaml import load
params = load(open('params.yaml', mode = 'r', encoding = 'utf-8'))
Сначала необходимо удалить из текста знаки препинания, чтобы его можно легко было разбить на слова через пробел. Воспользуемся методом translate. Метод берет список знаков препинания symbols и применяет к ним метод translate. При этом заменяя их на пробелы. Для этого мы заводим строку spaces из такого же количества пробелов, что и symbols:
def clear_punctuation(text):
"""Удаление знаком пунктуации из текста text"""
symbols = '.,!()"<>'
spaces = ' ' * len(symbols)
return text.translate(text.maketrans(symbols, spaces))
Проверяем как работает:
clear_punctuation('Просто шикарный клуб! Ходили с другом на "Animal Джаz"! Остались очень довольны, атмосфера очень уютная, дружелюбная, есть второй этаж, бар')
Результат:
'Просто шикарный клуб Ходили с другом на Animal Джаz Остались очень довольны атмосфера очень уютная дружелюбная есть второй этаж бар'
Теперь этот текст легко можно разделить на слова, используя пробел в качестве разделителя.