jjzjj

java - Hadoop M/R实现 “People You Might Know”友谊推荐

coder 2024-01-07 原文

如何通过查看两个人有多少个共同的 friend 来建立一个友谊推荐系统,并使用mapreduce工作将他们推荐为 friend ?有点像facebook或linkedin所做的事情,显示推荐人员列表,并按共同 friend 的数量对其进行排名。

最佳答案

该解决方案来自我的博客,我在项目中使用了此代码。

完整版,请参见https://www.dbtsai.com/blog/hadoop-mr-to-implement-people-you-might-know-friendship-recommendation/

由于我不确定这是否是最佳解决方案,并且我也希望在stackoverflow中有一个文档,因此我在这里提出并回答了自己的问题。我希望获得社区的反馈。

最好的友谊推荐通常来自 friend 。关键思想是,如果两个人有很多共同的 friend ,但他们不是 friend ,则系统应建议他们彼此连接。
让我们假设友谊是无向的:如果A是B的 friend ,那么B也是A的 friend 。这是Facebook,Google +,Linkedin和几个社交网络中最常用的友谊系统。将其扩展到Twitter中使用的定向友谊系统并不困难;但是,在本文中,我们将重点关注无方向的案例。

输入数据将包含邻接列表,并以 的格式显示多行,其中是唯一用户的唯一ID,是用户列表,中间用的 friend 的逗号。以下是输入示例。用户和用户之间的关系可以在图中更容易理解。

1    0,2,3,4,5
2    0,1,4
3    0,1,4
4    1,2,3
5    1,6
6    5

在图中,您可以看到用户0不是用户4和5的 friend ,但是用户0和用户4具有共同的 friend 1、2和3;用户0和用户5拥有共同的 friend 1。因此,我们建议将用户4和5推荐为用户0的 friend 。

推荐 friend 的输出将以以下格式给出。 <推荐给user的 friend="" (共同="" friend="" 的数量:[共同="" friend="" 的id,...]),...="">。根据共同 friend 的数量对输出结果进行排序,并可以从图中进行验证。
0    4 (3: [3, 1, 2]),5 (1: [1])
1    6 (1: [5])
2    3 (3: [1, 4, 0]),5 (1: [1])
3    2 (3: [4, 0, 1]),5 (1: [1])
4    0 (3: [2, 3, 1]),5 (1: [1])
5    0 (1: [1]),2 (1: [1]),3 (1: [1]),4 (1: [1])
6    1 (1: [5])

现在,让我们将此问题适合于单个M / R作业。用户0有 friend 1、2和3;结果,这对<1、2>,<2、1>,<2、3>,<3、2>,<1、3>和<3、1>具有用户0的共同 friend 。结果,我们可以发出 = <1,r =="" 2;="" m="0">,<2,r =="" 1;="" m="0">,<2,r =="" 3;="" m="0"> ...,其中r表示推荐的 friend ,m表示共同的 friend 。我们可以在缩减阶段汇总结果,并计算出用户和推荐用户之间有多少个共同的 friend 。但是,这种方法会引起问题。如果用户A和推荐用户B已经是 friend 怎么办?为了克服这个问题,我们可以在发射的值中添加另一个属性isFriend,如果我们知道他们已经是reduce阶段的 friend ,我们就不推荐该 friend 。在以下实现中,当他们已经成为 friend 时,将使用m = -1而不是使用额外的字段。

在输入数据中定义fromUser为,toUser为之一,然后,算法可以由

map 阶段

  • Emit 。假设有n个toUser;那么我们将发出n条记录来描述fromUser和toUser已经是 friend 。请注意,它们已经是发出的键和r之间的 friend ,因此我们将m设置为-1。
  • Emit 用于toUser的toUser1和toUser2的所有可能组合,并且它们有共同的 friend fromUser。它将发出n(n-1)条记录。
  • 在 map 阶段总共有n ^ 2个发射记录,其中n是拥有的 friend 数量。

  • 还原阶段

  • 仅求和键和值r之间有多少个共同的 friend 。如果他们中有共同的 friend -1,由于他们已经是 friend ,因此我们不推荐您。
  • 根据共同 friend 的数量对结果进行排序。

  • 由于hadoop中发出的值不是原始数据类型,因此我们必须自定义新的可写类型,如以下代码所示。
    static public class FriendCountWritable implements Writable {
        public Long user;
        public Long mutualFriend;
    
        public FriendCountWritable(Long user, Long mutualFriend) {
            this.user = user;
            this.mutualFriend = mutualFriend;
        }
    
        public FriendCountWritable() {
            this(-1L, -1L);
        }
    
        @Override
        public void write(DataOutput out) throws IOException {
            out.writeLong(user);
            out.writeLong(mutualFriend);
        }
    
        @Override
        public void readFields(DataInput in) throws IOException {
            user = in.readLong();
            mutualFriend = in.readLong();
        }
    
        @Override
        public String toString() {
            return " toUser: "
                    + Long.toString(user) + " mutualFriend: "
                    + Long.toString(mutualFriend);
        }
    }
    

    映射器可以通过以下方式实现
    public static class Map extends Mapper<LongWritable, Text, LongWritable, FriendCountWritable> {
        private Text word = new Text();
    
        @Override
        public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            String line[] = value.toString().split("\t");
            Long fromUser = Long.parseLong(line[0]);
            List toUsers = new ArrayList();
    
            if (line.length == 2) {
                StringTokenizer tokenizer = new StringTokenizer(line[1], ",");
                while (tokenizer.hasMoreTokens()) {
                    Long toUser = Long.parseLong(tokenizer.nextToken());
                    toUsers.add(toUser);
                    context.write(new LongWritable(fromUser),
                            new FriendCountWritable(toUser, -1L));
                }
    
                for (int i = 0; i < toUsers.size(); i++) {
                    for (int j = i + 1; j < toUsers.size(); j++) {
                        context.write(new LongWritable(toUsers.get(i)),
                                new FriendCountWritable((toUsers.get(j)), fromUser));
                        context.write(new LongWritable(toUsers.get(j)),
                                new FriendCountWritable((toUsers.get(i)), fromUser));
                    }
                    }
                }
            }
        }
    

    reducer 可以通过以下方式实现
    public static class Reduce extends Reducer<LongWritable, FriendCountWritable, LongWritable, Text> {
        @Override
        public void reduce(LongWritable key, Iterable values, Context context)
                throws IOException, InterruptedException {
    
            // key is the recommended friend, and value is the list of mutual friends
            final java.util.Map<Long, List> mutualFriends = new HashMap<Long, List>();
    
            for (FriendCountWritable val : values) {
                final Boolean isAlreadyFriend = (val.mutualFriend == -1);
                final Long toUser = val.user;
                final Long mutualFriend = val.mutualFriend;
    
                if (mutualFriends.containsKey(toUser)) {
                    if (isAlreadyFriend) {
                        mutualFriends.put(toUser, null);
                    } else if (mutualFriends.get(toUser) != null) {
                        mutualFriends.get(toUser).add(mutualFriend);
                    }
                } else {
                    if (!isAlreadyFriend) {
                        mutualFriends.put(toUser, new ArrayList() {
                            {
                                add(mutualFriend);
                            }
                        });
                    } else {
                        mutualFriends.put(toUser, null);
                    }
                }
            }
    
            java.util.SortedMap<Long, List> sortedMutualFriends = new TreeMap<Long, List>(new Comparator() {
                @Override
                public int compare(Long key1, Long key2) {
                    Integer v1 = mutualFriends.get(key1).size();
                    Integer v2 = mutualFriends.get(key2).size();
                    if (v1 > v2) {
                        return -1;
                    } else if (v1.equals(v2) && key1 < key2) {
                        return -1;
                    } else {
                        return 1;
                    }
                }
            });
    
            for (java.util.Map.Entry<Long, List> entry : mutualFriends.entrySet()) {
                if (entry.getValue() != null) {
                    sortedMutualFriends.put(entry.getKey(), entry.getValue());
                }
            }
    
            Integer i = 0;
            String output = "";
            for (java.util.Map.Entry<Long, List> entry : sortedMutualFriends.entrySet()) {
                if (i == 0) {
                    output = entry.getKey().toString() + " (" + entry.getValue().size() + ": " + entry.getValue() + ")";
                } else {
                    output += "," + entry.getKey().toString() + " (" + entry.getValue().size() + ": " + entry.getValue() + ")";
                }
                ++i;
            }
            context.write(key, new Text(output));
        }
    }
    

    其中,比较器在TreeMap中用于按共同好友数的降序对输出值进行排序。

    欢迎任何评论和反馈。谢谢。

    关于java - Hadoop M/R实现 “People You Might Know”友谊推荐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15035778/

    有关java - Hadoop M/R实现 “People You Might Know”友谊推荐的更多相关文章

    1. java - 等价于 Java 中的 Ruby Hash - 2

      我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

    2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

      我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

    3. java - 从 JRuby 调用 Java 类的问题 - 2

      我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

    4. ruby-on-rails - Rails 中的推荐引擎 - 2

      我想为我的Rails网络应用程序提供推荐功能。特别是,我想向新注册的用户推荐他可能想要关注的其他用户。Rails中是否有用于此目的的引擎/gem?如果没有,我应该从哪里开始构建它?谢谢。 最佳答案 有Coletivogemhttps://github.com/diogenes/coletivo我试了一下。在MySQL上运行。Neo4jhttp://neo4j.org真的很容易实现一个“跟随谁”。事实上,大多数展示其能力的样本都涉及“跟随谁”。快速提示-只有在JRuby上运行时,Neo4j.rb才会很酷。如果不是-使用Neograph

    5. java - 我的模型类或其他类中应该有逻辑吗 - 2

      我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

    6. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

      什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

    7. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

      华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

    8. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

      这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

    9. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

      HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

    10. 基于C#实现简易绘图工具【100010177】 - 2

      C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

    随机推荐