关系对象类:
class RelationBean {
//起始节点
private var _fromNode: String = "null"
def fromNode: String = _fromNode
def fromNode_=(value: String): Unit = {
_fromNode = value
}
//指向节点
private var _toNode: String = "null"
def toNode: String = _toNode
def toNode_=(value: String): Unit = {
_toNode = value
}
def canEqual(other: Any): Boolean = other.isInstanceOf[RelationBean]
override def equals(other: Any): Boolean = other match {
case that: RelationBean =>
(that canEqual this) && (_fromNode == that._fromNode && _toNode == that._toNode)
case _ => false
}
override def toString: String = "{fromNode:" + fromNode + ",toNode:" + toNode + ",info:" + info.toString() + "}"
}
object RelationBean {
def apply: RelationBean = new RelationBean()
}
louvain 实现类:
import scala.collection.mutable
/**
* 该算法 默认所有的边的权重为1
* 关系为无向关系
* @param communityId 社区id
*/
class LouvainCommunity(val communityId: String) {
//社区所包含的节点
var nodes: mutable.HashSet[String] = new mutable.HashSet[String]()
override def equals(obj: scala.Any): Boolean = obj.isInstanceOf[LouvainCommunity] && obj.asInstanceOf[LouvainCommunity].communityId == communityId
override def toString: String = "{communityId:" + communityId + ",nodes:" + nodes.toString() + "}"
}
object LouvainCommunity {
var global_1_m: Double = 0.0 //全局的 1/m
var graph: mutable.HashMap[String, LouvainCommunity] = new mutable.HashMap[String, LouvainCommunity]()
//某个节点的关系节点
var nodeMap: mutable.HashMap[String, mutable.HashSet[String]] = new mutable.HashMap[String, mutable.HashSet[String]]()
//节点所在的社区
var nodecom: mutable.HashMap[String, String] = new mutable.HashMap[String, String]()
//初始化louvain图
def initGraph(edgeSet: Set[RelationBean]): Unit = {
graph = new mutable.HashMap[String, LouvainCommunity]()
nodeMap = new mutable.HashMap[String, mutable.HashSet[String]]()
nodecom = new mutable.HashMap[String, String]()
global_1_m = 1.0 / edgeSet.size
for (edge: RelationBean <- edgeSet) {
val fn: String = edge.fromNode
val tn: String = edge.toNode
nodeMap.put(fn, {
val s: mutable.HashSet[String] = nodeMap.getOrElse(fn, new mutable.HashSet[String]())
s.add(tn)
s
})
nodeMap.put(tn, {
val s: mutable.HashSet[String] = nodeMap.getOrElse(tn, new mutable.HashSet[String]())
s.add(fn)
s
})
}
for (comid: String <- nodeMap.keySet.toSet) {
graph.put(comid, {
val c = new LouvainCommunity(comid)
c.nodes.add(comid)
c
})
}
nodeMap.keySet.foreach(a => {
nodecom.put(a, a)
})
}
//入射 社区 的边的权重之和
def getSigma_tot(comid: String): Int = {
val comnodes = graph(comid).nodes
val set = new mutable.HashSet[String]()
comnodes.foreach(a => {
nodeMap(a).foreach(b => {
if (!comnodes.contains(b)) set.add(b)
})
})
set.size
}
//入射 节点i 的权重之和
def getK_i(nodeid: String): Int = nodeMap(nodeid).size
//节点i 入射 社区 的权重之和
def getK_i_in(nodeid: String, comid: String): Int = graph(comid).nodes.intersect(nodeMap(nodeid)).size
//计算模块度 node为入射节点
private def getDeltaModularity(node: String): (String, String, Double) = {
val k_i = getK_i(node)
val relanodes: mutable.HashSet[String] = nodeMap(node)
val oldcomset = new mutable.HashSet[String]()
var deltamax = ("flag", "0", 0.0)
relanodes.foreach(node2 => {
val comid = nodecom(node2)
if (!oldcomset.contains(node2)) {
oldcomset.add(node2)
val k_i_in = getK_i_in(node, comid)
val sigma_tot = getSigma_tot(comid)
val deltatmp = k_i_in - sigma_tot * k_i * global_1_m
if (deltatmp > 0 && deltatmp > deltamax._3) deltamax = (node, comid, deltatmp)
}
})
deltamax
}
//重构louvain图
def rebuildgraph(fromnode: String, tocom: String): String = {
var re = " "
graph(nodecom(fromnode)).nodes.remove(fromnode)
if (graph(nodecom(fromnode)).nodes.isEmpty) {
graph.remove(nodecom(fromnode))
re = nodecom(fromnode)
}
graph(tocom).nodes.add(fromnode)
nodecom.update(fromnode, tocom)
re
}
def caculate(): Unit = {
val deltaSet = new mutable.HashSet[(String, String, Double)]()
val changenodeset = new mutable.HashSet[String]()
nodeMap.keySet.foreach(com => {
val delta = getDeltaModularity(com)
if (delta._1 != "flag") deltaSet.add(delta)
})
if (deltaSet.nonEmpty) {
deltaSet.foreach(a => {
if (!changenodeset.contains(a._2) && !changenodeset.contains(a._1) && a._2 != nodecom(a._1)) {
val re = rebuildgraph(a._1, a._2)
changenodeset.add(a._1)
changenodeset.add(a._2)
changenodeset.add(re)
}
})
}
}
def run(edgeSet: Set[RelationBean]): Set[LouvainCommunity] = {
println("init")
initGraph(edgeSet)
println("caculate")
var flag = 1
while (flag < 10) {
println("---- 第" + (flag + 1) + "次迭代 ----")
caculate()
flag += 1
}
graph.foreach(println(_))
val re = graph
graph.clear()
nodecom.clear()
nodeMap.clear()
re.valuesIterator.toSet
}
自用,待优化