示例:共同关注与推荐关注

在前面的内容中,我们学习了如何使用集合去储存社交网站的好友关系,但是除了基本的关注和被关注之外,社交网站通常还会提供一些额外的功能,帮助用户去发现一些自己可能会感兴趣的人。

比如说,当我们在微博上访问某个用户的个人页面时,页面上就会展示出我们和这个用户都在关注的人,就像图 5-12 所示那样。


图 5-12 微博上的共同关注示例_images/IMAGE_WEIBO_COMMON_FOLLOWING.png


除了共同关注之外,一些社交网站还会通过算法和数据分析,为用户推荐一些他可能会感兴趣的人,比如图 5-13 就展示了 twitter 是如何向用户推荐他可能会感兴趣的关注对象的。


图 5-13 twitter 的推荐关注功能示例_images/IMAGE_TWITTER_RECOMMEND_FOLLOW.png


在接下来的两个小节中,我们将分别学习如何使用集合去实现以上展示的共同关注功能和推荐关注功能。

共同关注

要实现共同关注功能,程序需要做的就是计算出两个用户的正在关注集合之间的交集,这一点可以通过前面介绍的 SINTER 命令和 SINTERSTORE 命令来完成,代码清单 5-7 展示了使用这一原理实现的共同关注程序。


代码清单 5-7 共同关注功能的实现:/set/common_following.py

  1. def following_key(user):
  2. return user + "::following"
  3.  
  4. class CommonFollowing:
  5.  
  6. def __init__(self, client):
  7. self.client = client
  8.  
  9. def calculate(self, user, target):
  10. """
  11. 计算并返回当前用户和目标用户共同关注的人。
  12. """
  13. user_following_set = following_key(user)
  14. target_following_set = following_key(target)
  15. return self.client.sinter(user_following_set, target_following_set)
  16.  
  17. def calculate_and_store(self, user, target, store_key):
  18. """
  19. 计算出当前用户和目标用户共同关注的人,
  20. 并把结果储存到 store_key 指定的键里面,
  21. 最后返回共同关注的人数作为返回值。
  22. """
  23. user_following_set = following_key(user)
  24. target_following_set = following_key(target)
  25. return self.client.sinterstore(store_key, user_following_set, target_following_set)

以下代码展示了共同关注程序的具体用法:

  1. >>> from redis import Redis
  2. >>> from relationship import Relationship
  3. >>> from common_following import CommonFollowing
  4. >>> client = Redis(decode_responses=True)
  5. >>> peter = Relationship(client, "peter")
  6. >>> jack = Relationship(client, "jack")
  7. >>> peter.follow("tom") # peter 关注一些用户
  8. >>> peter.follow("david")
  9. >>> peter.follow("mary")
  10. >>> jack.follow("tom") # jack 关注一些用户
  11. >>> jack.follow("david")
  12. >>> jack.follow("lily")
  13. >>> common_following = CommonFollowing(client)
  14. >>> common_following.calculate("peter", "jack") # 计算 peter 和 jack 的共同关注用户
  15. set(['tom', 'david']) # 他们都关注了 tom 和 david

推荐关注

代码清单 5-8 展示了一个推荐关注程序的实现代码,这个程序会从用户的正在关注集合中随机地选出指定数量的人作为种子用户,然后对这些种子用户的正在关注集合执行并集计算,最后再从这个并集里面随机地选出一些人作为推荐关注的对象。


代码清单 5-8 推荐关注功能的实现:/set/recommend_follow.py

  1. def following_key(user):
  2. return user + "::following"
  3.  
  4. def recommend_follow_key(user):
  5. return user + "::recommend_follow"
  6.  
  7. class RecommendFollow:
  8.  
  9. def __init__(self, client, user):
  10. self.client = client
  11. self.user = user
  12.  
  13. def calculate(self, seed_size):
  14. """
  15. 计算并储存用户的推荐关注数据。
  16. """
  17. # 1)从用户关注的人中随机选一些人作为种子用户
  18. user_following_set = following_key(self.user)
  19. following_targets = self.client.srandmember(user_following_set, seed_size)
  20. # 2)收集种子用户的正在关注集合键名
  21. target_sets = set()
  22. for target in following_targets:
  23. target_sets.add(following_key(target))
  24. # 3)对所有种子用户的正在关注集合执行并集计算,并储存结果
  25. return self.client.sunionstore(recommend_follow_key(self.user), *target_sets)
  26.  
  27. def fetch_result(self, number):
  28. """
  29. 从已有的推荐关注数据中随机地获取指定数量的推荐关注用户。
  30. """
  31. return self.client.srandmember(recommend_follow_key(self.user), number)
  32.  
  33. def delete_result(self):
  34. """
  35. 删除已计算出的推荐关注数据。
  36. """
  37. self.client.delete(recommend_follow_key(self.user))

以下代码展示了这个推荐关注程序的使用方法:

  1. >>> from redis import Redis
  2. >>> from recommend_follow import RecommendFollow
  3. >>> client = Redis(decode_responses=True)
  4. >>> recommend_follow = RecommendFollow(client, "peter")
  5. >>> recommend_follow.calculate(3) # 随机选择 3 个正在关注的人作为种子用户
  6. 30
  7. >>> recommend_follow.fetch_result(10) # 获取 10 个推荐关注对象
  8. ['D6', 'M0', 'S4', 'M1', 'S8', 'M3', 'S3', 'M7', 'M4', 'D7']

在执行这段代码之前,用户 peter 关注了 tomdavidjackmarysam 这五个用户,而这五个用户又分别关注了如图 5-14 所示的一些用户,从结果来看,推荐程序随机选中了 davidsammary 作为种子用户,然后又从这三个用户的正在关注集合的并集中,随机地选出了 10 个人作为 peter 的推荐关注对象。


图 5-14 peter 的正在关注关系图_images/IMAGE_FOLLOW_RELATIONSHIP.png


需要注意的是,这里展示的推荐关注程序使用的是非常简单的推荐算法,它假设用户会对自己正在关注的人的关注对象感兴趣,但实际的情况可能并非如此。为了获得更为精准的推荐效果,实际的社交网站通常会使用更为复杂的推荐算法,有兴趣的读者可以自行查找这方面的资料。