创建用于协作过滤产品推荐的矩阵的方法

时间:2018-12-04 13:51:00

标签: python machine-learning scikit-learn recommendation-engine

我正在探索python中的推荐系统,到目前为止,我已经使用KNN模型来建议采用“您也购买了...等用户”的品牌。我的数据表中每个客户都有一行,每个品牌都有一列,其中填充了10,以表明客户是否购买了该品牌。

我现在想将其推广到产品级别的建议,但仍在努力寻找这种方法的扩展方式。我尝试了相同的方法,但是无法使用足够大的查询来查询数据库(BigQuery)以为每个产品(10,000+)生成一列。

例如,我的来源是导出到BigQuery的Google Analytics(分析)每日数据,我将根据以下示例创建输入数据:

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?

这时我很沮丧,所以任何建议都将受到欢迎。

2 个答案:

答案 0 :(得分:1)

我不确定如何在BigQuery(或SQL的任何方言)中有效地创建所需的1-0(单热式)编码,但我绝对知道如何在Python中进行编码。

聚集这些数据以供在Python中使用的最有效方法可能是执行以下操作...

您的BigQuery表似乎遵循以下结构:

enter image description here

this question中,您可以使用以下方法将每个SKU聚合为一行:

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

这应该给你(从上面的示例表中获得):

enter image description here

从那里开始,很容易在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来衡量每个用户的购买向量之间的角度。

答案 1 :(得分:0)

通常,当我们的sql查询看起来更像服务器日志(很长很长)时,可能是时候重新考虑数据的策略和结构以及尝试围绕它设计方法了。

在您的特定情况下,您尝试使用绝对元素构造查询,这通常不是一个好习惯。因此,您需要做的就是将您的skus(全部)转储到BigQuery表中。完成此操作后,您就可以在BigQuery中使用ARRAYS来生成一次性编码(不是这样)。以下是使用公共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