Contagem de triângulos no Graphx

A importância dos nós computacionais na rede tem muitas aplicações, como encontrar alguns pontos grandes para disseminação de informações ou encontrar alguns pontos anormais para determinação de riscos.


Existem vários métodos comuns:

  1. betweeness (número limite): primeiro encontre todos os caminhos mais curtos na rede, depois conte os nós em cada caminho mais curto, quanto maior o valor, mais importante. Esse método encontra nós com fortes atributos de "mídia" ou "ponte" na rede, mas um problema-chave é que a complexidade de tempo é muito grande.
  2. pagerank : você também pode usar pagerank para calcular os pontos "autoritários" na rede, ou outros algoritmos de propagação também devem ser possíveis.

Aqui está um método baseado na contagem triangular. Um triângulo na rede significa que quaisquer dois dos três nós têm arestas, o que pode ser considerado uma versão enfraquecida de "Clique". Se um nó pode frequentemente formar uma estrutura triangular com vários nós diferentes, então o entorno desse ponto deve ser relativamente denso, e esse ponto também tem uma influência importante. Tomando uma rede social como exemplo facilitará o entendimento: se há uma relação de amizade entre três pessoas, ela pode ser considerada basicamente como um pequeno grupo, e se uma pessoa está em um pequeno grupo com um grande número de pessoas diferentes , então o TA deve ser um especialista social.



O Graphx já encapsula algoritmos prontos que podem ser chamados, e a ideia de implementação do Triangle-Counting também é muito simples. Especificamente, existem três etapas:

  1. Calcular o conjunto de nós vizinhos para cada nó
  2. Para cada aresta, calcule a interseção de dois nós e passe o tamanho da interseção para ambos os nós
  3. Para cada nó calcule o valor acumulado recebido e divida por 2 (este ponto tem dois lados no triângulo)

O algoritmo é relativamente simples e pode ser calculado em três etapas:

  • Calcule o conjunto de vizinhos para cada vértice.
  • Para cada aresta calcule a interseção dos conjuntos e envie a contagem para ambos os vértices.
  • Compute the sum at each vertex and divide by two since each triangle is counted twice.

Spark在执行这个算法之前会对网络进行一些标准化的处理,如果输入已经是标准化的话,那么可以直接调用runPreCanonicalized方法来减少开销

There are two implementations. The default TriangleCount.run implementation first removes self cycles and canonicalizes the graph to ensure that the following conditions hold:

  • There are no self edges
  • All edges are oriented (src is greater than dst) 确保两个点之间只有一条边
  • There are no duplicate edges

However, the canonicalization procedure is costly as it requires repartitioning the graph.

Se os dados de entrada já estiverem no "formato canônico" com os ciclos próprios removidos, então o TriangleCount.runPreCanonicalized deve ser usado. val canonicalGraph = graph.mapEdges(e => 1).removeSelfEdges().canonicalizeEdges() val counts = TriangleCount.runPreCanonicalized(canonicalGraph).vertices


Código fonte :

object TriangleCount {

  def run[VD: ClassTag, ED: ClassTag](graph: Graph[VD, ED]): Graph[Int, ED] = {
    // Transform the edge data something cheap to shuffle and then canonicalize
    val canonicalGraph = graph.mapEdges(e => true).removeSelfEdges().convertToCanonicalEdges()
    // Get the triangle counts
    val counters = runPreCanonicalized(canonicalGraph).vertices
    // Join them bath with the original graph
    graph.outerJoinVertices(counters) { (vid, _, optCounter: Option[Int]) =>
      optCounter.getOrElse(0)
    }
  }


  def runPreCanonicalized[VD: ClassTag, ED: ClassTag](graph: Graph[VD, ED]): Graph[Int, ED] = {
    // Construct set representations of the neighborhoods
    val nbrSets: VertexRDD[VertexSet] =
      graph.collectNeighborIds(EdgeDirection.Either).mapValues { (vid, nbrs) =>
        val set = new VertexSet(nbrs.length)
        var i = 0
        while (i < nbrs.length) {
          // prevent self cycle
          if (nbrs(i) != vid) {
            set.add(nbrs(i))
          }
          i += 1
        }
        set
      }

    // join the sets with the graph
    val setGraph: Graph[VertexSet, ED] = graph.outerJoinVertices(nbrSets) {
      (vid, _, optSet) => optSet.getOrElse(null)
    }

    // Edge function computes intersection of smaller vertex with larger vertex
    def edgeFunc(ctx: EdgeContext[VertexSet, ED, Int]): Unit = {
      val (smallSet, largeSet) = if (ctx.srcAttr.size < ctx.dstAttr.size) {
        (ctx.srcAttr, ctx.dstAttr)
      } else {
        (ctx.dstAttr, ctx.srcAttr)
      }
      val iter = smallSet.iterator
      var counter: Int = 0
      while (iter.hasNext) {
        val vid = iter.next()
        if (vid != ctx.srcId && vid != ctx.dstId && largeSet.contains(vid)) {
          counter += 1
        }
      }
      ctx.sendToSrc(counter)
      ctx.sendToDst(counter)
    }

    // compute the intersection along edges
    val counters: VertexRDD[Int] = setGraph.aggregateMessages(edgeFunc, _ + _)
    // Merge counters with the graph and divide by two since each triangle is counted twice
    graph.outerJoinVertices(counters) { (_, _, optCounter: Option[Int]) =>
      val dblCount = optCounter.getOrElse(0)
      // This algorithm double counts each triangle so the final count should be even
      require(dblCount % 2 == 0, "Triangle count resulted in an invalid number of triangles.")
      dblCount / 2
    }
  }
}
复制代码

Informações sobre clique podem ser encontradas em: codelibrary.tech/ml/communit…


Link original: codelibrary.tech/scala/spark…

Acho que você gosta

Origin juejin.im/post/7082522082134736926
Recomendado
Clasificación