GraphGists

介绍

最近,我受到启发,希望将BeachBody健身计划和营养补充剂重塑为图,目标是根据我的用户画像推荐健身计划、营养补充剂和其他用户。对于不熟悉的人来说,BeachBody负责P90X、INSANITY、T25以及多年来他们通过DVD发行的许多其他产品。

项目

在这个推荐演示中,我利用了他们现有网站上的所有公开可用数据,并创建了几个用户画像,以展示将其现有数据转换为Neo4j图数据库的个性化推荐和关联数据优势。我着重提供了以下几个关键推荐:

  • 1. 哪些健身计划最适合帮助我达成我的锻炼目标?

  • 2. 哪些营养补充剂将帮助我实现我的饮食和锻炼目标?

  • 3. 社区中是否有其他用户可以和我一起锻炼,以及哪些锻炼适合我们一起进行?

FitnessAndNutritionRecommendations

概念

网站目前主要存在的概念是健身计划、营养补充剂、装备和社区。在初步处理时,我选择忽略装备。

我认为这些已声明的概念之间能够很好地契合的主要想法,将使我作为他们产品的消费者和社区的参与者会感兴趣的推荐,包括:

  • 锻炼目标

  • 饮食目标

  • 肌肉群

  • 身体部位

  • 锻炼类型

  • 锻炼水平

  • 补充剂类型

……在考虑健身计划时,还有一个关键因素,那就是我作为消费者可能存在的任何身体限制或饮食限制。

设置

加权边

最初,我们将首先探讨用户画像对各种锻炼目标、饮食目标、肌肉群和身体部位所赋予的“重要性”,这将用作评分算法的一部分。

// Order User's Values by Importance
MATCH (u:User {username: "ben"})-[r:VALUES]->(x) RETURN x.name, r.importance ORDER BY r.importance DESC;
// Order User's Desires by Importance
MATCH (u:User {username: "ben"})-[r:DESIRES]->(x) RETURN x.name, r.importance ORDER BY r.importance DESC;

个性化

用户的偏好对于充分了解他们以做出明智的推荐至关重要。在这里,我们从用户“ben”开始,遍历一层,到达我们通过与该用户的互动收集到的所有直接信息。

// Show User's Preferences
MATCH (u:User {username: "ben"}) WITH u MATCH (u)-[:HAS]->(pl:PhysicalLimitation), (u)-[:IS_AT]->(wl:WorkoutLevel), (u)-[:PREFERS]->(wt:WorkoutType), (u)-[:VALUES]->(ba:BodyArea), (u)-[:DESIRES]->(mg:MuscleGroup) RETURN u, pl, wl, wt, ba, mg;

然后,我们查看所有用户,以及他们之间由“x”表示的分类节点。从这些“x”节点中,我们将找到所有与这些x相连的健身计划,这将构成我们的潜在推荐集合。

// Show All Users, Preferences/Classifcations and Fitness Programs
MATCH (u:User)-[]-(x)-[]-(u2:User)
OPTIONAL MATCH (x)-[]-(fp:FitnessProgram)
RETURN u, x, u2, fp;

类似地,我们也可以从饮食目标开始,看看哪些营养补充剂和用户具有这些共同点。

// Explore Nutritional Supplements, Eating Goals and Users
MATCH (eg:EatingGoal) WITH eg OPTIONAL MATCH (eg)-[]-(x) RETURN eg, x;

产品交叉销售

在这里,我们查看哪些饮食目标和锻炼目标可以视为并行关系,并由此可以检查用户没有直接连接时的模式。

// Explore Parallel Eating and Workout Goals - Performance/Fat Burning
MATCH (eg:EatingGoal {name: "Performance"})-[]-(x)-[]-(wg:WorkoutGoal {name: "Fat Burning"}) RETURN eg, x, wg;
// Explore Parallel Eating and Workout Goals - Weight Loss
MATCH (eg:EatingGoal {name: "Weight Loss"})-[]-(x)-[]-(wg:WorkoutGoal {name: "Weight Loss"}) RETURN eg, x, wg;
// Explore Parallel Eating and Workout Goals - Weight Loss/Get Healthy/Wellness
MATCH (eg:EatingGoal)-[]-(x)-[]-(wg:WorkoutGoal {name: "Weight Loss"}) WHERE eg.name = "Get Healthy" OR eg.name = "Wellness" RETURN eg, x, wg;

推荐:为用户推荐健身计划

我们从简单开始,为用户推荐他们最喜欢的五个健身计划。

  1. 用户是我们的起始节点,从中我们可以选择性地引入任何身体限制,作为评分中的排除项。

  2. 然后,我们找到所有带有重要性权重的偏好。

  3. 接下来,我们需要获取所有与用户偏好相连的健身计划,并且不排除用户具有的任何身体限制。

  4. 然后,我们构建带有权重的特质,并对用户与可能被推荐的健身计划之间连接的重要性进行评分。

  5. 然后,我们按照该分数进行排序,并将返回结果集限制为5个。

// Recommend User a Fitness Program
MATCH (u:User) WHERE u.username = "ben" OPTIONAL MATCH (u)-[:HAS]->(pl)
WITH u, pl MATCH (u)-[r:IS_AT|PREFERS|DESIRES|VALUES]->(x)
WITH u, pl, x, coalesce(r.importance, 0.5) AS importance
MATCH (x)<-[]-(x2:FitnessProgram) WHERE NOT (x2)-[:LIMITED_BY]->(pl)
WITH u, x2, collect({name: x.name, weight: importance}) AS traits
WITH u, x2, reduce(s = 0, t IN traits | s + t.weight) AS score
WITH u, x2, score OPTIONAL MATCH (x2)-[]->(x)<-[]-(u)
RETURN x2, collect(x) AS x, u, score ORDER BY score DESC LIMIT 5;

推荐:为用户推荐营养补充剂

这里的过程与健身计划相同,只是推荐的对象是营养补充剂。之所以可行,是因为这些类型之间的关系模式是相同的。

// Recommend User a Nutritional Supplement
MATCH (u:User) WHERE u.username = "ben" OPTIONAL MATCH (u)-[:HAS]->(pl)
WITH u, pl MATCH (u)-[r:IS_AT|PREFERS|DESIRES|VALUES]->(x)
WITH u, pl, x, coalesce(r.importance, 0.5) AS importance
MATCH (x)<-[]-(x2:NutritionalSupplement) WHERE NOT (x2)-[:LIMITED_BY]->(pl)
WITH u, x2, collect({name: x.name, weight: importance}) AS traits
WITH u, x2, reduce(s = 0, t IN traits | s + t.weight) AS score
WITH u, x2, score OPTIONAL MATCH (x2)-[]->(x)<-[]-(u)
RETURN x2, collect(x) AS x, u, score ORDER BY score DESC LIMIT 5;

推荐:为用户推荐健身计划与营养补充剂的组合

这与第一条推荐类似,但只是允许将健身计划和营养补充剂都作为可能的推荐。

// Recommend User a blend of Fitness Programs and Nutrional Supplements
MATCH (u:User) WHERE u.username = "ben" OPTIONAL MATCH (u)-[:HAS]->(pl)
WITH u, pl MATCH (u)-[r:IS_AT|PREFERS|DESIRES|VALUES]->(x)
WITH u, pl, x, coalesce(r.importance, 0.5) AS importance
MATCH (x)<-[]-(x2) WHERE (x2:FitnessProgram OR x2:NutritionalSupplement) AND NOT (x2)-[:LIMITED_BY]->(pl)
WITH u, x2, collect({name: x.name, weight: importance}) AS traits
WITH u, x2, reduce(s = 0, t IN traits | s + t.weight) AS score
WITH u, x2, score OPTIONAL MATCH (x2)-[]->(x)<-[]-(u)
RETURN x2, collect(x) AS x, u, score ORDER BY score DESC LIMIT 5;

推荐:推荐两个用户一起进行健身计划

在这里,我们进行了一些调整,以向一对应该一起体验健身计划的用户进行推荐。主要调整包括:

  1. 从起始用户的偏好出发,遍历出去以获取与他们相连的其他用户,这将构成可能被推荐的用户集合。

  2. 现在,我们对用户进行评分、排序,并将结果限制为最佳用户,然后继续为这两个用户找到最佳的健身计划。

  3. 从那时起,在我们找到可以同时推荐给这两个用户的健身计划之后,评分过程与之前相同。

// Recommend Two Users do a Fitness Program Together
MATCH (u:User) WHERE u.username = "ben" OPTIONAL MATCH (u)-[:HAS]->(pl)
WITH u, pl MATCH (u)-[r:IS_AT|PREFERS|DESIRES|VALUES]->(x)
WITH u, pl, x, coalesce(r.importance, 0.5) AS importance
MATCH (x)<-[]-(x2) WHERE (x2:User) AND NOT (x2)-[:LIMITED_BY]->(pl) AND u <> x2
WITH u, pl, x2, collect({name: x.name, weight: importance}) AS traits
WITH u, pl, x2, reduce(s = 0, t IN traits | s + t.weight) AS score
WITH u, pl, x2, score ORDER BY score DESC LIMIT 1
WITH u, pl, x2, score OPTIONAL MATCH (x2)-[]->(x)<-[]-(u), (x)<-[r]-(fp:FitnessProgram) WHERE NOT (fp)-[:LIMITED_BY]->(pl)
WITH u, x2, score, collect(x) AS common, fp, collect({name: x.name, weight: coalesce(r.importance, 0.5)}) AS traits
WITH u, x2, score, common, fp, reduce(s = 0, t IN traits | s + t.weight) AS score2
RETURN u, x2, score, common, fp, score2 ORDER BY score2 DESC LIMIT 1;
© . All rights reserved.