使用Spark计算节点之间的链接

时间:2018-01-09 15:32:40

标签: scala apache-spark spark-dataframe

我在Spark 2.2和Scala 2.11中有以下两个DataFrame。 DataFrame edges定义有向图的边,而DataFrame types定义每个节点的类型。

edges =

+-----+-----+----+
|from |to   |attr|
+-----+-----+----+
|    1|    0|   1|
|    1|    4|   1|
|    2|    2|   1|
|    4|    3|   1|
|    4|    5|   1|
+-----+-----+----+

types =
+------+---------+
|nodeId|type     |
+------+---------+
|     0|        0|
|     1|        0|
|     2|        2|
|     3|        4|
|     4|        4|
|     5|        4|
+------+---------+

对于每个节点,我想知道相同type的节点的边数。请注意,我只想计算从节点传出的边缘,因为我处理有向图。

为了实现这一目标,我执行了两个DataFrame的连接:

val graphDF = edges
                  .join(types, types("nodeId") === edges("from"), "left")
                  .drop("nodeId")
                  .withColumnRenamed("type","type_from")
                  .join(types, types("nodeId") === edges("to"), "left")
                  .drop("nodeId")
                  .withColumnRenamed("type","type_to")

我获得了以下新的DataFrame graphDF

+-----+-----+----+---------------+---------------+
|from |to   |attr|type_from      |type_to        |
+-----+-----+----+---------------+---------------+
|    1|    0|   1|              0|              0|
|    1|    4|   1|              0|              4|
|    2|    2|   1|              2|              2|
|    4|    3|   1|              4|              4|
|    4|    5|   1|              4|              4|
+-----+-----+----+---------------+---------------+

现在我需要获得以下最终结果:

+------+---------+---------+
|nodeId|numLinks |type     |
+------+---------+---------+
|     0|        0|        0| 
|     1|        1|        0|
|     2|        0|        2|
|     3|        0|        4|
|     4|        2|        4|
|     5|        0|        4| 
+------+---------+---------+

我在考虑使用groupByagg(count(...),但我不知道如何处理有向边。

更新

numLinks计算为从给定节点传出的边数。例如,节点5没有任何传出边(只有进入边4->5,请参阅DataFrame edges)。相同的是指节点0.但节点4有两个传出边(4->34->5)。

我的解决方案:

这是我的解决方案,但缺少那些有0个链接的节点。

graphDF.filter("from != to").filter("type_from == type_to").groupBy("from").agg(count("from") as "numLinks").show()

1 个答案:

答案 0 :(得分:1)

您可以按ID和类型进行过滤,聚合,并使用类型添加缺少的节点:

val graphDF = Seq(
  (1, 0, 1, 0, 0), (1, 4, 1, 0, 4), (2, 2, 1, 2, 2),
  (4, 3, 1, 4, 4), (4, 5, 1, 4, 4)
).toDF("from", "to", "attr", "type_from", "type_to")

val types = Seq(
  (0, 0), (1, 0), (2, 2), (3, 4), (4,4), (5, 4)
).toDF("nodeId", "type")

graphDF
  // I want to know the number of edges to the nodes of the same type
  .where($"type_from" === $"type_to" && $"from" =!= $"to")
  // I only want to count the edges outgoing from a node,
  .groupBy($"from" as "nodeId", $"type_from" as "type")
  .agg(count("*") as "numLinks")
  // but it lacks those nodes that have 0 links.
  .join(types, Seq("nodeId", "type"), "rightouter")
  .na.fill(0)

// +------+----+--------+
// |nodeId|type|numLinks|
// +------+----+--------+
// |     0|   0|       0|
// |     1|   0|       1|
// |     2|   2|       1|
// |     3|   4|       0|
// |     4|   4|       2|
// |     5|   4|       0|
// +------+----+--------+

要跳过自我链接,请在选择中添加$"from" =!= $"to"

graphDF
  .where($"type_from" === $"type_to" && $"from" =!= $"to")
  .groupBy($"from" as "nodeId", $"type_from" as "type")
  .agg(count("*") as "numLinks")
  .join(types, Seq("nodeId", "type"), "rightouter")
  .na.fill(0)

// +------+----+--------+
// |nodeId|type|numLinks|
// +------+----+--------+
// |     0|   0|       0|
// |     1|   0|       1|
// |     2|   2|       0|
// |     3|   4|       0|
// |     4|   4|       2|
// |     5|   4|       0|
// +------+----+--------+