1. 序章

グラフ処理は、ソーシャルネットワークから広告まで、多くのアプリケーションで役立ちます。ビッグデータのシナリオでは、その処理負荷を分散するためのツールが必要です。

このチュートリアルでは、Javaの Apache Spark を使用して、グラフの可能性をロードして調査します。 複雑な構造を回避するために、簡単で高レベルのApacheSparkグラフAPIであるGraphFramesAPIを使用します。

2. グラフ

まず、グラフとそのコンポーネントを定義しましょう。 グラフは、エッジと頂点を持つデータ構造です。 エッジは、頂点間の関係を表す情報を伝達します。

頂点はn次元空間内の点であり、エッジはそれらの関係に従って頂点を接続します。

上の画像には、ソーシャルネットワークの例があります。 文字で表された頂点と、頂点間にどのような関係があるかを示すエッジを見ることができます。

3. Mavenのセットアップ

それでは、Maven構成をセットアップしてプロジェクトを開始しましょう。

spark-graphx 2.11、 graphframes 、および spark-sql2.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>

これらのアーティファクトバージョンはScala2.11をサポートしています。

また、GraphFramesがMavenCentralにないこともあります。 それでは、必要なMavenリポジトリも追加しましょう。

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

4. Spark構成

GraphFrameを使用するには、 Hadoop をダウンロードし、HADOOP_HOME環境変数を定義する必要があります。

オペレーティングシステムとしてWindowsの場合は、適切なwinutils.exeHADOOP_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. データ

まず、この例では、両方のエンティティをUserRelationshipとして定義しましょう。

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のインスタンスを作成します。 The GraphFrame コンストラクターは2つを期待しますデータセットインスタンス、最初は頂点を表し、2番目はエッジを表します。

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を使用すると、クエリによってエッジと頂点をフィルタリングできます。

次に、Usernameプロパティで頂点をフィルタリングしましょう。

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

コンソールで、結果を確認できます。

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

また、filterEdgesまたはfilterVerticesを呼び出すことで、グラフを直接フィルタリングできます。

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

ここで、エッジをフィルタリングしたので、まだいくつかの孤立した頂点がある可能性があります。 だから、私たちは電話します dropIsolatedVertices()。 

その結果、サブグラフが作成されましたが、それでも GraphFrame インスタンスであり、「フレンド」ステータスの関係のみが含まれています。

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

6.2. 度

もう1つの興味深い機能セットは、degreesの一連の操作です。 これらの操作は、各頂点のエッジの数インシデントを返します。

degrees 操作は、各頂点のすべてのエッジのカウントを返すだけです。 一方、 inDegrees は入力エッジのみをカウントし、outDegreesは出力エッジのみをカウントします。

グラフ内のすべての頂点の入力角度を数えましょう。

graph.inDegrees().show();

その結果、 GraphFrame があり、頂点がないものを除いて、各頂点への入力エッジの数が表示されます。

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

7. グラフアルゴリズム

GraphFramesは、すぐに使用できる一般的なアルゴリズムも提供します。それらのいくつかを見てみましょう。

7.1. ページランク

ページランクアルゴリズムは、入力エッジを頂点に重み付けし、それをスコアに変換します。

アイデアは、各入力エッジが承認を表し、指定されたグラフで頂点の関連性を高めることです。

たとえば、ソーシャルネットワークでは、人の後にさまざまな人が続くと、その人は上位にランク付けされます。

ページランクアルゴリズムの実行は非常に簡単です。

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

このアルゴリズムを構成するには、以下を提供する必要があります。

  • maxIter –実行するページランクの反復回数– 20を推奨します。少なすぎると品質が低下し、多すぎるとパフォーマンスが低下します。
  • resetProbability –ランダムリセット確率(アルファ)–低いほど、勝者と敗者の間のスコアの広がりが大きくなります–有効な範囲は0から1です。 通常、0.15が良いスコアです

応答は同様のGraphFrame、ですが、今回は各頂点のページランクを示す追加の列が表示されます。

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

私たちのグラフでは、アリシアが最も関連性の高い頂点であり、マーティンとジョンがそれに続きます。

7.2. 接続されたコンポーネント

連結成分アルゴリズムは、孤立したクラスターまたは孤立したサブグラフを検出します。 これらのクラスターは、グラフ内の接続された頂点のセットであり、各頂点は同じセット内の他の頂点から到達可能です。

connectedComponents()メソッドを使用して、パラメーターなしでアルゴリズムを呼び出すことができます。

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

アルゴリズムは、各頂点とそれぞれが接続されているコンポーネントを含むGraphFrameを返します。

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

私たちのグラフには1つのコンポーネントしかありません。これは、孤立したサブグラフがないことを意味します。 このコンポーネントには、自動生成されたID(この場合は154618822656)があります。

ここにはもう1つの列(コンポーネントID)がありますが、グラフは同じです。

7.3. 三角形のカウント

三角形のカウントは、ソーシャルネットワークグラフでのコミュニティの検出とカウントとして一般的に使用されます。 三角形は3つの頂点のセットであり、各頂点は三角形の他の2つの頂点と関係があります。

ソーシャルネットワークコミュニティでは、互いに接続されているかなりの数の三角形を簡単に見つけることができます。

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上でグラフ操作を簡単に分散できます。

いつものように、この例の完全なソースコードは、GitHubから入手できます。