<jupyter_start><jupyter_text>Задание 5.1 Набор данных тут: https://github.com/sismetanin/rureviews, также есть в папке [Data](https://drive.google.com/drive/folders/1YAMe7MiTxA-RSSd8Ex2p-L0Dspe6Gs4L). Те, кто предпочитает работать с английским языком, могут использовать набор данных `sms_spam`.<jupyter_code>import pandas as pd
import numpy as np
import io

from sklearn.metrics import * 
from sklearn.model_selection import train_test_split 
from google.colab import files
uploaded = files.upload()
spam = pd.read_csv(io.BytesIO(uploaded['sms_spam.csv']))
spam<jupyter_output><empty_output><jupyter_text>Применим полученные навыки и решим задачу анализа тональности отзывов. Нужно повторить весь пайплайн от сырых текстов до получения обученной модели.Обязательные шаги предобработки:1. токенизация2. приведение к нижнему регистру3. удаление стоп-слов4. лемматизация5. векторизация (с настройкой гиперпараметров)6. построение модели7. оценка качества моделиОбязательно использование векторайзеров:1. мешок n-грамм (диапазон для n подбирайте самостоятельно, запрещено использовать только униграммы).2. tf-idf ((диапазон для n подбирайте самостоятельно, также нужно подбирать параметры max_df, min_df, max_features)3. символьные n-граммы (диапазон для n подбирайте самостоятельно)В качестве классификатора нужно использовать наивный байесовский классификатор. Для сравнения векторайзеров между собой используйте precision, recall, f1-score и accuracy. Для этого сформируйте датафрейм, в котором в строках будут разные векторайзеры, а в столбцах разные метрики качества, а в  ячейках будут значения этих метрик для соответсвующих векторайзеров. **Решение** 1) токенизация<jupyter_code>import nltk # уже знакомая нам библиотека nltk
from nltk.tokenize import word_tokenize # готовый токенизатор библиотеки nltk
nltk.download('stopwords')
nltk.download('punkt')
word_tokenized_list = []
for sms in spam.text:
  words = word_tokenize(sms)
  new_words = [word for word in words if word.isalnum()]
  word_tokenized_list.append(new_words)
len(word_tokenized_list)<jupyter_output><empty_output><jupyter_text>2) приведение к нижнему регистру<jupyter_code>for i in range(len(word_tokenized_list)):
  for j in range(len(word_tokenized_list[i])):
    word_tokenized_list[i][j] = word_tokenized_list[i][j].lower()<jupyter_output><empty_output><jupyter_text>3) удаление стоп-слов<jupyter_code># импортируем стоп-слова из библиотеки nltk
from nltk.corpus import stopwords
print(stopwords.words("english"))
noise = stopwords.words("english")

word_list_without_noize = []
for i in range(len(word_tokenized_list)):
  element = []
  for j in range(len(word_tokenized_list[i])):
    if word_tokenized_list[i][j] not in noise:
      element.append(word_tokenized_list[i][j])
  word_list_without_noize.append(element)<jupyter_output><empty_output><jupyter_text>4) лемматизация<jupyter_code># устанавливаем pymorphy2
!pip install pymorphy2
from pymorphy2 import MorphAnalyzer
pymorphy2_analyzer = MorphAnalyzer()
word_list_with_lemmatization = []
for sentence in word_list_without_noize:
  current_sentence = ""
  for word in sentence:
    current_sentence = current_sentence + " " + pymorphy2_analyzer.parse(word)[0].normal_form
  word_list_with_lemmatization.append(current_sentence)<jupyter_output><empty_output><jupyter_text>5) векторазирация<jupyter_code>x_train, x_test, y_train, y_test = train_test_split(word_list_with_lemmatization, spam.type, train_size = 0.7)
from sklearn.naive_bayes import MultinomialNB # наивный байесовский классификатор
from sklearn.feature_extraction.text import CountVectorizer # модель "мешка слов", см. далее
history = []
median_length_word = 0
words = 0
for sentence in word_list_with_lemmatization:
  for word in sentence:
    median_length_word += len(word)
  words += len(sentence.split())
median_length_word /= words
median_length_word<jupyter_output><empty_output><jupyter_text>5.1) мешок n-грамм<jupyter_code>for i in range (2, 6):
  for j in range (i, 6):
    vectorizer = CountVectorizer(ngram_range=(i, j))
    vectorized_x_train = vectorizer.fit_transform(x_train)
    clf = MultinomialNB()
    clf.fit(vectorized_x_train, y_train)

    vectorized_x_test = vectorizer.transform(x_test)
    pred = clf.predict(vectorized_x_test)
    print(f"n-framm from {i} to {j}")
    print(classification_report(y_test, pred))

    history_element = []
    history_element.append("CountVectorizer")
    history_element.append(1.0)
    history_element.append(1.0)
    history_element.append(None)
    history_element.append(f"({i}, {j})")
    weighted_avg = precision_recall_fscore_support(y_test, pred, average='weighted')
    history_element.append(weighted_avg[0])
    history_element.append(weighted_avg[1])
    history_element.append(weighted_avg[2])
    history_element.append(accuracy_score(y_test, pred))
    history.append(history_element)<jupyter_output>n-framm from 2 to 2
              precision    recall  f1-score   support

         ham       0.96      1.00      0.98      1428
        spam       0.97      0.78      0.87       240

    accuracy                           0.97      1668
   macro avg       0.97      0.89      0.92      1668
weighted avg       0.97      0.97      0.96      1668

n-framm from 2 to 3
              precision    recall  f1-score   support

         ham       0.97      1.00      0.98      1428
        spam       0.97      0.79      0.87       240

    accuracy                           0.97      1668
   macro avg       0.97      0.89      0.92      1668
weighted avg       0.97      0.97      0.96      1668

n-framm from 2 to 4
              precision    recall  f1-score   support

         ham       0.97      1.00      0.98      1428
        spam       0.97      0.79      0.87       240

    accuracy                           0.97      1668
   macro avg       0.97      0.89      0.92      1668
weighted avg  [...]<jupyter_text>5.2) tf-idf<jupyter_code>from sklearn.feature_extraction.text import TfidfVectorizer
for i in range (1, 4):
  for j in range (i, 4):
    for max_features in [500, 1500, 4000]:
      for min_df in [0.0, 0.0025]:
        for max_df in [0.01, 1.0]:
          if (max_df < min_df):
            continue
          
          tfidf_vectorizer = TfidfVectorizer(ngram_range=(i, j), max_df=max_df, min_df=min_df, max_features=max_features)
          tfidf_vectorized_x_train = tfidf_vectorizer.fit_transform(x_train)

          clf = MultinomialNB()
          clf.fit(tfidf_vectorized_x_train, y_train)

          tfidf_vectorized_x_test = tfidf_vectorizer.transform(x_test)

          # получаем предсказания и выводим информацию о качестве
          pred = clf.predict(tfidf_vectorized_x_test)
          print(f"n-framm from {i} to {j}")
          print(f"min_df = {min_df}; max_df = {max_df}; max_features = {max_features}")
          print(classification_report(y_test, pred))

          history_element = []
          history_element.append("TfidfVectorizer")
          history_element.append(min_df)
          history_element.append(max_df)
          history_element.append(max_features)
          history_element.append(f"({i}, {j})")
          weighted_avg = precision_recall_fscore_support(y_test, pred, average='weighted')
          history_element.append(weighted_avg[0])
          history_element.append(weighted_avg[1])
          history_element.append(weighted_avg[2])
          history_element.append(accuracy_score(y_test, pred))
          history.append(history_element)<jupyter_output>n-framm from 1 to 1
min_df = 0.0; max_df = 0.01; max_features = 500
              precision    recall  f1-score   support

         ham       0.95      0.99      0.97      1428
        spam       0.94      0.68      0.78       240

    accuracy                           0.95      1668
   macro avg       0.94      0.83      0.88      1668
weighted avg       0.95      0.95      0.94      1668

n-framm from 1 to 1
min_df = 0.0; max_df = 1.0; max_features = 500
              precision    recall  f1-score   support

         ham       0.96      1.00      0.98      1428
        spam       0.96      0.75      0.84       240

    accuracy                           0.96      1668
   macro avg       0.96      0.87      0.91      1668
weighted avg       0.96      0.96      0.96      1668

n-framm from 1 to 1
min_df = 0.0025; max_df = 0.01; max_features = 500
              precision    recall  f1-score   support

         ham       0.95      0.99      0.97      1428
        spam       0.94      0.[...]<jupyter_text>5.3) символьные n-граммы<jupyter_code>for i in range (3, 8):
  for j in range (i, 8):
    vectorizer = CountVectorizer(analyzer='char', ngram_range=(i, j))
    vectorized_x_train = vectorizer.fit_transform(x_train)
    clf = MultinomialNB()
    clf.fit(vectorized_x_train, y_train)

    vectorized_x_test = vectorizer.transform(x_test)
    pred = clf.predict(vectorized_x_test)
    print(f"n-gramm from {i} to {j}")
    print(classification_report(y_test, pred))

    history_element = []
    history_element.append("CountVectorizer - analyzer=char")
    history_element.append(1.0)
    history_element.append(1.0)
    history_element.append(None)
    history_element.append(f"({i}, {j})")
    weighted_avg = precision_recall_fscore_support(y_test, pred, average='weighted')
    history_element.append(weighted_avg[0])
    history_element.append(weighted_avg[1])
    history_element.append(weighted_avg[2])
    history_element.append(accuracy_score(y_test, pred))
    history.append(history_element)<jupyter_output>n-gramm from 3 to 3
              precision    recall  f1-score   support

         ham       0.98      0.99      0.98      1428
        spam       0.91      0.86      0.88       240

    accuracy                           0.97      1668
   macro avg       0.94      0.92      0.93      1668
weighted avg       0.97      0.97      0.97      1668

n-gramm from 3 to 4
              precision    recall  f1-score   support

         ham       0.98      0.98      0.98      1428
        spam       0.91      0.89      0.90       240

    accuracy                           0.97      1668
   macro avg       0.94      0.94      0.94      1668
weighted avg       0.97      0.97      0.97      1668

n-gramm from 3 to 5
              precision    recall  f1-score   support

         ham       0.98      0.98      0.98      1428
        spam       0.90      0.89      0.90       240

    accuracy                           0.97      1668
   macro avg       0.94      0.94      0.94      1668
weighted avg  [...]<jupyter_text>Итог:<jupyter_code>df = pd.DataFrame(history, columns=['Vectorize', 'min_df', 'max_df', 'max_features', 'n-gramm', 'precision', 'recall', 'f1-score', 'accuracy'])
df.sort_values(by=["accuracy"], ascending=False).head(20)<jupyter_output><empty_output><jupyter_text>Задание 5.2 Регулярные выраженияРегулярные выражения - способ поиска и анализа строк. Например, можно понять, какие даты в наборе строк представлены в формате DD/MM/YYYY, а какие - в других форматах. Или бывает, например, что перед работой с текстом, надо почистить его от своеобразного мусора: упоминаний пользователей, url и так далее.Навык полезный, давайте в нём тоже потренируемся.Для работы с регулярными выражениями есть библиотека **re**<jupyter_code>import re<jupyter_output><empty_output><jupyter_text>В регулярных выражениях, кроме привычных символов-букв, есть специальные символы:* **?а** - ноль или один символ **а*** **+а** - один или более символов **а*** **\*а** - ноль или более символов **а** (не путать с +)* **.** - любое количество любого символаПример:Выражению \*a?b. соответствуют последовательности a, ab, abc, aa, aac НО НЕ abb! Рассмотрим подробно несколько наиболее полезных функций:  findallвозвращает список всех найденных непересекающихся совпадений.Регулярное выражение **ab+c.**: * **a** - просто символ **a*** **b+** - один или более символов **b*** **c** - просто символ **c*** **.** - любой символ<jupyter_code>result = re.findall('ab+c.', 'abcdefghijkabcabcxabc') 
print(result)<jupyter_output>['abcd', 'abca']<jupyter_text>Вопрос на внимательность: почему нет abcx? **Ответ** Как сказано вначале, метод возвращает список всех найденных *непересекающихся* совпадений. Так как метод нашёл 'abca', то 'abcx' он уже не может выделить - они имеют пересечение в тексте в виде подстроки 'a'. **Задание**: вернуть список первых двух букв каждого слова в строке, состоящей из нескольких слов.<jupyter_code>result_findall = re.findall('(?:\s|^)\w{2}', 'Example: a Docker image is a recipe for running a containerized process.')
result_findall<jupyter_output><empty_output><jupyter_text>splitразделяет строку по заданному шаблону<jupyter_code>result = re.split(',', 'itsy, bitsy, teenie, weenie') 
print(result)<jupyter_output>['itsy', ' bitsy', ' teenie', ' weenie']<jupyter_text>можно указать максимальное количество разбиений<jupyter_code>result = re.split(',', 'itsy, bitsy, teenie, weenie', maxsplit=2) 
print(result)<jupyter_output>['itsy', ' bitsy', ' teenie, weenie']<jupyter_text>**Задание**: разбейте строку, состоящую из нескольких предложений, по точкам, но не более чем на 3 предложения.<jupyter_code>sentence_1 = 'Docker is a Linux container management toolkit with a “social” aspect, letting users publish container images and consume those published by others.'
sentence_2 = ' A Docker image is a recipe for running a containerized process.'
sentence_3 = ' In this guide, we build one for a simple Spring boot application.'
sentence_4 = ' If you are NOT using a Linux machine, you need a virtualized server.'
sentence_5 = ' If you install VirtualBox, other tools like the Mac’s boot2docker can seamlessly manage it for you.'
sentence_6 = ' Visit VirtualBox’s download site and pick the version for your machine.'
sentence_7 = ' Download and install.'
sentence_8 = ' Do not worry about actually running it.'
text = sentence_1 + sentence_2 + sentence_3 + sentence_4 + sentence_5 + sentence_6 + sentence_7 + sentence_8
result = re.split('\.', text, maxsplit=2)
print(result)<jupyter_output>['Docker is a Linux container management toolkit with a “social” aspect, letting users publish container images and consume those published by others', ' A Docker image is a recipe for running a containerized process', ' In this guide, we build one for a simple Spring boot application. If you are NOT using a Linux machine, you need a virtualized server. If you install VirtualBox, other tools like the Mac’s boot2docker can seamlessly manage it for you. Visit VirtualBox’s download site and pick the version for your machine. Download and install. Do not worry about actually running it.']<jupyter_text>subищет шаблон в строке и заменяет все совпадения на указанную подстрокупараметры: (pattern, repl, string)<jupyter_code>result = re.sub('a', 'b', 'abcabc')
print (result)<jupyter_output>bbcbbc<jupyter_text>**Задание**: напишите регулярное выражение, которое позволит заменить все цифры в строке на "DIG".<jupyter_code>result = re.sub('[0-9]', 'DIG', 'Код для входа в приложение: 1234.')
print(result)<jupyter_output>Код для входа в приложение: DIGDIGDIGDIG.<jupyter_text>**Задание**: напишите  регулярное выражение, которое позволит убрать url из строки.<jupyter_code>result = re.sub('(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w\.-]*)*\/?', '', 'регулярные выражения онлайн: https://regex101.com/')
result<jupyter_output><empty_output><jupyter_text>compileкомпилирует регулярное выражение в отдельный объект<jupyter_code># Пример: построение списка всех слов строки:
prog = re.compile('[А-Яа-яё\-]+')
prog.findall("Слова? Да, больше, ещё больше слов! Что-то ещё.")<jupyter_output><empty_output><jupyter_text>**Задание**: для выбранной строки постройте список слов, которые длиннее трех символов.<jupyter_code>prog = re.compile('[А-Яа-яё\-]{4,}')
prog.findall("Слова? Да, больше, ещё больше слов! Что-то ещё.")<jupyter_output><empty_output><jupyter_text>**Задание**: вернуть список доменов (@gmail.com) из списка адресов электронной почты:```<EMAIL>, <EMAIL>, <EMAIL>, <EMAIL>```<jupyter_code>prog = re.compile('@[a-z0-9_\.-]+\.[a-z\.]{2,6}')
prog.findall("<EMAIL>, <EMAIL>, <EMAIL>, <EMAIL>")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10030921459492888, 'fraction_numerical': 0.0396413110698825, 'mean_word_length': 3.3191773504273505, 'pattern_counts': {'":': 0, '<': 89, '<?xml version=': 0, '>': 88, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 29, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '22', 'dir': 'jupyter-structured-clean-dedup', 'id': '302762', 'license': 'MIT', 'n_tokens_mistral': 6190, 'n_tokens_neox': 6202, 'n_words': 1386, 'path': 'Machine Learning/lab5/lab_5.ipynb', 'repo_name': 'richiurb/ITMO_labs', 'size': '16270'}