Эта статья является продолжением нашей серии статей об анализе данных о потребительских товарах: Сбор данных с чеков гипермаркетов на Python и Парсинг данных каталога сайта с помощью Beautiful Soup и Selenium». Мы собираемся построить модель, которая будет классифицировать товары по наименованию в кассовом чеке. Квитанции Till содержат данные по каждому купленному продукту, но не дают сводной информации о том, сколько в целом было потрачено на сладости или молочные продукты.
Обработка данных
Загрузите данные из нашего файла .csv в Pandas DataFrame и посмотрите, как это выглядит:
import pandas as pd
sku = pd.read_csv('SKU_igoods.csv',sep=';')
sku.head()
Знаете ли вы, что мы можем эмулировать поведение человека для анализа данных из веб-каталога? Подробнее об этом в статье: «Парсинг данных каталога сайта с помощью Beautiful Soup и Selenium»
Как видите, DataFrame содержит даже больше, чем нам нужно для предсказания категории товаров по названию. Таким образом, мы можем удалить () столбцы с ценами и весами, а оставшиеся переименовать ():
sku.drop(columns=[‘Unnamed: 0’, ‘Weight’,’Price’],inplace=True) sku.rename(columns={“SKU”: “SKU”, “Category”: “Group”},inplace=True) sku.head()
Сгруппируйте продукты по категориям и подсчитайте их следующими способами:
sku.groupby('Group').agg(['count'])
Мы будем обучать нашу прогностическую модель на этих данных, чтобы она могла идентифицировать категорию продукта по названию. Так как DataFrame включает в себя названия продуктов в основном на русском языке, модель не будет правильно делать прогнозы. В русском языке много предлогов, союзов и специфических речевых оборотов. Мы хотим, чтобы в нашей модели различалось, что «Мангал с ребрами жесткости» («Мангал с ребрами жесткости») и «Мангал с 6 шампурами» («Мангал с 6 шампурами») относятся к одной категории. С помощью этого my нам нужно очистить все названия продуктов, удалив союзы, предлоги, междометия, частицы и оставить только основы слов с помощью стемминга.
Стеммер — это инструмент, работающий по принципу распознавания «основных» слов, вложенных в другие слова.
import nltk from nltk.corpus import stopwords from pymystem3 import Mystem from string import punctuation nltk.download('stopwords')
В нашем случае будет использоваться библиотека pymystem3, разработанная Яндексом. Названия продуктов в нашем DataFrame могут отличаться от тех, которые вы можете найти в супермаркетах сегодня. Итак, сначала давайте улучшим список стоп-слов, которые наша прогностическая модель будет игнорировать.
mystem = Mystem() russian_stopwords = stopwords.words("russian") russian_stopwords.extend(['лента','ассорт','разм','арт','что', 'это', 'так', 'вот', 'быть', 'как', 'в', '—', 'к', 'на'])
Напишите функцию, которая предварительно обработает наши данные и извлечет базу слов, удалит знаки препинания, цифровые знаки и стоп-слова. Следующий фрагмент кода принадлежит одному ядру Kaggle.
def preprocess_text(text): text = str(text) tokens = mystem.lemmatize(text.lower()) tokens = [token for token in tokens if token not in russian_stopwords\ and token != " " \ and len(token)>=3 \ and token.strip() not in punctuation \ and token.isdigit()==False] text = " ".join(tokens) return text
Посмотри, как это работает:
Отрывок из Бородино (русский: Бородино), стихотворения русского поэта Михаила Лермонтова, в котором описывается Бородинская битва.
preprocess_text("Мой дядя самых честных правил, Когда не в шутку занемог, Он уважать себя заставил И лучше выдумать не мог.")
Превратился в:
'дядя самый честный правило шутка занемогать уважать заставлять выдумывать мочь'
Все работает как положено — результат включает только основы слов в нижнем регистре без знаков препинания, предлогов и союзов. Давайте применим эту функцию к названию продукта из нашего DataFrame:
print(‘Before:’, sku['SKU'][0])
print(‘After:’, preprocess_text(sku['SKU'][0]))
Предварительно обработанный текст:
Before: Фисташки соленые жареные ТМ 365 дней
After: фисташка соленый жареный день
Функция отлично работает, и теперь мы можем применить ее ко всей колонке и создать новую с обработанными именами:
sku['processed']=sku['SKU'].apply(preprocess_text)
sku.head()
Построение нашей прогностической модели
Мы будем использовать CountVectorizer для прогнозирования категории продукта и Наивный байесовский классификатор. CountVectorizer разметит наш текст и создаст словарь известных слов, а Naive Bayes Classifier позволит нам обучать нашу модель на DataFrame с несколькими классами. Также нам понадобится TfidfTransformer для подсчета количества слов (частоты терминов). Поскольку мы хотим связать эти шаги в цепочку, давайте импортируем библиотеку Pipeline:
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.naive_bayes import MultinomialNB
from imblearn.pipeline import Pipeline
Отделите наши цели Y (категории) от предикторов X (названия обрабатываемых продуктов). И разделите DataFrame на наборы Test и Training, выделив 33% выборок для тестирования.
x = sku.processed
y = sku.Group
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.33)
Добавьте в наш пайплайн следующие методы:
- CountVectorizer() — возвращает матрицу количества токенов.
- TfidfTransformer() — преобразует матрицу в нормализованное представление tf-idf.
- MultinomialNB() — алгоритм прогнозирования категории товара
text_clf = Pipeline([('vect', CountVectorizer(ngram_range=(1,2))),
('tfidf', TfidfTransformer()),
('clf', MultinomialNB())])
Сопоставьте нашу модель с обучающим набором данных и сделайте прогнозы для тестового набора данных:
text_clf = text_clf.fit(X_train, y_train)
y_pred = text_clf.predict(X_test)
Оцените нашу прогностическую модель:
print('Score:', text_clf.score(X_test, y_test))
Модель предсказывает правильно в 90% случаев.
Score: 0.923949864498645
Подтвердите нашу модель реальными данными
Давайте проверим, насколько хорошо наша модель работает на реальных данных. Мы обратимся к DataFrame из нашей предыдущей статьи «Сбор данных из чеков гипермаркетов на Python» и предварительно обработаем названия продуктов:
my_products['processed']=my_products['name'].apply(preprocess_text)
my_products.head()
Передайте обработанный текст в модель и создайте новый столбец, который будет содержать наши прогнозы:
prediction = text_clf.predict(my_products['processed'])
my_products['prediction']=prediction
my_products[['name', 'prediction']]
Теперь DataFrame выглядит следующим образом:
Рассчитайте расходы для каждой категории продуктов:
my_products.groupby('prediction').sum()
В целом, модель кажется надежной в прогнозировании того, что колбасы относятся к мясным продуктам, творог относится к молочным продуктам, а багет относится к хлебу и выпечке. Но иногда неправильно классифицирует киви как молочный продукт, а грушу как экопродукт. Вероятно, это связано с тем, что в эти категории входит много продуктов «со вкусом груши» или «со вкусом киви», а алгоритм делает прогнозы на основе преобладающей группы продуктов. Это хорошо известная проблема несбалансированных классов, но ее можно решить, передискретизировав набор данных или выбрав правильные веса для нашей модели.
Понравилась эта статья? Не забудь поставить 👏 и подписаться :)
Присоединяйтесь к нам на других платформах: