Amazon Web Services ブログ

グラフ化なら、おまかせください (パート 1 – 航空ルートの事例)

Amazon Neptune に関する記事を複数回に分けてお届けします。本シリーズ記事で、グラフアプリケーションデータセットと、多数の異なるドメインおよび問題空間から取り出されたクエリを探求します。

Amazon Neptune は高速で信頼性が高い完全マネージド型グラフデータベースで、高度に連結されたデータの保存およびクエリ実行のために最適化されています。高度に連結されたデータを使用するオンラインアプリケーションでは、接続をナビゲートし、エンティティ同士のリレーションシップの強度、重要性または品質を活用できるような接続性能がクエリの仕事量で求められますが、まさに理想的な用途と言えます。皆さんは、こんな問いかけに遭遇したことがおありだと思います。

  • 私たちに共通の友人や仲間はいる?
  • あるネットワークエレメント、たとえばルーターやスイッチが故障すると、自分のネットワーク内でその影響がおよぶアプリケーションやサービスはどれ? ネットワークに最重要顧客用の冗長性はある?
  • 2 つの駅を地下道を使って行き来する最短ルートはどれ?
  • このお客様に次に買うもの、次に見るもの、次に聞くものをあなたがすすめるとしたら、何を?
  • ユーザーがアクセスしたり変更したりする権限がある製品、サービス、サブスクリプションはどれ?
  • この荷物を A 地点から B 地点に届けるのに最安または最速の方法は?
  • 銀行または保険会社に共謀して詐欺をはたらきそうな連中グループはどれ?

これで、高度に連結されたデータの管理および意味を理解する必要性にすでにお気づきだと思います。

本シリーズ記事はまず、世界のエアラインの運航ネットワークをモデリングしたオープンソースの航空路データセットで始めることにします。このデータセットには Practical Gremlin というブックが付属しています。

本シリーズ記事の事例はすべて、Analyze Amazon Neptune Graphs using Amazon SageMaker Jupyter Notebooks で記述されている Amazon SageMaker および Neptune の統合ソリューションを使用した Jupyter ノートブックとして提供します。各ノートブックには、サンプルデータおよびクエリ、ならびにデータモデル上の解説と本アプリケーションのユースケースに対応するクエリ設計テクニックが含まれています。

航空路データセットの起動

次の表で、[Launch Stack] ボタンの1つを選択して、AWS CloudFormation コンソールから Neptune-SageMaker スタックを起動します。チェックボックスを選択して、AWS CloudFormation が IAM リソースを作成することを確認します。 次に、[Create] を選択します。

注意: 本ソリューションでデプロイする Neptune および SageMaker のリソースにはコストが発生します。SageMaker がホストするノートブックを使用すると、ノートブックをホストする EC2 インスタンスに対して料金がかかります。このブログ記事では、ml.t2.medium インスタンスを使用しています。このインスタンスは、AWS 無料利用枠 の対象です。

リージョン 表示 起動
米国東部 (バージニア北部) 表示
米国東部(オハイオ州) 表示
米国西部(オレゴン州) 表示
欧州(アイルランド) 表示

ノートブックインスタンスを起動する

スタックが作成されたら、Amazon SageMaker コンソールを開き、左側のメニューから [Notebook instances] を選択します。Neptune ノートブックインスタンスの横の [Open] リンクをクリックします。

Jupyter ウィンドウで、Neptune ディレクトリを開き、Let-Me-Graph-That-For-You サブディレクトリを開きます。次に、01-Air-Routes.ipynb ノートブックを開きます。

航空路データセット

この航空路データセットは、世界のエアラインの運航ネットワークの大部分をモデリングしています。データがカバーする頂点は、7 つの大陸にわたる 237 の国または地域に位置する 3,397 の空港におよびます。エッジは、空港間のルートや大陸、国、空港を結ぶ路線を表しています。グラフには 52,639 のエッジがありますが、そのうち 45,845 が航空路線を表します。

航空路データセットは GraphML 形式で利用可能ですが、Neptune のバルクローダー API は CSV 形式のファイルを想定しています。今回 GraphML を CSV に変換するために、AWS Labs GitHub プロジェクトから入手可能な graphml2csv.py を使用しました。次に、変換後のファイルのコピーをリージョン固有の S3 バケットに格納しました。ノートブックは、お使いの Neptune および SageMaker インスタンスとして同じリージョンの S3 バケットから Neptune へデータをロードします。

今回のノートブックでは、gremlinpython を使用した Neptune インスタンスの接続および操作方法を説明しています。

ノートブックがヘルパーモデル neptune.py を使用し、Neptune のローダー API を使用してデータをグラフにロードします。このヘルパーコードは次に、Neptune に接続して、変数 g をグラフトラバーサルソースにバインドします。この変数 g は後続のすべての gremlinpython クエリに使用します。(neptune.py の詳細は、ブログ記事「Neptune-SageMaker solution」でお読みいただけます)

それでは、グラフからわかることを見てみましょう。

Neptune が動作するか確認するために、シンプルなクエリを実行してみます。以下のクエリでは、グラフのすべての頂点およびエッジを指示しており、グラフのデモグラフィック解析する 2 つのマップを作成します。今回は航空路データセットを使用しているため、当然のことながら返された値は空港やルート関連のものです。

vertices = g.V().groupCount().by(T.label).toList()
edges  = g.E().groupCount().by(T.label).toList()
print(vertices)
print(edges)

ノートブックでこれらのクエリを実行すると、結果は以下のようになります。

[{'continent': 7, 'country': 237, 'version': 1, 'airport': 3397}]
[{'contains': 6794, 'route': 45845}]

8,400 マイルより長いルートを検索

次のクエリでは、グラフで 8,400 マイルより長いルートを検索します。これを行うには、グラフの routes エッジの dist プロパティを調べます。条件に合うエッジが複数見つかったら、距離の降順でソートします。各ルートについて 1 件の結果が出るようにしたいので、where ステップで、既に見つかっているルートの逆方向のルートを除外します。最終的に、空港コードとルート距離を使用した複数の path 結果が出ます。

(ノートブックコードでは、読みやすくするために、Gremlin クエリを複数行に分けて並べてある点に注意してください。エラーを避けるには、Python を使ってこのようにクエリを並べる場合、各ラインが必ずバックスラッシュ文字 “\” で終わるようにします。)

クエリを実行した結果が変数 paths に代入されます。Gremlin クエリが toList() を呼び出して終わったことに注意してください。これは、Gremlin に結果を表にして返すよう指示するものです。その後、Python の for ループを使って、これらの結果を print で出力することができます。表の各エントリは、それ自体が出発地の空港コード、ルートの長さ、到着地の空港コードからなるリストです。

ここにクエリを示します。

paths =  g.V().hasLabel('airport').as_('a') \
              .outE('route').has('dist',gt(8400)) \
              .order().by('dist',Order.decr) \
              .inV() \
              .where(P.lt('a')).by('code') \
              .path().by('code').by('dist').by('code') \
              .toList()

for p in paths:
    print(p)

ノートブックでコードを実行すると、以下の結果が生成されます。

['DOH', 9025, 'AKL']
['PER', 9009, 'LHR']
['PTY', 8884, 'PEK']
['DXB', 8818, 'AKL']
['SIN', 8756, 'LAX']
['MEX', 8754, 'CAN']
['SYD', 8591, 'IAH']
['SYD', 8574, 'DFW']
['JNB', 8434, 'ATL']
['SIN', 8433, 'SFO']

発見したルートを表す棒グラフを描く

グラフと一緒に Python を使う利点の 1 つが matplotlibnumpypandas などのライブラリがある Python の広範なエコシステムを活用すると、さらにデータを分析して図形に表すことができることです。さて、複数の長距離飛行ルートは見つけてありますから、これをグラフィカルに表す棒グラフを作成することができます。

import matplotlib.pyplot as plt; plt.rcdefaults()
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
 
routes = list()
dist = list()

# Construct the x-axis labels by combining the airport pairs we found
# into strings with with a "-" between them.We also build a list containing
# the distance values that will be used to construct and label the bars.
for i in range(len(paths)):
    routes.append(paths[i][0] + '-' + paths[i][2])
    dist.append(paths[i][1])

# Setup everything we need to draw the chart
y_pos = np.arange(len(routes))
y_labels = (0,1000,2000,3000,4000,5000,6000,7000,8000,9000)
freq_series = pd.Series(dist) 
plt.figure(figsize=(11,6))
fs = freq_series.plot(kind='bar')
fs.set_xticks(y_pos, routes)
fs.set_ylabel('Miles')
fs.set_title('Longest routes')
fs.set_yticklabels(y_labels)
fs.set_xticklabels(routes)
fs.yaxis.set_ticks(np.arange(0, 10000, 1000))
fs.yaxis.set_ticklabels(y_labels)

# Annotate each bar with the distance value
for i in range(len(paths)):
    fs.annotate(dist[i],xy=(i,dist[i]+60),xycoords='data',ha='center')

# We are finally ready to draw the bar chart
plt.show()

このコードをノートブックで実行すると、次のグラフが生成されます。

 

大陸ごとの空港の分布を調べる

次の例は、各大陸にいくつの空港があるのかがわかるグラフを求めるものです。クエリは、頂点となるすべての大陸を探すことから始まります。次に、クエリはマップを作成 (あるいは辞書化) するために頂点をグループ化します。キーとなるのは大陸の種類で、値は外に向かうエッジの数を contains ラベルで表します。最後に、結果のマップをキーの昇順でソートします。その結果が Python コードに変数 m として返されるので、これを何度も繰り返して出力を表示します。

# Return a map where the keys are the continent names and the values are the
# number of airports in that continent.
m = g.V().hasLabel('continent') \
         .group().by('desc').by(__.out('contains').count()) \
         .order(Scope.local).by(Column.keys) \
         .next()

for c,n in m.items():
    print('%4d %s' %(n,c))

ノートブックでコードを実行すると、以下の結果が生成されます。

295 Africa
   0 Antarctica
 939 Asia
 596 Europe
 980 North America
 285 Oceania
 305 South America

大陸ごとの分布を表す円グラフを描く

上記のように文字で結果を返すのではなく、円グラフで割合を表示する方がいいのではないでしょうか。それが、次のコードで行っていることであり、2 桁の文字を各大陸のコードに使ってグラフにラベルを付しています。

import matplotlib.pyplot as plt; plt.rcdefaults()
import numpy as np

# Return a map where the keys are the continent codes and the values are the
# number of airports in that continent.
m = g.V().hasLabel('continent').group().by('code').by(__.out().count()).next()

fig,pie1 = plt.subplots()

pie1.pie(m.values() \
        ,labels=m.keys() \
        ,autopct='%1.1f%%'\
        ,shadow=True \
        ,startangle=90 \
        ,explode=(0,0,0.1,0,0,0,0))

pie1.axis('equal')  

plt.show()

このコードによって、以下の円グラフが生成されます。

ロンドンからサンノゼまでのルートを見つけて描画する

連結グラフデータの素晴らしい点の 1 つは、便利な視覚化の作成にとても役立つことです。Python networkx ライブラリはグラフの描画を非常に簡単にします。ノートブックの次の例では、この機能を利用して航空ルートの有向グラフ (DiGraph) を描画します。

以下のクエリは、ロンドンのヒースロー (LHR) 空港を表す頂点を見つけることから始まります。その後、LHR から一度乗り継ぎをして、カリフォルニア州サンノゼ (SJC) を終着点とする 15 のルートを見つけます。これらのルートは、パスのリストとして返されます。それぞれのパスには、見つかった空港を表す 3 文字の IATA コードが含まれます。

この例の主な目的は、大規模なグラフの一部を簡単に抽出し、エンドユーザーが理解しやすい方法でグラフィカルに描画できることをお見せすることです。

import matplotlib.pyplot as plt; plt.rcdefaults()
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import networkx as nx

# Find up to 15 routes from LHR to SJC that make one stop.
paths = g.V().has('airport','code','LHR') \
             .out().out().has('code','SJC').limit(15) \
             .path().by('code').toList()

# Create a new empty DiGraph
G=nx.DiGraph()

# Add the routes we found to DiGraph we just created
for p in paths:
    G.add_edge(p[0],p[1])
    G.add_edge(p[1],p[2])

# Give the starting and ending airports a different color
colors = []

for label in G:
    if label in['LHR','SJC']:
        colors.append('yellow')
    else:
        colors.append('#11cc77')

# Now draw the graph    
plt.figure(figsize=(5,5))
nx.draw(G, node_color=colors, node_size=1200, with_labels=True)
plt.show()

このコードをノートブックで実行すると、こちらのグラフッィクが出力されます:


結論

この記事では、高度に連結された航空路データセットをロードしてクエリを実行し、そのクエリ結果に基づいて棒グラフ、円グラフ、グラフ図を作成する方法を紹介ました。

Let Me Graph That For You シリーズの続きの投稿をご覧ください。特定のアプリケーションユースケースについてのご意見は、コメントにご記入ください。


著者について

Kelvin Lawrence は、Amazon Neptune および他の多くの関連サービスに重点を置く、データベースサービス顧客顧問チームのプリンシパルデータアーキテクトです彼は長年にわたりグラフデータベースを扱っており、本書 Practical Gremlin の著者であり、Apache TinkerPop プロジェクトのコミッターです。

 

 

 

Ian Robinson は、データベースサービスカスタマーアドバイザリーチームのアーキテクトです。彼は「Graph Databases」、「REST in Practice」(両方ともに O’Reilly)と「REST: From Research to Practice」(Springer)と「Service Design Patterns(Addison-Wesley)」の共著者です。