Использование алгоритма FP-growth для анализа ассоциаций между продуктами супермаркета
Супермаркеты по всему миру используют методы интеллектуального анализа данных для анализа модели покупок пользователей, чтобы сделать свой бизнес более эффективным, тем самым увеличивая свой бизнес и в то же время удовлетворяя потребности клиентов. Одно из таких приложений определяет, какие товары потребитель склонен покупать вместе, и анализирует модели «если-то», чтобы понять, покупает ли потребитель А, которые являются элементами Б, чтобы рекомендовать потребителю. Это помогает им размещать нужные продукты в нужном проходе, тем самым помогая потребителю вспомнить о своих потребностях. Это помогает потребителям запасать свои холодильники и дома в соответствии с их потребностями и в то же время сокращает время покупки.
В этом проекте я буду использовать реальный набор данных Instacart от Kaggle, который содержит данные о 3 миллионах заказов продуктов от 200 000 пользователей. Для каждого пользователя существует около 4–100 различных заказов в зависимости от того, сколько раз они совершали покупки в Instacart. Вот ссылка на набор данных:
Https://www.kaggle.com/c/instacart-market-basket-analysis/data
Я покажу реализацию на Spark из-за того, что она работает очень быстро на Databricks, поскольку использует распределенные внутрипроцессные вычисления и занимает гораздо меньше времени даже на таком большом наборе данных. К счастью, у нас уже есть библиотека FP (поиск часто-шаблонов) на spark, и поэтому я буду использовать ее для понимания шаблонов. Давайте начнем.
Примечание. Databricks предоставляет пользователям возможность бесплатно использовать платформу своего сообщества. Итак, не стесняйтесь реплицировать код и увидеть волшебство поиска паттернов.
Начнем с импорта всех файлов во фрейм данных Spark:
#checking the files we have in the databricks file system %fs ls /FileStore/tables
#enabling arrow just for a backup if pandas is needed at any point of time import numpy as np import pandas as pd #Importing all the available files into the spark dataframe aisles = spark.read.csv("/FileStore/tables/aisles.csv", header=True, inferSchema=True) departments = spark.read.csv("/FileStore/tables/departments.csv", header=True, inferSchema=True) order_products_prior = spark.read.csv("/FileStore/tables/order_products__prior.csv", header=True, inferSchema=True) order_products_train = spark.read.csv("/FileStore/tables/order_products__train.csv", header=True, inferSchema=True) orders = spark.read.csv("/FileStore/tables/orders.csv", header=True, inferSchema=True) products = spark.read.csv("/FileStore/tables/products.csv", header=True, inferSchema=True) # Create Temporary Tables to work using sql like commands aisles.createOrReplaceTempView("aisles") departments.createOrReplaceTempView("departments") order_products_prior.createOrReplaceTempView("order_products_prior") order_products_train.createOrReplaceTempView("order_products_train") orders.createOrReplaceTempView("orders") products.createOrReplaceTempView("products")
Вот словарь данных для всех файлов:
1) заказы (3,4 млн строк, 206 тыс пользователей):
order_id: идентификатор заказа
user_id: идентификатор клиента
eval_set: к какому набору оценок принадлежит этот порядок (см. SET, описанный ниже)
order_number: порядковый номер заказа для этого пользователя (1 = первый, n = n-й)
order_dow: день недели, когда был размещен заказ
order_hour_of_day: час дня, когда был размещен заказ
days_since_prior: количество дней с момента последнего заказа, максимум 30 (с НП для order_number = 1)
2) изделия (50к рядов):
product_id: идентификатор продукта
product_name: название продукта
aisle_id: внешний ключ
Department_id: внешний ключ
3) междурядья (134 ряда):
aisle_id: идентификатор прохода
проход: название прохода
4) отделы (21 ряд):
Department_id: идентификатор отдела
отдел: название отдела
5) order_products__SET (30м + рядов):
order_id: внешний ключ
product_id: внешний ключ
add_to_cart_order: порядок, в котором каждый товар был добавлен в корзину
reordered: 1, если этот продукт был заказан этим пользователем в прошлом, 0 в противном случае, где SET - один из четырех следующих оценочных наборов (eval_set в заказах):
«Предыдущий»: заказы, предшествующие последнему заказу этого пользователя (~ 3,2 млн заказов).
«Поезд»: данные по обучению, предоставленные участникам (~ 131 тыс. Заказов)
«Тест»: тестовые данные, зарезервированные для соревнований по машинному обучению (~ 75 тыс. Заказов)
Давайте теперь исследуем, какие данные у нас есть в разных файлах, используя функцию .show в Spark, и проведем базовый исследовательский анализ данных, чтобы ознакомиться с ними.
#Top 5 orders in the orders dataframe orders.show(n=5)
products.show(n=5)
order_products_train.show(n=5)
order_products_prior.show(n=5)
departments.show(n=5)
aisles.show(n=5)
Давайте теперь посмотрим, как общее количество заказов различается для разных дней недели.
%sql select count(order_id) as total_orders, (case when order_dow = '0' then 'Sunday' when order_dow = '1' then 'Monday' when order_dow = '2' then 'Tuesday' when order_dow = '3' then 'Wednesday' when order_dow = '4' then 'Thursday' when order_dow = '5' then 'Friday' when order_dow = '6' then 'Saturday' end) as day_of_week from orders group by order_dow order by total_orders desc
Большинство заказов Instacart размещаются в воскресенье или понедельник. Похоже, люди вспоминают о своих потребностях сразу после того, как их веселье на выходных заканчивается. Помимо шуток, давайте также посмотрим распределение по времени суток, чтобы проанализировать время, когда большинство пользователей размещают заказ в Instacart.
%sql select count(order_id) as total_orders, order_hour_of_day as hour from orders group by order_hour_of_day order by order_hour_of_day
Как видно из приведенного выше графика, большинство заказов размещается между 10:00 и 16:00. Для меня это было немного неожиданно, так как я ожидал, что пик приходиться на нерабочее время. Теперь давайте посмотрим, какой отдел лидирует по количеству предлагаемых продуктов.
select countbydept.* from ( -- from product table, let's count number of records per dept -- and then sort it by count (highest to lowest) select department_id, count(1) as counter from products group by department_id order by counter asc ) as maxcount inner join ( -- let's repeat the exercise, but this time let's join -- products and departments tables to get a full list of dept and -- prod count select d.department_id, d.department, count(1) as products from departments d inner join products p on p.department_id = d.department_id group by d.department_id, d.department order by products desc ) countbydept -- combine the two queries's results by matching the product count on countbydept.products = maxcount.counter
Отдел личной гигиены, за которым следуют кладовая и замороженные, лидируют по количеству имеющихся в них продуктов. Теперь давайте визуализируем, какие продукты имеют наибольшее количество уникальных заказов, что означает получение самых популярных продуктов.
%sql select count(opp.order_id) as orders, p.product_name as popular_product from order_products_prior opp, products p where p.product_id = opp.product_id group by popular_product order by orders desc limit 10
Бананы, клубника и шпинат лидируют по популярности. На самом деле, большинство лучших продуктов кажутся здоровыми. Когда Америка стала такой здоровой?
Затем мы должны подготовить наши данные в форме, которая может быть использована в алгоритме анализа паттернов (рост числа FP). Нам нужно, чтобы в каждой строке была корзина с товарами, которые были заказаны вместе. Давайте создадим для этого фрейм данных корзины перед подачей в алгоритм.
# Organize the data by shopping basket from pyspark.sql.functions import collect_set, col, count rawData = spark.sql("select p.product_name, o.order_id from products p inner join order_products_train o where o.product_id = p.product_id") baskets = rawData.groupBy('order_id').agg(collect_set('product_name').alias('items')) baskets.createOrReplaceTempView('baskets') rawData.show(5) baskets.show(5) display(baskets)
print((baskets.count(), len(baskets.columns))) (131209, 2)
Всего у нас порядка 1,31,209 корзины товаров. Теперь давайте введем эти данные в алгоритм FPGrowth, доступный в Spark. Прежде чем сделать это, позвольте нам прояснить некоторые термины -
1) Поддержка:
Этот показатель дает представление о том, насколько часто набор элементов встречается во всех транзакциях. Интуитивно понятно, что для любой корзины A поддержка измеряет процент транзакций, содержащих эту корзину как подмножество.
2) Уверенность:
Эта мера определяет вероятность появления консеквента в тележке при условии, что в тележке уже есть антецеденты. Интуитивно предположим, что есть корзина {a, b, c}, имеющая поддержку 's', тогда, если мы анализируем ({a} подразумевает {b, c}), достоверность - это% транзакций, имеющих {a, b, c}, содержащий {b, c}.
3) Лифт:
Поднимите элементы управления для поддержки (частоты) консеквента при вычислении условной вероятности появления {Y} данного {X}. Подъем - самый важный параметр, который супермаркеты используют для размещения продуктов. Думайте об этом как о подъеме, который {X} дает для нашей уверенности при наличии {Y} в тележке. Перефразируя, подъем - это повышение вероятности наличия {Y} в тележке со знанием наличия {X} по сравнению с вероятностью наличия {Y} в тележке без каких-либо сведений о наличии {X}.
Ссылка для понимания этих терминов:
Https://towardsdatascience.com/association-rules-2-aa9a77241654
Давайте установим минимальную поддержку на 0,001, это означает, что для нашего анализа любая корзина, которую мы будем анализировать, должна встречаться по крайней мере 0,001 * 1,31,209 (131) раз, чтобы она учитывалась в нашем анализе частых паттернов.
%scala import org.apache.spark.ml.fpm.FPGrowth // Extract out the items val baskets_ds = spark.sql("select items from baskets").as[Array[String]].toDF("items") // Use FPGrowth val fpgrowth = new FPGrowth().setItemsCol("items").setMinSupport(0.001).setMinConfidence(0) val model = fpgrowth.fit(baskets_ds) %scala // Display frequent itemsets val mostPopularItemInABasket = model.freqItemsets mostPopularItemInABasket.createOrReplaceTempView("mostPopularItemInABasket")
А теперь давайте посмотрим, какая корзина с товарами чаще всего встречается.
%sql select items, freq from mostPopularItemInABasket where size(items) > 2 order by freq desc limit 20
Теперь давайте воспользуемся атрибутом правил ассоциации алгоритма роста FP, чтобы проанализировать ассоциации «если-то» и увидеть значения достоверности и подъема для различных элементов. Чтобы правило было полезным для Instacart, значение подъемной силы должно быть ›1.
%scala // Display generated association rules. val ifThen = model.associationRules ifThen.createOrReplaceTempView("ifThen") %sql select * from ifThen where lift > 1 order by lift desc
Как видно из приведенной выше таблицы, в которой указаны правила уменьшения значений подъемной силы, если кто-то покупает [«Йогурт из клубники и ревеня»], очень высока вероятность покупки [«Йогурта из черники»]
Отображение в порядке уверенности приводит к следующему.
Примечание. Рост количественно определяет силу ассоциации, которая уникальна из-за антецедента, тогда как уверенность - это просто вероятность появления следствия при наличии антецедента.
%sql select antecedent as `antecedent (if)`, consequent as `consequent (then)`, confidence from ifThen order by confidence desc limit 20