使用GraphFrames进行Spark Graph处理简介

2025/04/13

1. 简介

图处理对社交网络、广告等众多应用都非常有用。在大数据场景中,我们需要一个工具来分配处理负载。

在本教程中,我们将使用Java中的Apache Spark加载并探索图的各种可能性,为了避免复杂的结构,我们将使用一个简单且高级的Apache Spark图API:GraphFrames API。

2. 图

首先,我们来定义一下图及其组成部分;图是一种包含边和顶点的数据结构,边承载着表示顶点之间关系的信息

顶点是n维空间中的点,边根据顶点之间的关系连接顶点:

上图是一个社交网络的例子,我们可以看到用字母表示的顶点,以及顶点之间承载关系的边。

3. Maven设置

现在,让我们通过设置Maven配置来启动项目。

让我们添加spark-graphx 2.11graphframesspark-sql 2.11

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-graphx_2.11</artifactId>
    <version>2.4.4</version>
</dependency>
<dependency>
   <groupId>graphframes</groupId>
   <artifactId>graphframes</artifactId>
   <version>0.7.0-spark2.4-s_2.11</version>
</dependency>
<dependency>
   <groupId>org.apache.spark</groupId>
   <artifactId>spark-sql_2.11</artifactId>
   <version>2.4.4</version>
</dependency>

这些工件版本支持Scala 2.11。

另外,GraphFrames恰好不在Maven Central中,因此,我们来添加所需的Maven仓库:

<repositories>
     <repository>
          <id>SparkPackagesRepo</id>
          <url>http://dl.bintray.com/spark-packages/maven</url>
     </repository>
</repositories>

4. Spark配置

为了使用GraphFrames,我们需要下载Hadoop并定义HADOOP_HOME环境变量。

如果操作系统是Windows,我们还将适当的winutils.exe下载到HADOOP_HOME/bin文件夹。

接下来,让我们通过创建基本配置来开始我们的代码:

SparkConf sparkConf = new SparkConf()
    .setAppName("SparkGraphFrames")
    .setMaster("local[*]");
JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);

我们还需要创建一个SparkSession:

SparkSession session = SparkSession.builder()
    .appName("SparkGraphFrameSample")
    .config("spark.sql.warehouse.dir", "/file:C:/temp")
    .sparkContext(javaSparkContext.sc())
    .master("local[*]")
    .getOrCreate();

5. 图构建

现在,一切准备就绪,可以开始编写主代码了。首先,定义顶点和边的实体,并创建GraphFrame实例。

我们将研究假设的社交网络中的用户之间的关系。

5.1 数据

首先,对于这个例子,我们将两个实体定义为User和Relationship:

public class User {
    private Long id;
    private String name;
    // constructor, getters and setters
}

public class Relationship implements Serializable {
    private String type;
    private String src;
    private String dst;
    private UUID id;

    public Relationship(String type, String src, String dst) {
        this.type = type;
        this.src = src;
        this.dst = dst;
        this.id = UUID.randomUUID();
    }
    // getters and setters
}

接下来,让我们定义一些User和Relationship实例:

List<User> users = new ArrayList<>();
users.add(new User(1L, "John"));
users.add(new User(2L, "Martin"));
users.add(new User(3L, "Peter"));
users.add(new User(4L, "Alicia"));

List<Relationship> relationships = new ArrayList<>();
relationships.add(new Relationship("Friend", "1", "2"));
relationships.add(new Relationship("Following", "1", "4"));
relationships.add(new Relationship("Friend", "2", "4"));
relationships.add(new Relationship("Relative", "3", "1"));
relationships.add(new Relationship("Relative", "3", "4"));

5.2 GraphFrame实例

现在,为了创建和操作我们的关系图,我们将创建一个GraphFrame实例。GraphFrame构造函数需要两个Dataset<Row>实例,第一个表示顶点,第二个表示边:

Dataset<Row> userDataset = session.createDataFrame(users, User.class);
Dataset<Row> relationshipDataset = session.createDataFrame(relationships, Relation.class);

GraphFrame graph = new GraphFrame(userDataframe, relationshipDataframe);

最后,我们将在控制台中记录我们的顶点和边以查看它的外观:

graph.vertices().show();
graph.edges().show();
+---+------+
| id|  name|
+---+------+
|  1|  John|
|  2|Martin|
|  3| Peter|
|  4|Alicia|
+---+------+

+---+--------------------+---+---------+
|dst|                  id|src|     type|
+---+--------------------+---+---------+
|  2|622da83f-fb18-484...|  1|   Friend|
|  4|c6dde409-c89d-490...|  1|Following|
|  4|360d06e1-4e9b-4ec...|  2|   Friend|
|  1|de5e738e-c958-4e0...|  3| Relative|
|  4|d96b045a-6320-4a6...|  3| Relative|
+---+--------------------+---+---------+

6. 图运算符

现在我们有了一个GraphFrame实例,让我们看看可以用它做什么。

6.1 过滤

GraphFrames允许我们通过查询过滤边和顶点。

接下来,让我们通过User上的name属性来过滤顶点:

graph.vertices().filter("name = 'Martin'").show();

在控制台我们可以看到结果:

+---+------+
| id|  name|
+---+------+
|  2|Martin|
+---+------+

另外,我们可以通过调用filterEdges或filterVertices直接在图上进行过滤:

graph.filterEdges("type = 'Friend'")
    .dropIsolatedVertices().vertices().show();

现在,由于我们过滤了边,可能仍然会有一些孤立的顶点。因此,我们将调用dropIsolatedVertices()。

因此,我们有一个子图,仍然是一个GraphFrame实例,仅包含具有“Friend”状态的关系:

+---+------+
| id|  name|
+---+------+
|  1|  John|
|  2|Martin|
|  4|Alicia|
+---+------+

6.2 度

另一个有趣的特征集是度运算集,这些运算返回与每个顶点关联的边数。

degrees运算仅返回每个顶点所有边的数量;另一方面,inDegrees运算仅计算入边数量,outDegrees运算仅计算出边数量。

让我们计算一下图中所有顶点的传入度:

graph.inDegrees().show();

因此,我们有一个GraphFrame,它显示了每个顶点的传入边的数量,不包括没有传入边的边:

+---+--------+
| id|inDegree|
+---+--------+
|  1|       1|
|  4|       3|
|  2|       1|
+---+--------+

7. 图算法

GraphFrames还提供了可立即使用的流行算法-让我们来看看其中的一些。

7.1 页面排名

Page Rank算法对到达顶点的传入边进行加权并将其转换为分数。

这个想法是,每个传入的边代表一个认可,并使顶点在给定的图中更加相关。

例如,在社交网络中,如果一个人被很多人关注,那么他或她的排名就会很高。

运行页面排名算法非常简单:

graph.pageRank()
    .maxIter(20)
    .resetProbability(0.15)
    .run()
    .vertices()
    .show();

要配置此算法,我们只需要提供:

  • maxIter:运行页面排名的迭代次数,建议为20,太少会降低质量,太多会降低性能
  • resetProbability:随机重置概率(alpha),该值越低,获胜者和失败者之间的分数差距就越大;有效范围是0到1,通常,0.15是一个不错的分数

响应是一个类似的GraphFrame,但这次我们看到一个额外的列给出了每个顶点的页面排名:

+---+------+------------------+
| id|  name|          pagerank|
+---+------+------------------+
|  4|Alicia|1.9393230468864597|
|  3| Peter|0.4848822786454427|
|  1|  John|0.7272991738542318|
|  2|Martin| 0.848495500613866|
+---+------+------------------+

在我们的图中,Alicia是最相关的顶点,其次是Martin和John。

7.2 连通分量

连通分量算法用于查找孤立簇或孤立子图,这些簇是图中连通顶点的集合,其中每个顶点都可以从同一集合中的任何其他顶点到达。

我们可以通过connectedComponents()方法调用不带任何参数的算法:

graph.connectedComponents().run().show();

该算法返回一个包含每个顶点以及每个顶点所连接的组件的GraphFrame:

+---+------+------------+
| id|  name|   component|
+---+------+------------+
|  1|  John|154618822656|
|  2|Martin|154618822656|
|  3| Peter|154618822656|
|  4|Alicia|154618822656|
+---+------+------------+

我们的图只有一个组件-这意味着我们没有孤立的子图,该组件有一个自动生成的ID,在本例中为154618822656。

尽管这里多了一列组件ID,但我们的图仍然相同。

7.3 三角形计数

三角形计数通常用于社交网络图中的社群检测和计数,三角形是由三个顶点组成的集合,每个顶点都与三角形中的其他两个顶点存在关联。

在社交网络社区中,很容易找到大量相互连接的三角形。

我们可以轻松地直接从GraphFrame实例执行三角形计数:

graph.triangleCount().run().show();

该算法还返回一个GraphFrame,其中包含通过每个顶点的三角形的数量。

+-----+---+------+
|count| id|  name|
+-----+---+------+
|    1|  3| Peter|
|    2|  1|  John|
|    2|  4|Alicia|
|    1|  2|Martin|
+-----+---+------+

8. 总结

Apache Spark是一款出色的工具,能够以优化的分布式方式计算大量数据。此外,GraphFrames库使我们能够轻松地在Spark上分发图操作。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章