Подходы к созданию матрицы для совместной рекомендации по продуктам

1

Я изучаю системы рекомендаций в python, до сих пор я использовал модель KNN, чтобы предлагать бренды с методологией "пользователи, такие как вы также приобрели...". В моей таблице данных есть строка для каждого клиента и столбец для каждой марки, заполненный 1 или 0 чтобы указать, приобрел ли клиент этот бренд.

Теперь я хотел бы отнестись к рекомендациям уровня продукта, но изо всех сил стараюсь понять, как этот подход будет масштабироваться. Я пробовал такой же подход, но не смог запросить мою базу данных (BigQuery) с запросом, достаточным для создания столбца для каждого продукта (10, 000+).

Например, мой источник - это ежедневные данные Google Analytics, экспортированные в BigQuery, и я создаю свои входные данные в примере ниже:

SELECT
  customDimension.value AS UserID,
  MAX(IF(UPPER(hits_product.productSKU) LIKE "SKU1",1,0)) AS SKU1,
  MAX(IF(UPPER(hits_product.productSKU) LIKE "SKU2",1,0)) AS SKU2,
  MAX(IF(UPPER(hits_product.productSKU) LIKE "SKU3",1,0)) AS SKU3
  # plus 10,000 more...
  FROM
  'PROJECT.DATASET.ga_sessions_20*' AS t
CROSS JOIN
  UNNEST (hits) AS hits
CROSS JOIN
  UNNEST(t.customdimensions) AS customDimension
CROSS JOIN
  UNNEST(hits.product) AS hits_product
WHERE
  parse_DATE('%y%m%d',
    _table_suffix) BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 1 day)
  AND DATE_SUB(CURRENT_DATE(), INTERVAL 1 day)
  AND customDimension.index = 2
  AND customDimension.value NOT IN ("true","false","undefined")
  AND customDimension.value IS NOT NULL
  AND hits.eventInfo.eventCategory = 'Ecommerce'
  AND hits.eventInfo.eventAction = 'Purchase'
GROUP BY
  UserID

Выполнение этого запроса с помощью строки для каждого SKU генерирует ошибку:

Запрос слишком велик. Максимальная длина запроса составляет 256.000K символов, включая комментарии и символы пробела.

Как создать рекомендации по уровню продукта в этом случае? Являются ли данные, которые обычно попадают в python в другой форме и преобразуются в maxrix в коде?

На данный момент я полностью потрясен, поэтому любые предложения были бы наиболее желанными.

  • 0
    Не могли бы вы уточнить, что «... не удалось запросить мою базу данных (BigQuery) с запросом, достаточно большим, чтобы сгенерировать столбец для каждого продукта (более 10000)»? Похоже, вы не можете выполнить это расширение 1-0 в BigQuery, не так ли? Чтобы ответить на ваш вопрос, да, вы можете сделать это создание матрицы в Python. Вам просто нужно с осторожностью относиться к измерениям ваших данных, если вы также хотите выполнить рекомендацию «математика» в Python, в зависимости от характеристик вашего компьютера ... Сколько клиентов (строк) и сколько столбцов (продукты) будут созданы?
  • 0
    Еще одна вещь, которую вы могли бы сделать, это принять подход к продуктам в стиле Парето - ограничить количество продуктов, которые входят в область рекомендаций. Как выглядит распределение покупок вашего продукта? Это довольно сильно перекос вправо? Я полагаю, что это может распространиться и на клиентов - не давать рекомендаций для нижнего процента клиентов с наименьшей стоимостью ...
Показать ещё 3 комментария
Теги:
machine-learning
scikit-learn
recommendation-engine

2 ответа

0

Обычно время от времени, когда наши запросы sql больше напоминают журналы сервера (длинные и длинные), возможно, это время, чтобы пересмотреть стратегию и структуру данных и попытаться спроектировать вокруг нее.

В вашем конкретном случае вы пытаетесь построить запрос с абсолютными элементами, что обычно не является хорошей практикой. Итак, вам нужно сбросить свой skus (все из них) в таблицу BigQuery. Как только это будет сделано, вы можете использовать ARRAYS в BigQuery для генерации ваших горячих кодировок (а что нет). Вот краткая справка, использующая общедоступные данные GA:

with listskus as (
  -- this is fake data. 
  -- replace it with your sku listing query (i.e. select sku as listsku from myskutable)
  select 
    listsku from 
    unnest(generate_array(0, 11000, 1)) 
  as listsku
),
data as (
  select 
    visitId as userid,
    array(
      (
        select 
          if(p.productSKU like concat('%',cast(l.listsku as string)), 1, 0) 
        from unnest(hits.product) p 
        left outer join listskus l on 1=1
      )
    ) as onehotvector
  from 
  'bigquery-public-data.google_analytics_sample.ga_sessions_20170801', 
  unnest(hits) hits
)
select userid, onehotvector from data
0

Я не уверен, как эффективно создать эту кодировку 1-0 (one-hot-esque), которую вы хотите в BigQuery (или действительно любой диалект SQL), но я определенно знаю, как это сделать на Python.

Вероятно, наиболее эффективным способом агрегирования этих данных для использования в Python было бы сделать что-то вроде следующего...

Похоже, ваша таблица BigQuery следует этой структуре:

Изображение 174551

Из этого вопроса, похоже, вы можете объединить каждый SKU в одну строку, используя что-то вроде этого:

SELECT UserID, STRING_AGG(SKU) AS SKU_string FROM my_transactions_table GROUP BY UserID

Который должен дать вам это (возьмите примерную таблицу сверху):

Изображение 174551

Оттуда, это действительно легко потреблять эти данные в Python:

>>> import pandas as pd
>>> df = pd.read_csv('~/Desktop/test.csv', sep='\t')
>>> df
   UserID SKU_string
0       1      a,b,c
1       2        b,b
2       3      c,b,a

Мы можем использовать scikit-learn класс CountVectorizer для подсчета вхождения каждого продукта для каждого пользователя:

>>> from sklearn.feature_extraction.text import CountVectorizer
>>> vec = CountVectorizer(tokenizer=lambda x: x.split(','))
>>> X = vec.fit_transform(df['SKU_string'])
>>> X
<3x3 sparse matrix of type '<class 'numpy.int64'>'
    with 7 stored elements in Compressed Sparse Row format>
>>> pd.DataFrame(X.toarray(), columns=vec.get_feature_names())
   a  b  c
0  1  1  1
1  0  2  0
2  1  1  1

Если хотите, присоедините эту матрицу к DataFrame и другим метаданным пользователя, которые вы могли бы выбрать:

>>> df = df.join(pd.DataFrame(X.toarray(), columns=['product_{}'.format(x) for x in vec.get_feature_names()]))
>>> df
   UserID SKU_string  product_a  product_b  product_c
0       1      a,b,c          1          1          1
1       2        b,b          0          2          0
2       3      c,b,a          1          1          1

Тем не менее, я бы скорее всего рекомендовал против этого, если у вас столько разных продуктов, как вы говорите. 10 000 продуктов создают 10 000 дополнительных, не разреженных столбцов, которые могут съесть много памяти, если у вас много клиентов.

Кроме того, если вы хотите преобразовать этот объект X (scipy.sparse.csr_matrix) строго в кодировку с одним нулем, попробуйте следующее:

>>> import numpy as np
>>> import scipy.sparse
>>> def booleanize_csr_matrix(mat):
...     ''' Convert sparse matrix with positive integer elements to 1s '''
...     nnz_inds = mat.nonzero()
...     keep = np.where(mat.data > 0)[0]
...     n_keep = len(keep)
...     result = scipy.sparse.csr_matrix(
...             (np.ones(n_keep), (nnz_inds[0][keep], nnz_inds[1][keep])),
...             shape=mat.shape
...     )
...     return result
... 
>>> pd.DataFrame(booleanize_csr_matrix(X).toarray(), columns=vec.get_feature_names())
     a    b    c
0  1.0  1.0  1.0
1  0.0  1.0  0.0
2  1.0  1.0  1.0

Оттуда вы можете использовать различные алгоритмы для рекомендации элементов на основе пользователя... Вы можете посмотреть на sklearn.metrics.pairwise.cosine_similarity для измерения углов между каждым пользовательским вектором покупки.

  • 0
    Спасибо вам, это похоже на подход, который я могу применить и продвинуть вперед!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню