在Postgres中使用Python和Pandas构建推荐引擎

2020-08-15 06:11:52

总的来说,我是数据的铁杆粉丝。数据可以告诉您许多有关用户正在做什么的信息,并可以帮助您获得各种洞察力。其中一个方面是根据过去的历史或其他做出类似选择的人提出建议。事实上,几年前,我写了一个小应用,看看我是否可以根据其他葡萄酒的评级来推荐葡萄酒。这是一个很小的应用程序,我只在几个朋友之间分享了这个应用程序,其中一些人的口味相似,一些人的口味不同。起初,这在很大程度上是一个编写推荐引擎的学术练习,但如果我能在这个过程中找到一些我喜欢的新酒,那就太好了。事实证明,它在推荐东西方面比我预期的要有效得多,即使只有一小部分葡萄酒得到了评级。

我喜欢的另一件事是Postgres(在那里并不令人惊讶),今天早些时候我开始想,为什么我不能直接在它里面做更多的机器学习呢?是的,有Madlib,但是如果我想编写我自己的推荐引擎呢?所以我总共绕行了几个小时,你瞧,我在Postgres大概可以做比我以前意识到的更多的事情。下面是直接在postgres中设置推荐引擎的快速演练。

首先,我快速浏览了Python中的一些示例推荐引擎。为了简单起见,我想要一些更小、更简洁的东西--我不太介意它是否利用了其他库。在这种情况下,我遇到了一个干净的rec-engine示例,它利用了Pandas和一个简单的数据模型来使生活变得更容易。

我获取了示例应用程序使用的确切数据集,并将其转换为SQL以加载它:

创建表ORDERS(id int,product_id int);创建PRODUCTS表(id序列,名称文本);将值(1,1)、(1,2)、(2,3)、(2,10)、(2,13)、(3,3)、(4,8)、(4,9)、(4,12)、(5,3)、(5,5)、(5,7)、(5,12)、(6,1)、(7,5)、(7,13)、(8,4)、(9,3)、(10,3)、(10,13)、(11,1)、(11,8)、(11,4)(13,5),(13,2),(13,7),(14,3),(14,13),(14,5),(15,3),(15,13);在产品(#34;名称&34;)中插入值(';棒球球拍)、(#39;棒球手套)、(#39;足球)、(#39;篮球篮圈)、(#39;足球头盔)、(#39;击球手套)、(#39;棒球#39;)、(#39;棒球#39;)、(#39;棒球#39;)、(#39;棒球#39;)、(#39;棒球#39;)、(#39;棒球#39;)、(#39;棒球#39;)、(#39;棒球#39;)、(#39;棒球#39;)、(#39;棒球#39;)、(#39;棒球#39;)、(#39;棒球#39;)。冰鞋、足球、守门员面具、冰球、冰鞋;

Python示例直接从CSV加载DataFrame。就我而言,我想要波斯格雷斯的一切。在上面的代码中,我拥有表中的所有数据,但是将这些数据放到DataFrame中...。嗯,我并不是很想解析回CSV格式。可能有很多方法可以做到这一点(创建一个JSONB对象,创建一个自定义类型,等等),但是我选择了一些方法,虽然非常简单,但是可以很容易地遵循……。2个阵列以相同方式排序,然后根据它们创建数据帧。

因此,为了做到这一点,我将定义我的功能并进口熊猫:

CREATE OR REPLACE函数getRecommendations(id整数,orderid int[],orderedproducts int[],ProductID int[],ProductNames Text[])返回jsonAS$$import Pandas as PD

如果您注意到,我传入的不是所有订单,而是一个数组或字典,它是2个数组,然后产品是2个数组。为了将数据传递给SQL函数,我会将它们构造为:

下一组数据将与刚刚嵌入到我的PostgreSQL函数中的PYHTON示例相同:

Orders_for_product=Orders[orders.product_id==id].order_id.Unique();Related_Orders=orders[orders.order_id.isin(orders_for_product)]accompanying_products_by_order=Related_Orders[Related_orders.product_id!=id]NUM_INSTANCE_BY_ADVERATING_PRODUCT=accompanying_products_by_order.groupby(";product_id";)[";product_id";].count().reset_index(name=";实例";)NUM_ORDERS_FOR_PRODUCT=ORDERS_FOR_PRODUCT_SIZPRODUCT_INSTANCES=pd.DataFrame(num_instance_by_accompanying_product)product_instances[";frequency";]=product_instances[";instances";]/num_orders_for_productrecommended_products=pd.DataFrame(product_instances.sort_values(";frequency";,升序=FALSE).head(3))。

当我进入到我的产品部分时,我将做与我对订单所做的相同的事情--创建一个字典,然后加载DataFrame。最后,我运行将结果集作为JSONB对象返回。当我将所有这些放在一起时,端到端函数如下所示:

CREATE OR REPLACE函数getRecommendations(id整数,orderid int[],orderedproducts int[],ProductId int[],ProductNames Text[])返回jsonAS$$import熊猫,pd o={';order_id';:orderids,';product_id';:orderedproducts}order=pd.DataFrame(data=o)order_for_product=orders[orders.product_id==id].order_id。Related_Orders=orders[orders.order_id.isin(orders_for_product)]伴随产品_by_Order=相关订单[Related_orders.product_id!=id]NUM_INSTANCE_BY_ADVERSING_PRODUCT=accompanying_products_by_order.groupby(";product_id";)[";product_id";].count().reset_index(name=";instances";)NUM_ORDERS_FOR_PRODUCT=ORDERS_FOR_PRODUCT。SIZE PRODUCT_INSTANCES=pd.DataFrame(num_instance_by_accompanying_product)PRODUCT_INSTANCES[";FREQUENCE";]=product_instances[";instances";]/num_orders_for_product REPORTED_PRODUCTS=pd.DataFrame(product_instances.sort_values(";frequency";,升序=FALSE).head(3))p={';PRODUCT_ID';:ProductID,';名称&#。用法:ProductNames}Products=pd.DataFrame(Data=p)Recommendated_Products=pd.Merge(Recommendated_Products,Products,on=";PRODUCT_ID";)RETURN recommended_products.to_json(orient=";table";)$$Language';plpython3u';;

SELECT JSON_PRYTY(getRecommendations(3,(SELECT ARRAY(SELECT ARRAY(SELECT ID FROM ORDERS ORDERS ORDER BY ORDER BY ID),(SELECT ARRAY(SELECT PRODUCT_ID从ORDERS ORDER BY ID)),(SELECT ARRAY(从产品ORDER BY ID选择名称);{";schema";:{";fields";:[{";name";:";index";,";类型";:";整数";},{";名称";:";product_id";,";类型";:";整数";},{";名称";:";实例";,";类型";:";整数";},{";名称";:";频率&,";类型";:";编号";},{";名称";:";名称";,";类型";:";字符串";}],";PrimiyKey";:[";index";],";PANAS_VERSION";:";0.20.0";},";数据";索引#34;:0,";product_id";:13,";实例";:4,";频率";:0.5714285714,";名称";:";cleats";},{";index";:1,";product_id";:5,";实例";:2,";频率";:0.2857142857,";名称";:";足球头盔";},{";index";:2,";product_id";:7,";实例";:1,";频率";:0.1428571429,";名称";:";棒球";}]}。

仅仅因为你能做某事并不总是意味着你应该做。将所有应用程序逻辑直接嵌入到数据库中可能会使跟踪迁移和发布变得困难。同时,一条复杂的管道每晚进行提取,将一些内容加载到Spark中,生成结果,然后将结果反馈到数据库中,这并不完全是轻量级的。对于plpython3u和pandas,计划使用pg_cron每天运行类似的内容可能是一个简单得多的解决方案。有了SciPy、NumPy和Pandas的组合,这里有很多有趣的潜力,我很想听听其他人提出的@crunchydata有什么实际用途。