Conteo de triángulos en Graphx

La importancia de los nodos de cómputo en la red tiene muchas aplicaciones, como encontrar algunos puntos grandes para la difusión de información o encontrar algunos puntos anormales para la determinación del riesgo.


Hay varios métodos comunes:

  1. betweeness (número de límite): primero encuentre todos los caminos más cortos en la red, luego cuente los nodos en cada camino más corto, cuanto mayor sea el valor, más importante. Este método encuentra nodos con fuertes atributos de "medios" o "puente" en la red, pero un problema clave es que la complejidad del tiempo es demasiado grande.
  2. pagerank : también puede usar pagerank para calcular los puntos "autorizados" en la red, o también deberían ser posibles otros algoritmos de propagación.

Aquí hay un método basado en el conteo triangular. Un triángulo en la red significa que dos de los tres nodos tienen bordes, lo que puede considerarse como una versión debilitada de "Clique". Si un nodo puede formar con frecuencia una estructura triangular de este tipo con múltiples nodos diferentes, entonces el entorno de este punto debe ser relativamente denso, y este punto también tiene una influencia importante. Tomando una red social como ejemplo, será más fácil de entender: si hay una relación de amistad entre tres personas, básicamente se puede considerar como un grupo pequeño, y si una persona está en un grupo pequeño con una gran cantidad de personas diferentes , entonces el AT debe ser un experto social .



Graphx ya ha encapsulado algoritmos listos para usar que se pueden llamar, y la idea de implementación de Triangle-Counting también es muy simple. En concreto, hay tres pasos:

  1. Calcule el conjunto de nodos vecinos para cada nodo
  2. Para cada borde, calcule la intersección de dos nodos y pase el tamaño de la intersección a ambos nodos
  3. Para cada nodo, calcule el valor acumulado recibido y divídalo por 2 (este punto tiene dos lados en el triángulo)

El algoritmo es relativamente sencillo y se puede calcular en tres pasos:

  • Calcule el conjunto de vecinos para cada vértice.
  • Para cada borde, calcule la intersección de los conjuntos y envíe el conteo a ambos 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.

Si los datos de entrada ya están en "forma canónica" con los ciclos propios eliminados, entonces se debe usar TriangleCount.runPreCanonicalized en su lugar. val canonicalGraph = graph.mapEdges(e => 1).removeSelfEdges().canonicalizeEdges() val counts = TriangleCount.runPreCanonicalized(canonicalGraph).vertices


Código fuente :

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
    }
  }
}
复制代码

Puede encontrar información sobre la camarilla en: codelibrary.tech/ml/communit…


Enlace original: codelibrary.tech/scala/spark…

Supongo que te gusta

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