Я изучаю системы рекомендаций в 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 в коде?
На данный момент я полностью потрясен, поэтому любые предложения были бы наиболее желанными.
Обычно время от времени, когда наши запросы 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
Я не уверен, как эффективно создать эту кодировку 1-0 (one-hot-esque), которую вы хотите в BigQuery (или действительно любой диалект SQL), но я определенно знаю, как это сделать на Python.
Вероятно, наиболее эффективным способом агрегирования этих данных для использования в Python было бы сделать что-то вроде следующего...
Похоже, ваша таблица BigQuery следует этой структуре:
Из этого вопроса, похоже, вы можете объединить каждый SKU в одну строку, используя что-то вроде этого:
SELECT UserID, STRING_AGG(SKU) AS SKU_string FROM my_transactions_table GROUP BY UserID
Который должен дать вам это (возьмите примерную таблицу сверху):
Оттуда, это действительно легко потреблять эти данные в 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
для измерения углов между каждым пользовательским вектором покупки.