文部科学省発行「高等学校情報科『情報Ⅰ』教員研修用教材」の「学習21」にある「さまざまな形式のデータとその表現形式」では、表形式以外のデータ表現例として、離散グラフと隣接行列が紹介されています。こちらの内容をJavaScriptとネットワークのグラフを可視化できるライブラリのCytoscape.jsで学習する方法を紹介いたします。
サンプルプロジェクト
グラフとは
グラフ理論のグラフは、駅と路線の関係のような「ネットワーク」を表現できる「データ構造」及び「図」を指します。
例えば、岡本所長は東京の八王子市に住んでいますが、八王子には新宿駅から「JR」と「京王線」のどちらを使ってもたどり着くことができます。新宿と八王子の関係をグラフで表してみます。
※ 表示の関係上、特急の停車駅のみを掲載しています。
駅が頂点(ノード)で路線が(エッジ)です。
このようなネットワークを表すときにノードとエッジで構成された離散グラフの図が役に立ちます。
この図から読み取れる情報として、立川と分倍河原は路線が繋がっています(JR南部線があります)。つまり、立川と八王子の間に事故があった時には南部線で迂回できます。
グラフをコンピューター上で表現するためには?
先ほどの図はパワーポイントで適当に描いたものですが、プログラムで自動的に経路を計算したりするためにはコンピュータで処理できるデータ形式で表現する必要があります。
例として、以下のような離散グラフがあったとします。
これは以下のような隣接行列で表すことができます。
隣接行列ではノード間が繋がっているところは「1」、繋がっていないところは「0」で表現します。
この隣接行列をプログラミングの2次元配列で記述すると以下のような形になります。
let label = ["a","b","c","d","e","f"];
let matrix = [
[0,1,1,1,0,0],
[1,0,0,1,0,0],
[1,0,0,1,0,0],
[1,1,1,0,1,0],
[0,0,0,1,0,1],
[0,0,0,0,1,0],
];
更に、隣接行列を隣接リストで表現すると以下のような形になります。
let data = {
a : [ "b","c","d" ] ,
b : [ "a" , "d" ] ,
c : [ "a" , "d" ] ,
d : [ "a" , "b" , "c" , "e" ] ,
e : [ "d" , "f" ] ,
f : [ "e" ]
}
このような項目(キー)と値(バリュー)で表現するデータ形式をキー・バリュー形式とも呼びます。
隣接行列から隣接グラフを求める
上記の隣接行列と隣接グラフは同じものを表現しているため、繰り返しや分岐処理を駆使すれば相互変換が可能です。
let data = {a:[], b:[], c:[], d:[], e:[], f:[]};
let label = ["a","b","c","d","e","f"];
let matrix = [
[0,1,1,1,0,0],
[1,0,0,1,0,0],
[1,0,0,1,0,0],
[1,1,1,0,1,0],
[0,0,0,1,0,1],
[0,0,0,0,1,0],
];
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] == 1) {
data[label[i]].push(label[j]);
}
}
}
console.log(data); // 隣接リストをログに書き出し
隣接リストから離散グラフを描画する
ネットワーク図を書き出せるライブラリを用れば、隣接リストから離散グラフを描画できます。全体のソースコードはサンプルプロジェクトを参照して下さい。
Cytoscape.js用の配列を作成する
ノードとエッジのオブジェクトを作成して、配列に格納します。ノードはa~fの6個、エッジは7x双方向で14個、計20個を配列に格納することになります。ノードやエッジのオブジェクトは二次元なので、それらを格納する配列は都合、三次元になります。
let graph = [];
for (let key in data) {
graph.push({data:{id:key}});
for (let i = 0; i < data[key].length; i++) {
graph.push({
data:{
id:key + data[key][i],
source:key,
target:data[key][i]
}
});
}
}
console.log(graph); //グラフ用データをログに書き出し
ノードのオブジェクト
{data:{id: "a"}}
エッジのオブジェクト
{data: {id: "ab", source: "a", target: "b"}}
配列をconsole.logで表示した例
グラフを出力
HTML側にあらかじめ要素を一つ用意しておき、そこにグラフを書き出します。スタイルシート的な記述を加えることで、グラフの見た目をある程度装飾することも可能です。
let cy = cytoscape({
container: document.getElementById('cy'),
style: [
{
selector: 'node',
style: {
'background-color': 'pink',
'label': 'data(id)',
'text-valign':'center',
}
},
],
elements:graph
});