数据来源:Kaggle
数据网址:https://www.kaggle.com/c/learning-social-circles/data
实验目的:读取每个egonet文件内容,根据这些朋友及朋友间的连接创建一个图,找出图中的连通组件,输出社交圈结果
参考书籍:spark GraphX实战
编程语言:Scala
// 1. 读取数据,生成pairRDD
val egonets = sc.wholeTextFile("/usr/local/spark/input/egonets")
// 2. 从文件名中解析用户ID
def extract(s: String) = {
val Pattern = """^.*?(\d+).egonet""".r
val Pattern(num) = s
num
}
// 3. 处理egonet文件的每行数据,返回元组形式的边数据
def get_edges_from_line(line: String): Array[(Long,Long)] = {
val ary = line.split(":")
val srcId = ary(0).toInt
val dstIds = ary(1).split(" ")
val edges = for {
dstId <- dstIds
if(dstId != "")
}yield{
(srcId.toLong, dstId.toLong)
}
// 如果用户没有和任何人没有连接,则生成自连接
if(edges.size > 0) edges else Array((srcId,srcId))
}
// 4. 从文件内容中构建元组,逐行调用get_edges_from_line(line)处理整个文件
def make_edges(contents: String) = {
val lines = contents.split("\n")
val unflat = for {
line <- lines
}yield {
get_edges_from_line(line)
}
// unflat是一个二维数组,应该使用flatten函数使其扁平化为一维数组
val flat = unflat.flatten
flat
}
// 5. 从Edge元组构建一个图对象,执行connectedComponents函数,返回String结果。
def get_circles(flat: Array[(Long,Long)]) = {
val edges = sc.makeRDD(flat)
val g = Graph.fromEdgeTuples(edges,1)
val cc = g.connectedComponents()
// 竞赛要求输出特定格式的预测结果。社交圈子用空格分隔的用户ID列表表示;每个社交圈子间用分号(;)分隔
cc.vertices.map(x => (x._2, Array(x._1))).
reduceByKey( (a,b) => a++b).
values.map(_.mkString(" ")).collect.mkString(";")
}
// 6. 主函数/执行语句
// 读入数据
val egonets = sc.wholeTextFiles("file:///usr/local/spark/input/egonets")
// 从pairRDD中提取用户名(文件名)
val egonet_numbers = egonets.map(x => extract(x._1)).collect
// 根据make_edges产生边
val egonet_edges = egonets.map(x => make_edges(x._2)).collect
// 根据边产生图对象
val egonet_circles = egonet_edges.toList.map(x => get_circles(x))
println("UserId, Prediction")
val result = egonet_numbers.zip(egonet_circles).map(x => x._1 + "," + x._2)
println(result.mkString("\n"))