[Spark好友推荐]

11 篇文章 0 订阅
10 篇文章 0 订阅

好友推荐概念     

目前大量的社交网站上,它们都有一个共同的特性,就是可以在好友关系的基础上推荐更多的联系人。例如,QQ的“你可能认识的人”特性允许用户查看它们可能联系的人。基本思想就是:如果tom是jack的好友,而tom又是peter的好友,也就是说,tomjackpeter的共同好友,但是jackpeter可能并不认识你。那么社交网络系统可能就会推荐jackpeter联系,又或者推荐peterjack联系。简单通俗的说,如果两个人有一组共同好友,但是两人又不是好友关系,那么推荐系统就会基于他们之间的好友关系推荐他们相互认识。

       所有用户之间的好友关系可以表示为一个图,对于一个简单的案例,可以使用下图进行表示。

在这里,图是一个有序对G=(V,E),其中:

       V:是由人(社交网络中的用户)构成的衣一个有限集合。

G:是V上的二元关系,称为边集,其中的元素称为一个好友关系。

好友推荐问题可以表述为:对于每一个用户P,其好友列表P1,P2,…P20,表示用户P一共有20个好友。即P:{ P1,P2,…P20}

好友推荐样本数据

假设输入记录是一个按照用户ID排序的列表,输入的没有行数据分别代表用户ID(如用户ID表示为P),其后面的数据表示该用户的好友列表,则好友关系可表示为:

       P,P1 P2 … Pn,其中P1<P2<…Pn.

其样本数据如下所示:

P1,P2 P3 P4 P5 P6 P7
P2,P1 P3 P4
P3,P1 P2 P4 P5
P4,P1 P2 P3
P5,P1 P3 P7
P6,P1
P7,P2

Spark代码实现

/**
  * Spark共同好友推荐
  **/
object FriendRecommendation {
    def main(args: Array[String]): Unit = {
        if (args.length < 2) {
            println("Usage: FriendRecommendation <input-path> <output-path>")
            sys.exit(1)
        }

        val inputPath: String = args(0)
        val outputPath: String = args(1)
        val sparkConf: SparkConf = new SparkConf()
            .setMaster("local[1]")
            .setAppName("FriendRecommendation")
        val sc: SparkContext = SparkContext.getOrCreate(sparkConf)

        val records: RDD[String] = sc.textFile(inputPath)
        //计算两两之间的可能好友关系
        val pairs: RDD[(String, (String, String))] = records.flatMap(line => {
            val tokens: Array[String] = line.split(",")
            val person: String = tokens(0)
            val friends: List[String] = tokens(1).split("\\s+").toList
            val mapperOutput: List[(String, (String, String))] = friends.map(directFriend => (person, (directFriend, "-1")))
            val result: List[List[(String, (String, String))]] = for {
                fi <- friends
                fj <- friends
                possibleFriend1 = (fj, person)
                possibleFriend2 = (fi, person)
                if fi != fj
            } yield {
                (fi, possibleFriend1) :: (fj, possibleFriend2) :: List()
            }
            mapperOutput ::: result.flatten
        })

        //合并同一个键对应的共同好友
        val combineValue: RDD[(String, List[(String, String)])] = pairs.combineByKey(
            (v: (String, String)) => List(v),
            (c: List[(String, String)], v: (String, String)) => c :+ v,
            (c1: List[(String, String)], c2: List[(String, String)]) => c1 ::: c2
        )

        //这里是测试,可以使用collect()生成集合后进行打印
        pairs.collect().foreach(println);
        combineValue.foreach(println)

        //共同好友个数计算及好友推荐
        val result: RDD[(String, Map[String, List[String]])] = combineValue.mapValues(values => {
            val mutualFriends: mutable.HashMap[String, List[String]] = new collection.mutable.HashMap[String, List[String]].empty
            values.foreach(f = t2 => {
                val toUser: String = t2._1
                val mutualFriend: String = t2._2
                val alreadyFriend: Boolean = mutualFriend == "-1"
                if (mutualFriends.contains(toUser)) {
                    if (alreadyFriend) {
                        mutualFriends.put(toUser, List.empty)
                    } else if (mutualFriends.get(toUser).isDefined
                        && mutualFriends.get(toUser).get.size > 0
                        && !mutualFriends(toUser).contains(mutualFriend)) {
                        val existingList: List[String] = mutualFriends.get(toUser).get
                        mutualFriends.put(toUser, mutualFriend :: existingList)
                    }
                } else {
                    if (alreadyFriend) {
                        mutualFriends.put(toUser, List.empty)
                    } else {
                        mutualFriends.put(toUser, List(mutualFriend))
                    }
                }
            })
            mutualFriends.filter(_._2.nonEmpty).toMap
        })

        //result.saveAsTextFile(outputPath)

        //打印结果
        result.foreach(f => {
            val friends = if (f._2.isEmpty) "" else {
                val items = f._2.map(tuple => (tuple._1, "(" + tuple._2.size + ": " + tuple._2.mkString("[", ",", "]") + ")")).map(g => "" + g._1 + " " + g._2)
                items.toList.mkString(",")
            }
            println(s"${f._1}: ${friends}")
        })

        sc.stop()
    }
}

运行结果

可能推荐的好友关系

(P1,(P2,-1))
(P1,(P3,-1))
(P1,(P4,-1))
(P1,(P5,-1))
(P1,(P6,-1))
(P1,(P7,-1))
(P2,(P3,P1))
(P3,(P2,P1))
(P2,(P4,P1))
(P4,(P2,P1))
(P2,(P5,P1))
(P5,(P2,P1))
(P2,(P6,P1))
(P6,(P2,P1))
(P2,(P7,P1))
(P7,(P2,P1))
(P3,(P2,P1))
(P2,(P3,P1))
(P3,(P4,P1))
(P4,(P3,P1))
(P3,(P5,P1))
(P5,(P3,P1))
(P3,(P6,P1))
(P6,(P3,P1))
(P3,(P7,P1))
(P7,(P3,P1))
(P4,(P2,P1))
(P2,(P4,P1))
(P4,(P3,P1))
(P3,(P4,P1))
(P4,(P5,P1))
(P5,(P4,P1))
(P4,(P6,P1))
(P6,(P4,P1))
(P4,(P7,P1))
(P7,(P4,P1))
(P5,(P2,P1))
(P2,(P5,P1))
(P5,(P3,P1))
(P3,(P5,P1))
(P5,(P4,P1))
(P4,(P5,P1))
(P5,(P6,P1))
(P6,(P5,P1))
(P5,(P7,P1))
(P7,(P5,P1))
(P6,(P2,P1))
(P2,(P6,P1))
(P6,(P3,P1))
(P3,(P6,P1))
(P6,(P4,P1))
(P4,(P6,P1))
(P6,(P5,P1))
(P5,(P6,P1))
(P6,(P7,P1))
(P7,(P6,P1))
(P7,(P2,P1))
(P2,(P7,P1))
(P7,(P3,P1))
(P3,(P7,P1))
(P7,(P4,P1))
(P4,(P7,P1))
(P7,(P5,P1))
(P5,(P7,P1))
(P7,(P6,P1))
(P6,(P7,P1))
(P2,(P1,-1))
(P2,(P3,-1))
(P2,(P4,-1))
(P1,(P3,P2))
(P3,(P1,P2))
(P1,(P4,P2))
(P4,(P1,P2))
(P3,(P1,P2))
(P1,(P3,P2))
(P3,(P4,P2))
(P4,(P3,P2))
(P4,(P1,P2))
(P1,(P4,P2))
(P4,(P3,P2))
(P3,(P4,P2))
(P3,(P1,-1))
(P3,(P2,-1))
(P3,(P4,-1))
(P3,(P5,-1))
(P1,(P2,P3))
(P2,(P1,P3))
(P1,(P4,P3))
(P4,(P1,P3))
(P1,(P5,P3))
(P5,(P1,P3))
(P2,(P1,P3))
(P1,(P2,P3))
(P2,(P4,P3))
(P4,(P2,P3))
(P2,(P5,P3))
(P5,(P2,P3))
(P4,(P1,P3))
(P1,(P4,P3))
(P4,(P2,P3))
(P2,(P4,P3))
(P4,(P5,P3))
(P5,(P4,P3))
(P5,(P1,P3))
(P1,(P5,P3))
(P5,(P2,P3))
(P2,(P5,P3))
(P5,(P4,P3))
(P4,(P5,P3))
(P4,(P1,-1))
(P4,(P2,-1))
(P4,(P3,-1))
(P1,(P2,P4))
(P2,(P1,P4))
(P1,(P3,P4))
(P3,(P1,P4))
(P2,(P1,P4))
(P1,(P2,P4))
(P2,(P3,P4))
(P3,(P2,P4))
(P3,(P1,P4))
(P1,(P3,P4))
(P3,(P2,P4))
(P2,(P3,P4))
(P5,(P1,-1))
(P5,(P3,-1))
(P5,(P7,-1))
(P1,(P3,P5))
(P3,(P1,P5))
(P1,(P7,P5))
(P7,(P1,P5))
(P3,(P1,P5))
(P1,(P3,P5))
(P3,(P7,P5))
(P7,(P3,P5))
(P7,(P1,P5))
(P1,(P7,P5))
(P7,(P3,P5))
(P3,(P7,P5))
(P6,(P1,-1))
(P7,(P2,-1))

合并推荐好友关系

(P6,List((P2,P1), (P3,P1), (P4,P1), (P5,P1), (P2,P1), (P3,P1), (P4,P1), (P5,P1), (P7,P1), (P7,P1), (P1,-1)))
(P5,List((P2,P1), (P3,P1), (P4,P1), (P2,P1), (P3,P1), (P4,P1), (P6,P1), (P7,P1), (P6,P1), (P7,P1), (P1,P3), (P2,P3), (P4,P3), (P1,P3), (P2,P3), (P4,P3), (P1,-1), (P3,-1), (P7,-1)))
(P2,List((P3,P1), (P4,P1), (P5,P1), (P6,P1), (P7,P1), (P3,P1), (P4,P1), (P5,P1), (P6,P1), (P7,P1), (P1,-1), (P3,-1), (P4,-1), (P1,P3), (P1,P3), (P4,P3), (P5,P3), (P4,P3), (P5,P3), (P1,P4), (P1,P4), (P3,P4), (P3,P4)))
(P1,List((P2,-1), (P3,-1), (P4,-1), (P5,-1), (P6,-1), (P7,-1), (P3,P2), (P4,P2), (P3,P2), (P4,P2), (P2,P3), (P4,P3), (P5,P3), (P2,P3), (P4,P3), (P5,P3), (P2,P4), (P3,P4), (P2,P4), (P3,P4), (P3,P5), (P7,P5), (P3,P5), (P7,P5)))
(P7,List((P2,P1), (P3,P1), (P4,P1), (P5,P1), (P6,P1), (P2,P1), (P3,P1), (P4,P1), (P5,P1), (P6,P1), (P1,P5), (P3,P5), (P1,P5), (P3,P5), (P2,-1)))
(P3,List((P2,P1), (P2,P1), (P4,P1), (P5,P1), (P6,P1), (P7,P1), (P4,P1), (P5,P1), (P6,P1), (P7,P1), (P1,P2), (P1,P2), (P4,P2), (P4,P2), (P1,-1), (P2,-1), (P4,-1), (P5,-1), (P1,P4), (P2,P4), (P1,P4), (P2,P4), (P1,P5), (P1,P5), (P7,P5), (P7,P5)))
(P4,List((P2,P1), (P3,P1), (P2,P1), (P3,P1), (P5,P1), (P6,P1), (P7,P1), (P5,P1), (P6,P1), (P7,P1), (P1,P2), (P3,P2), (P1,P2), (P3,P2), (P1,P3), (P2,P3), (P1,P3), (P2,P3), (P5,P3), (P5,P3), (P1,-1), (P2,-1), (P3,-1)))

好友推荐结果

P6: P3 (1: [P1]),P5 (1: [P1]),P2 (1: [P1]),P7 (1: [P1]),P4 (1: [P1])
P5: P4 (2: [P3,P1]),P6 (1: [P1]),P2 (2: [P3,P1])
P2: P7 (1: [P1]),P6 (1: [P1]),P5 (2: [P3,P1])
P1: 
P7: P3 (2: [P5,P1]),P5 (1: [P1]),P1 (1: [P5]),P4 (1: [P1]),P6 (1: [P1])
P3: P7 (2: [P5,P1]),P6 (1: [P1])
P4: P7 (1: [P1]),P6 (1: [P1]),P5 (2: [P3,P1])

推荐结果解读

数输出结果采用以下格式输出:

     <USER>:P(N:[U1,U2,...,Un])

其中:

      P是推荐给好友USER的一个好友。

      N是他们之间的共同好友数。

      U1,U2,...,Un是共同好友的ID。

从输出的结果中我们可以看出,用户P1没有任何好友推荐,因为P1y与所有用户都已经是好友了。

结论

基于Spark的好友推荐算法,我们可以在一个庞大的社交网络系统中,运用好友关系的大数据能够快速计算共同好友,并给好友推荐其他的好友。同理,好友推荐算法也衍生到商品推荐及电影推荐。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值