グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係...

49
グラフ グラフとは 1 2 3 4 5 6 7 8 9 10 グラフとは図の ように、ノードと呼ばれる点とエッジと呼ばれる線とからなるものである。 ノードの集合を V、エッジの集合を E とし、G =(V, E) のように、ノード集合とエッ ジ集合の組がグラフである。ただしエッジ集合 E は、引きうるすべてのエッジの部分集 合であるとする。

Upload: others

Post on 30-Mar-2020

0 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1

第 1章

グラフ

1.1 グラフとは

> library(igraph)

> adjm <- matrix(sample(0:1, 100, replace=TRUE, prob=c(0.8,0.2)), nc=10)

> g1 <- graph.adjacency( adjm )

> plot(g1,vertex.color="green",vertex.label.family="Helvetica")

1

2

3

4

5

6

7

8

910

グラフとは図の

ように、ノードと呼ばれる点とエッジと呼ばれる線とからなるものである。

ノードの集合をV、エッジの集合を Eとし、G = (V,E)のように、ノード集合とエッ

ジ集合の組がグラフである。ただしエッジ集合 Eは、引きうるすべてのエッジの部分集

合であるとする。

Page 2: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

2 第 1章 グラフ

1.1.1 ノードとエッジの関係

エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

こともある。また、ノード間の関係にのみ着目する場合には、端点が同一のノードである

エッジは対象外となるが、そのような条件を付けたグラフもよく用いる。ノードは 0個以

上のエッジと接続する。

1.1.2 無向グラフと有向グラフ

エッジの性質によりグラフは大きく2種類に分けられる。エッジに向きのない無向グラ

フとエッジに向きのある有向グラフである。エッジをノードの間を結ぶ道であると考えれ

ば、無向グラフのエッジは両方向性の道であり、エッジの端点のどちらからでももう片方

の端へ移動可能であるが、有向グラフの場合には、片道交通であるので、片方のノードか

らもう片方へは移動できるが、逆の移動できない。有向グラフの場合に2つのノードの間

を行き来できるようにするには、vi → vj という有向エッジと vj → vi という有向エッジ

の2本を引けばよい。

1.2 グラフを行列で表す

n個のノードの間にひきうるエッジは n2 本あり、グラフはエッジ集合としてその部分

集合を指定することで特定される。n2 本の引きうるエッジは n × n行列のセルに対応す

ることができるので、グラフは、n× n行列で表すことができる。

1.2.1 有向グラフの行列表現

有向グラフではエッジ ei → j と ej → iとが異なるので、n× n行列のすべてのセルは

異なるエッジに対応する。

いくつかのグラフを行列表現してみる。

列車

始発駅から終着駅まで n 個の駅を持つ各駅停車の列車のグラフを描く。対角成分の一

つ右上の線上に 1が並んだ形をしている。

M1 =

0 1 0 0 ... 0 00 0 1 0 ... 0 00 0 0 1 ... 0 0... ... ... ... ... ... ...0 0 0 0 ... 0 10 0 0 0 ... 0 0

> library(igraph)

> my.chain <- function(n){

+ M <- matrix(0,n,n)

+ for(i in 1:(n-1)){

+ M[i,i+1] <-1

Page 3: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.2 グラフを行列で表す 3

+ }

+ M

+ }

> n <- 9

> M1 <- my.chain(n)

> g1 <- graph.adjacency(M1)

> plot(g1,vertex.label.family="Helvetica")

12 3

4

5

6

7

8

9

対角成分の二つ

右上の線上に 1が並んだ行列ではどうなるかを見てみる。

M2 =

0 0 1 0 0 0 ... 0 00 0 0 1 0 0 ... 0 00 0 0 0 1 0 ... 0 0... ... ... ... ... ... ... ... ...0 0 0 0 0 0 ... 0 10 0 0 0 0 0 ... 0 00 0 0 0 0 0 ... 0 0

> n <- 9

> M2 <- matrix(0,n,n)

> for(i in 1:(n-2)){

+ M2[i,i+2] <- 1

+ }

> g2 <- graph.adjacency(M2)

> plot(g2,vertex.label.family="Helvetica")

Page 4: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

4 第 1章 グラフ

1

2

3

4

5

67

8

9

列車で考えれば、

一駅飛ばしの快速列車を表していて、奇数番目の駅に停車する快速列車と、偶数番目の駅

に停車する快速列車の 2種類があって、奇数番目の駅からは奇数番目の駅にしか行けず、

偶数番目の駅からは偶数番目の駅にしか行けないことが視覚的に表示されている。

グラフの行列の演算

では、各駅停車の列車と偶奇 2種類の快速列車との全部で3種類が走っているときのグ

ラフと行列表現はどうなるか。

行列は対角成分の一つ右上と二つ右上との2本の線上が1になっている。

M12 < −M1 +M2

> M12 <- M1 + M2

> g12 <- graph.adjacency(M12)

> M12

[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]

[1,] 0 1 1 0 0 0 0 0 0

[2,] 0 0 1 1 0 0 0 0 0

[3,] 0 0 0 1 1 0 0 0 0

[4,] 0 0 0 0 1 1 0 0 0

[5,] 0 0 0 0 0 1 1 0 0

[6,] 0 0 0 0 0 0 1 1 0

[7,] 0 0 0 0 0 0 0 1 1

[8,] 0 0 0 0 0 0 0 0 1

[9,] 0 0 0 0 0 0 0 0 0

Page 5: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.2 グラフを行列で表す 5

> plot(g12,vertex.label.family="Helvetica")

1

2

3

4

5

6

7

8

9

偶奇 2 種類の快

速列車だけの図に戻ろう。これは2つの路線があってそこに各駅停車がそれぞれ走ってい

るのと同じ絵である。

そのことは行列表現ではどのようになっているのだろうか。各駅停車は対角線の一つ右

上に1が並んだ行列であるから n = n1 + n2なる 2つ (n1, n2)の鎖状グラフに対応する

行列M1,M2 をまず作り、それを、ノードの重複がない形式で連結すればよく、それは、

すべての成分が 0である行列 0を用いて、次のように表せる。

M12 =

(M1 00 M2

)> my.join.graph <- function(M1,M2){

+ n1 <- length(M1[,1])

+ n2 <- length(M2[,1])

+ n <- n1+n2

+ M12 <- matrix(0,n,n)

+ M12[1:n1,1:n1] <- M1

+ M12[(n1+1):n,(n1+1):n] <- M2

+ M12

+ }

> n1 <- 5

> M1 <- my.chain(n1)

> n2 <- 4

> M2 <- my.chain(n2)

Page 6: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

6 第 1章 グラフ

> M12.2 <- my.join.graph(M1,M2)

> g12.2 <- graph.adjacency(M12.2)

> M1

[,1] [,2] [,3] [,4] [,5]

[1,] 0 1 0 0 0

[2,] 0 0 1 0 0

[3,] 0 0 0 1 0

[4,] 0 0 0 0 1

[5,] 0 0 0 0 0

> M2

[,1] [,2] [,3] [,4]

[1,] 0 1 0 0

[2,] 0 0 1 0

[3,] 0 0 0 1

[4,] 0 0 0 0

> M12.2

[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]

[1,] 0 1 0 0 0 0 0 0 0

[2,] 0 0 1 0 0 0 0 0 0

[3,] 0 0 0 1 0 0 0 0 0

[4,] 0 0 0 0 1 0 0 0 0

[5,] 0 0 0 0 0 0 0 0 0

[6,] 0 0 0 0 0 0 1 0 0

[7,] 0 0 0 0 0 0 0 1 0

[8,] 0 0 0 0 0 0 0 0 1

[9,] 0 0 0 0 0 0 0 0 0

> plot(g12.2,vertex.label.family="Helvetica")

Page 7: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.2 グラフを行列で表す 7

1

2

3

45

6

7

8

9

グラフの行列の

演算はグラフ代数と呼ばれる。若干の補足資料がここにある。

サイクル

TCAサイクルは

の よ う に

(Wikipediaより)、9分子をメンバーとするサイクルを持つ。9駅の環状線と見てもよい。

これに対応するグラフは、9分子の鎖を構成するエッジに終点から始点へのエッジを加え

たものになる

Page 8: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

8 第 1章 グラフ

> my.cycle <- function(n){

+ M <- my.chain(n)

+ M[n,1] <- 1

+ M

+ }

> n <- 9

> M.cycle <- my.cycle(n)

> M.cycle[n,1] <- 1

> g.cycle <- graph.adjacency(M.cycle)

> plot(g.cycle,vertex.label.family="Helvetica")

1

2

3

4

5

6

78

9

グラフを連結して複雑にする

これまでに鎖とサイクルとを作ったので、それらを組み合わせてみる。各駅停車のグラ

フと快速のグラフとの合体を行列の和として計算した。同様に行列を操作して複雑なグラ

フを作ってみる。ノード数 n1の鎖の i番目のノードから出発して j ≥ i番目のノードに

接続するエッジ数 ne のバイパス (ノード数 n2 = ne − 1)を持つグラフを作る。ノード数

n1, n2のグラフの行列の作り方はわかる。2つのグラフを単純に合体させた上で、ノード

数 n1のグラフの第 iノードからノード数 n2のグラフの第 1ノードへのエッジを加え、ま

た、ノード数 n2の最後のノードからノード数 n1のグラフの第 jノードへのエッジを加

える。

> my.bypass.graph <-function(M1,M2,i,j){

+ n1 <- length(M1[,1])

Page 9: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.2 グラフを行列で表す 9

+ n2 <- length(M2[,1])

+ M.bypass <- my.join.graph(M1,M2)

+ M.bypass[i,n1+1] <- 1

+ M.bypass[n1+n2,j] <- 1

+ M.bypass

+ }

> n1 <- 7

> ne <- 3

> n2 <- ne+1

> i <- 3

> j <- 6

> M1 <- my.chain(n1)

> M2 <- my.chain(n2)

> M.bypass <- my.bypass.graph(M1,M2,i,j)

> g.bypass <- graph.adjacency(M.bypass)

> plot(g.bypass,vertex.label.family="Helvetica")

123

4

56

7

8

9

10

11

2つのサイクル

が何カ所かのノードで交叉させてみる。第1のグラフはノード数 n1のサイクルで、第2

のグラフはノード数 n2のサイクルとする。第1のグラフの i1, i2, ...ノードと第2のグラ

フの j1, j2, ...ノードとが共通のノードであるとする。

> my.share.nodes <- function(M1,M2,v1,v2){

+ n1 <- length(M1[,1])

+ n2 <- length(M2[,1])

Page 10: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

10 第 1章 グラフ

+ M.share <- my.join.graph(M1,M2)

+ tmp <- M.share[v1,] + M.share[n1+v2,]

+ M.share[v1,] <- tmp

+ tmp <- M.share[,v1] + M.share[,n1+v2]

+ M.share[,v1] <- tmp

+ M.share[-(n1+v2),-(n1+v2)]

+ }

> n1 <- 9

> n2 <- 7

> v1 <- c(2,8)

> v2 <- c(2,6)

> M1 <- my.cycle(n1)

> M2 <- my.cycle(n2)

> M.share <- my.share.nodes(M1,M2,v1,v2)

> g.share <- graph.adjacency(M.share)

> plot(g.share,vertex.label.family="Helvetica")

1

23

4

5

6

7

8

9

10

11

12

13

14

1.2.2 無向グラフの行列表現

無向グラフでは vi → vj のエッジと vj → vi のエッジとは同じなので、行列表現は2通

りありえます。

一つは、下三角成分をすべて 0 とする表現 (重複がないように vi → vj のエッジと

vj → vi のエッジのうち片方だけを表す)。もう1つは、対称行列とする表現です (両方の

Page 11: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.2 グラフを行列で表す 11

エッジを表し、その結果、行列は対称行列となる)。以下では、対称行列で表すこととし

ます。

対称行列と転置行列

ノード数 n = 4 のグラフがあり、v1 − −v2 と v1 − −v3 と v4 − −v2 の3エッジがあ

るとします。両端のノードの添え字に着目してエッジの本数のセルに1を立てて行列を作

ると 0 1 1 00 0 0 00 0 0 00 1 0 0

となる。この行列は、下三角成分が 0でもないし、対称行列でもない。このような行列か

らグラフを表す対称行列を作るのには転置行列を用いる。

> n <- 4

> M <- matrix(0,n,n)

> edges <- cbind(c(1,1,4),c(2,3,2))

> M[edges] <- 1

> M

[,1] [,2] [,3] [,4]

[1,] 0 1 1 0

[2,] 0 0 0 0

[3,] 0 0 0 0

[4,] 0 1 0 0

> t(M)

[,1] [,2] [,3] [,4]

[1,] 0 0 0 0

[2,] 1 0 0 1

[3,] 1 0 0 0

[4,] 0 0 0 0

> M+t(M)

[,1] [,2] [,3] [,4]

[1,] 0 1 1 0

[2,] 1 0 0 1

[3,] 1 0 0 0

[4,] 0 1 0 0

> sign(M+t(M))

[,1] [,2] [,3] [,4]

[1,] 0 1 1 0

[2,] 1 0 0 1

Page 12: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

12 第 1章 グラフ

[3,] 1 0 0 0

[4,] 0 1 0 0

完全グラフ

すべてのノードペアにエッジがあるグラフを完全グラフと言います。

> my.k.graph <- function(n){

+ M <- matrix(1,n,n)

+ diag(M) <- 0

+ M

+ }

> n <- 1:9

> par(mfcol=c(3,3))

> for(i in 1:length(n)){

+ M <- my.k.graph(n[i])

+ g <- graph.adjacency(M,mode="undirected")

+ plot(g,vertex.label.family="Helvetica")

+ }

> par(mfcol=c(1,1))

1

1

2

1

2

3

12

34

1

2

3

4

5

1

2

3

4

5

6

1

23

4

5

6

7

1

2

3

4

5

6

7

8

123

4

5

6

789

ノードの数を増

やしていくと、点、線分、正三角形、正四面体、というように見ることもできます。その

ように見るには、空間次元を、0,1,2,3,と上げて行く必要があります。

Page 13: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.2 グラフを行列で表す 13

このように完全グラフを空間にある幾何学的対象とみなすとき、これらは正単体と呼ば

れます。ノード数 n個の完全グラフは n−1次元空間にある頂点数 n個の正単体 ((n−1)-

正単体と呼びます)に対応します。頂点数 n個の正単体は

(n

1

)個の頂点 (0-正単体)

(n

2

)個の辺 (1-正単体)

(n

3

)個の正三角形の面 (2-正単体)

(n

4

)個の正四面体の立体 (3-正単体)

• ...

(n

n− 1

)個の (n− 2)-正単体

(n

n

)個の辺 (n− 1)-正単体

を持ちます。

ランダムなグラフ

要素同士の関係の有無に偏りがあるのかないのかを解析するときには、『偏りがないと

きにできるはずのグラフ』がどのようなものなのかを把握する必要がある。その例とし

て、すべてのエッジの存在が等確率で決まるモデルに基づくグラフを作ってみる。n(n−1)

2

本のエッジに 0か 1かの値を確率的にわりつけ、それを用いて行列を作る。

> n <- 20

> p <- 0.1

> n.edge <- n*(n-1)/2

> n.iter <- 9

> par(mfcol=c(3,3))

> for(i in 1:n.iter){

+ s <- sample(0:1,n.edge,replace=TRUE,prob=c(1-p,p))

+ M.random <- matrix(0,n,n)

+ M.random[upper.tri(M.random)] <- s

+ M.random <- M.random + t(M.random)

+ g.random <- graph.adjacency(M.random,mode="undirected")

+ plot(g.random,vertex.label=NA)

+ }

> par(mfcol=c(1,1))

Page 14: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

14 第 1章 グラフ

エッジの存在確

率を上げてみる。

> p <- 0.3

> n.edge <- n*(n-1)/2

> n.iter <- 9

> par(mfcol=c(3,3))

> for(i in 1:n.iter){

+ s <- sample(0:1,n.edge,replace=TRUE,prob=c(1-p,p))

+ M.random <- matrix(0,n,n)

+ M.random[upper.tri(M.random)] <- s

+ M.random <- M.random + t(M.random)

+ g.random <- graph.adjacency(M.random,mode="undirected")

+ plot(g.random,vertex.label=NA)

+ }

> par(mfcol=c(1,1))

Page 15: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.3 グラフの統計量 15

1.3 グラフの統計量

グラフというデータセットを与えると一つの値を返すような関数は統計量。グラフを表

す行列を与えると一つの値を返すような関数は統計量。

1.3.1 ノードの次数

ノードに接続するエッジの本数を次数と言うので、ノードの次数の平均値はグラフの統

計量。エッジが多ければ多いほど次数の平均値は大きくなるグラフ全体が均質であれば

エッジの次数の分散は小さくなる

> library(igraph)

> # ランダムグラフ生成回数

> n.g <- 1000

> Gs <- list()

> mean.degree <- var.degree <- rep(0,n.g)

> for(i in 1:n.g){

+ # erdos.renyi.game()関数で erdos.renyiランダムグラフを生成

+ Gs[[i]] <- erdos.renyi.game(10, 2/10)

+ # ノード次数の平均値

+ degrees <- degree(Gs[[i]])

+ mean.degree[i] <- mean(degrees)

+ var.degree[i] <- sum((degrees-mean.degree[i])^2)/length(mean.degree[i])

Page 16: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

16 第 1章 グラフ

+ }

> par(mfcol=c(2,2))

> for(i in 1:2){

+ plot(Gs[[i]],vertex.label=NA)

+ }

> hist(mean.degree)

> hist(var.degree)

> par(mfcol=c(1,1))

Histogram of mean.degree

mean.degree

Fre

quen

cy

0.5 1.5 2.5 3.5

050

100

150

Histogram of var.degree

var.degree

Fre

quen

cy

0 10 20 30 40

010

020

030

0

1.3.2 グラフ上の距離

ノードからノードへ到達できなければ距離は無限大、到達できれば、到達に要する最少

エッジ数を距離とする。全ノード間の距離を測り、相互に到達できるノードの亜集合 (連

結グラフ)を作れば、連結グラフ数がわかる。これもグラフの統計量。ノード間距離の平

均値も統計量。ノード間距離が無限大であるペア (到達できないペア)の数も統計量

> num.clusters <- mean.dist <- zero.pairs <- rep(0,n.g)

> for(i in 1:n.g){

+ num.clusters[i] <- clusters(Gs[[i]])$no

+ dd <- shortest.paths(Gs[[i]])

+ mean.dist[i] <- average.path.length(Gs[[i]])

+ Inf.pair <- which(dd==Inf)

+ Non.Inf.pair <- which(dd!=Inf)

Page 17: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.4 重み付きグラフ 17

+ n.Non.Inf.pair <- length(Non.Inf.pair)

+ # 有限距離のペアのうち、自身から自身への距離の分を差し引いたもの

+ # mean.dist[i] <- sum(dd[Non.Inf.pair])/(Non.Inf.pair-length(dd[,1]))

+ zero.pairs[i] <- length(Inf.pair)

+ }

> par(mfcol=c(2,2))

> plot(Gs[[4]],vertex.label=NA)

> hist(num.clusters)

> hist(mean.dist)

> hist(zero.pairs)

> par(mfcol=c(1,1))

Histogram of num.clusters

num.clusters

Fre

quen

cy

1 2 3 4 5 6 7 8

010

020

030

0

Histogram of mean.dist

mean.dist

Fre

quen

cy

1.0 1.5 2.0 2.5 3.0 3.5

050

100

200

Histogram of zero.pairs

zero.pairs

Fre

quen

cy

0 20 40 60 80

050

150

1.3.3 Graph property

グラフの特徴を1つの値で表したものが統計量。それ以外にもグラフの特長をかいつま

んで表す指標が知られている。Wikipediaの Graph propertyにはそのような特徴量のリ

ストがある。

1.4 重み付きグラフ

エッジがあるかないかを 1,0で表してきた。ノード間の関係があるかないか、というこ

とに相当する。ノード間の関係に強弱をつけたいとすると、エッジに重みをつければよ

い。これを重み付きグラフと言う。エッジの重みを行列のセルの値にすれば、重み付きグ

Page 18: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

18 第 1章 グラフ

ラフを表現できる。プロットするときにノード間の最短パス距離を反映させるレイアウト

を用いれば次のようになる。

> adjm <- matrix(sample(c(0,1,4,10), 225, replace=TRUE,prob=c(0.9,0.04,0.04,0.02)), nc=15)

> diag(adjm) <- 0

> g <- graph.adjacency(adjm, mode="undirected",weighted=TRUE)

> plot(g,layout=layout.svd(g),vertex.label=NA)

1.4.1 重み付きグラフの特徴抽出

複数の要素があり、その要素ペア間に距離を定めると距離行列ができる。距離の大小

に寄らずエッジを引くと、それは完全グラフになる。観察データに基づく遠近関係では、

「無関係 (帰無仮説)」でもなにがしかの「近さ」が値として得られてしまうので、閾値を

定めて「無関係~距離:無限大~エッジなし」とすることで、「真実」に近い要素関係に

簡略化することができる。

> # 相関のあるデータを作る

> n.gene <- 100 # たとえば遺伝子数

> n.sample <- 200 # たとえばサンプル数

> X <- matrix(rnorm(n.gene*n.sample),ncol=n.gene)

> n.shu�e <- 10

> for(i in 1:n.shu�e){

+ gr <- sample(1:n.gene,sample(10:20,1))

+ k <- sample(1:n.sample,3*n.sample/4)

Page 19: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.4 重み付きグラフ 19

+ for(j in 1:length(gr)){

+ X[k,gr[j]] <- sort(X[k,gr[j]])

+ }

+ }

> heatmap(X)

14 90 5 49 21 58 39 78 71 35 6 43 11 19 100 52 60 97 44 29 8 28 92 7 37 55 16 50 26 33 9 73 31 65 27 57 79 56 3 4 95 34 48 32 38 91 59 41 10 76 66 46 36 96 72 40 17 68 1 74 67 82 62 75 94 45 99 51 18 70 53 98 15 24 69 22 87 88 93 54 2 80 47 89 63 20 83 25 30 23 86 42 61 77 12 64 84 85 13 81

132691222919091114881924861510819118532172168173739782163259426801627618521392068160170283158771552319818743147543595112159153183176533886113107180167186199967119467156442715047142174196581511250144831451661368118984373318812949171551282001910521179267012611813593117102177164165851576612014619312411011589784657511811047531721431113839140134901517587942130125121981161521096264131815434246313318445127366519717816123760101171100106111991827919522141037459148414014941611316913730119141925610

エッジを重みで選別

このような偏りのあるデータについて、遺伝子ペアの距離を相関係数 r2 で取り、その

逆数をエッジの重みとする。

> adjm <- 1-(cor(X))^2

> #adjm <- 1/(cor(X))^2

> diag(adjm) <- 0

> g <- graph.adjacency(adjm,mode="undirected",weighted=TRUE)

> plot(g,layout=layout.svd(g),vertex.label=NA)

Page 20: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

20 第 1章 グラフ

エッジの重みの

分布を取ると

> hist(E(g)$weight)

Histogram of E(g)$weight

E(g)$weight

Fre

quen

cy

0.0 0.2 0.4 0.6 0.8 1.0

010

0020

0030

0040

00

となっているの

で、重みが 0.7以上のエッジは省略することにすれば

Page 21: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.4 重み付きグラフ 21

> adjm2 <- adjm

> adjm2[which(adjm2>=0.7)] <- 0

> g2 <- graph.adjacency(adjm2,mode="undirected",weighted=TRUE)

> plot(g2,layout=layout.svd(g2),vertex.label=NA)

この図も残した

エッジについては重みを加味しているが、省略をさらに進めて、残したエッジは同じ重み

にする (グラフとしては重みなしのグラフにする)こともできる。

> g3 <- graph.adjacency(sign(adjm2),mode="undirected")

> plot(g3,layout=layout.svd(g3),vertex.label=NA)

Page 22: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

22 第 1章 グラフ

1.5 木

木はループのない無向グラフ (両端ノードが同一なエッジを持たないグラフ)の一種で

あり、次のように色々な定義の仕方がある。

• すべてのノードが連結で、閉路を持たないようなグラフ• サイクルを持たないグラフであり、1本のエッジを加えると必ずサイクルが生じるようなグラフ

• 連結なグラフであり、1本のエッジを取り去ると必ず非連結になるようなグラフ• 任意の2ノードの間に単一のパスがあるようなグラフ• 連結でエッジ数がノード数より1少ないようなグラフ• エッジ数がノード数より1少なく、サイクルを持たないようなグラフ

1.5.1 n分岐木

段階的に n 分岐を繰り返す木を n 分岐木と言います。n 分岐木では、1つの親 (ルー

ト)ノードから n個の子ノードが生じ、n個の子ノードから n2 個の孫ノードが生じます。

第 k世代まで増えると、

1 + n+ n2 + n3 + ...+ nk =k−1∑i=0

ni

> n.children <- c(1,2,3,4)

> k <- 4

Page 23: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.5 木 23

> par(mfcol=c(2,2))

> for(i in 1:length(n.children)){

+ n.node <- sum(n.children[i]^(0:(k-1)))

+ g.tree <- graph.tree(n.node,children=n.children[i])

+ plot(g.tree,vertex.label=NA)

+ }

> par(mfcol=c(1,1))

1.5.2 世代経過

遺伝子のアレルの世代経過は、分岐木となる。平均次世代数が 1.5であると人口 (染色

体人口)は一定となる。次世代数を平均1のポアッソン分布でシミュレートすると次のよ

うになる。平均 a = 1.5のポアソン分布の分布は、次世代 0の確率も低くなく、次世代数

の最頻値は 1となり、次世代数 2の確率は次世代数 0よりも高いと読み取れる。

> k <- 0:10

> a <- 1.5

> plot(k,dpois(k,a),type="h")

Page 24: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

24 第 1章 グラフ

0 2 4 6 8 10

0.00

0.05

0.10

0.15

0.20

0.25

0.30

k

dpoi

s(k,

a)

このような条件

で n.origin = 5からの各アレルの世代経過をシミュレーションしてみる。

> n.origin <- 5

> # 世代数

> k <- 4

> # エッジリストを格納

> ed <- matrix(0,0,2)

> # 現在世代のノード番号

> current <- 1:n.origin

> # 現時点での総ノード数

> n.pop <- n.origin

> for(i in 1:k){

+ # 現世代のノードごとに次世代数をポアソン乱数で指定

+ num.o�spring <- rpois(length(current),a)

+ # 次世代ノード総数

+ num.total.o�spring <- sum(num.o�spring)

+ # 次世代数が 0なら終了

+ if(num.total.o�spring==0){

+ break;

+ }

+ # 次世代ノードに IDを付与

+ o�spring.id <- (n.pop+1):(n.pop+num.total.o�spring)

+ # エッジの両端ノード IDをエッジリストに格納

Page 25: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.5 木 25

+ tmp.st <- n.pop+1

+ for(j in 1:length(num.o�spring)){

+ if(num.o�spring[j]>0){

+ tmp <- cbind(rep(current[j],num.o�spring[j]),tmp.st:(tmp.st+num.o�spring[j]-1))

+ ed <- rbind(ed,tmp)

+ tmp.st <- tmp.st + num.o�spring[j]

+ }

+ }

+ # 総ノード数を更新

+ n.pop <- o�spring.id[length(o�spring.id)]

+ # 現世代ノード IDを更新

+ current <- o�spring.id

+ }

> g <- graph.edgelist(ed)

> plot(g,vertex.label.family="Helvetica")

1

2

3

4

5

6

7

8

9

10

11

1213

14

15

16

1718

19

2021

22

23

24

25

2627

28

29

30

31

3233

34

35 36

37

383940

4142

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

606162

63

646566

67

6869

70

71

72

7374

75

76

77

1.5.3 世代経過、その2

世代ごとに染色体数の変化がないとする。すべての染色体には必ず親がただ一つある。

そのパターンを描くと、次のようになる。水平軸に集団 (n.pop本の染色体)があり、垂直

軸が世代経過であり、下から上に向かって時間が経過している。(上に向かって分岐した)

分岐木が複数本ある森グラフとなっている。これは、ある特定の座位・塩基についての世

代経過のグラフである。

Page 26: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

26 第 1章 グラフ

> n.pop <- 20

> n.gen <- 10

> xy.A <- xy.B <- expand.grid(1:n.pop,1:n.gen)

> ed.A <- ed.B <- matrix(0,0,2)

> for(i in 2:n.gen){

+ tmpA <- tmpB <- cbind((i-2)*n.pop + 1:n.pop,(i-1)*n.pop + 1:n.pop)

+ sh <- (-5):5

+ prob <- 4^(-abs(sh))

+ prob <- prob/sum(prob)

+ rA <- sample(sh,n.pop,replace=TRUE,prob=prob)

+ rB <- sample(sh,n.pop,replace=TRUE,prob=prob)

+ tmpA[,1] <- tmpA[,1]+rA

+ tmpB[,1] <- tmpB[,1]+rB

+ tmpA[which(tmpA[,1]<(i-2)*n.pop+1),1] <- (i-2)*n.pop+1

+ tmpB[which(tmpB[,1]<(i-2)*n.pop+1),1] <- (i-2)*n.pop+1

+ tmpA[which(tmpA[,1]>(i-1)*n.pop),1] <- (i-1)*n.pop

+ tmpB[which(tmpB[,1]>(i-1)*n.pop),1] <- (i-1)*n.pop

+ ed.A <- rbind(ed.A,tmpA)

+ ed.B <- rbind(ed.B,tmpB)

+ }

> plot(xy.A,xlab="population",ylab="ancestors -> o�springs")

> segments(xy.A[ed.A[,1],1],xy.A[ed.A[,1],2],xy.A[ed.A[,2],1],xy.A[ed.A[,2],2],col=1)

Page 27: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.5 木 27

5 10 15 20

24

68

10

population

ance

stor

s −

> o

ffspr

ings

この染色体上の

近傍の座位・塩基では、これとよく似た森グラフを作る。十分に近いとき、まったく同一

の森グラフになる。少し距離がある場合には、2つの座位の間で交叉が起きることで、森

グラフのパターンが変わる。その様子を2つの森グラフを重ねて描くことで示す。ある

ノードに下から赤と黒の2本のエッジが接続しており、それらは2座位の伝達を表して

いる。2座位がともに同じ染色体から伝達しているとき、赤・黒の2本のエッジは平行だ

が、その2本が平行でないとき、そのノードは交叉の結果生じた組換え体であることを意

味している。

このように、個々の座位では木が集まった森であるが、それを異なる座位について重ね

合わせると、交叉・組換えを反映して (木・森より複雑な)グラフになる。これをAncestral

Recombination Graph(ARG)と呼ぶ。

このグラフは親世代から子世代へと向きがあるので、有向グラフであり、また、子世代

に伝えたものが親世代に再度戻ってくることがないから、Acyclic graphである。つまり、

Directed Acyclic Graph(DAG)の例となっている。

DAGは閉路を持たない有向グラフであり、ぐるぐる回らずに探索できることから、計

算機での処理に都合がよく、DAGはネットワーク解析において有用である。DAGが半

順序を表していることは統計量・順序に関する文書でも触れた。

> par(mfcol=c(1,2))

> plot(xy.A,xlab="population",ylab="ancestors -> o�springs")

> segments(xy.A[ed.A[,1],1],xy.A[ed.A[,1],2],xy.A[ed.A[,2],1],xy.A[ed.A[,2],2],col=2)

> plot(xy.B,xlab="population",ylab="ancestors -> o�springs")

> segments(xy.B[ed.B[,1],1],xy.B[ed.B[,1],2],xy.B[ed.B[,2],1],xy.B[ed.B[,2],2],col=1)

> par(mfcol=c(1,1))

Page 28: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

28 第 1章 グラフ

5 10 15 20

24

68

10

population

ance

stor

s −

> o

ffspr

ings

5 10 15 20

24

68

10

population

ance

stor

s −

> o

ffspr

ings

> plot(xy.A,xlab="population",ylab="ancestors -> o�springs")

> segments(xy.A[ed.A[,1],1]-0.05,xy.A[ed.A[,1],2],xy.A[ed.A[,2],1]-0.05,xy.A[ed.A[,2],2],col=2)

> segments(xy.B[ed.B[,1],1]+0.05,xy.B[ed.B[,1],2],xy.B[ed.B[,2],1]+0.05,xy.B[ed.B[,2],2],col=1)

5 10 15 20

24

68

10

population

ance

stor

s −

> o

ffspr

ings

Page 29: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.5 木 29

1.5.4 進化と系統樹

生物の進化を木構造で捉えると系統樹となる。分子進化学では生物種の DNA配列の違

いを距離として (ハミング距離)測り、それをもとに系統樹を推定する。次のような手順

で変異箇所を発生させ、進化をシミュレーションしてみる。

> library(ape)

> # 種の種類 n.sp

> n.sp <- 30

> # DNA長

> n.dna <- 1000

> # 変異箇所を格納 (0/1変異とする)

> x <- list()

> # DNA配列を 0/1で格納

> X <- matrix(0,n.sp,n.dna)

> # n.sp種類の種を一つずつ、歴史のある時点でいずれかの種からわかれたものとしてシミュレーションする

> for(i in 1:n.sp){

+ # 第1の種は始源配列から歴史の中で変異を蓄積したものとする

+ if(i==1){

+ # 変異数は、ポアソン分布に従うものとする

+ n.mut <- rpois(1,100)

+ x[[i]] <- sample(1:n.dna,n.mut)

+ }else{

+ # 第2番目以降は、それまでの種に親を求める

+ parent <- sample(1:(i-1),1)

+ # 親から別れた時刻を歴史の全行程の長さを 1とした時刻としてランダムに決める

+ time.bifurc <- runif(1)

+ # 親から別れて以降の変異数をポアソン分布で決める

+ n.mut <- rpois(1,100*time.bifurc)

+ # 親と分かれる前の変異を受け継ぐ

+ x[[i]] <- c(x[[parent]][1:(length(x[[parent]])*time.bifurc)],sample(1:n.dna,n.mut))

+ }

+ # 変異箇所を dna配列で1とする

+ X[i,x[[i]]] <- 1

+ }

> # 距離行列をハミング距離・マンハッタン距離で計算し行列に納める

> d <- as.matrix(dist(X,method="manhattan"))

> # Neighbor-Joining法で木構造化してプロットする

> nj.out <- nj(d)

> plot(nj.out,"u")

Page 30: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

30 第 1章 グラフ

1

2

3

45

6

7

8

9

10

11

12

13

14

15

16

17

18

19 20 21

22

23

24

25

26

27

28

29

30

Neighbor-

Joining法では、ノード間の距離を保ちながらノードを増やし、最終的にすべての種が木

の末梢ノード (葉)となるようなエッジに長さの情報を乗せた木グラフを構成する。実際、

nj()関数によって作られた木グラフをグラフ理論パッケージの木オブジェクトとし、その

木の上で末梢ノード間の距離を計算すると、元の距離行列の値とほぼ等しくなっているこ

とが確かめられる。

> # nj()関数の出力のエッジ情報からグラフオブジェクトを作る

> g.nj <- graph.edgelist(nj.out$edge)

> # nj()関数の出力からエッジの重みを指定し、その上で葉ノード間の距離行列を作成する

> sh.nj <- shortest.paths(g.nj,weights=nj.out$edge.length)[1:n.sp,1:n.sp]

> # 2つの距離行列の成分を比較する

> plot(c(sh.nj),c(d))

> abline(0,1,col=2)

Page 31: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.5 木 31

0 50 100 150 200

050

100

150

200

c(sh.nj)

c(d)

Neighbor-

Joining法による木グラフ再構成後の距離行列が元のそれと完全には一致していない。こ

れは同じ座位に複数回の変異が発生することにより、ペアワイズの距離が進化の過程と正

確には一致しなくなることで発生する。従って、上のシミュレーションで、種数を減ら

し、DNA長を長くすると、この乖離は解消するし、また、すべての変異が新規座位に起

きたと仮定したシミュレーションをしても同じく、解消する。まず、少種数・長 DNAの

例を示す。

> # 種の種類 n.sp

> n.sp <- 6

> # DNA長

> n.dna <- 10000

> # 変異箇所を格納 (0/1変異とする)

> x <- list()

> # DNA配列を 0/1で格納

> X <- matrix(0,n.sp,n.dna)

> # n.sp種類の種を一つずつ、歴史のある時点でいずれかの種からわかれたものとしてシミュレーションする

> for(i in 1:n.sp){

+ # 第1の種は始源配列から歴史の中で変異を蓄積したものとする

+ if(i==1){

+ # 変異数は、ポアソン分布に従うものとする

+ n.mut <- rpois(1,100)

+ x[[i]] <- sample(1:n.dna,n.mut)

+ }else{

Page 32: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

32 第 1章 グラフ

+ # 第2番目以降は、それまでの種に親を求める

+ parent <- sample(1:(i-1),1)

+ # 親から別れた時刻を歴史の全行程の長さを 1とした時刻としてランダムに決める

+ time.bifurc <- runif(1)

+ # 親から別れて以降の変異数をポアソン分布で決める

+ n.mut <- rpois(1,100*time.bifurc)

+ # 親と分かれる前の変異を受け継ぐ

+ x[[i]] <- c(x[[parent]][1:(length(x[[parent]])*time.bifurc)],sample(1:n.dna,n.mut))

+ }

+ # 変異箇所を dna配列で1とする

+ X[i,x[[i]]] <- 1

+ }

> # 距離行列をハミング距離・マンハッタン距離で計算し行列に納める

> d <- as.matrix(dist(X,method="manhattan"))

> # Neighbor-Joining法で木構造化してプロットする

> nj.out <- nj(d)

> #plot(nj.out,"u")

> # nj()関数の出力のエッジ情報からグラフオブジェクトを作る

> g.nj <- graph.edgelist(nj.out$edge)

> # nj()関数の出力からエッジの重みを指定し、その上で葉ノード間の距離行列を作成する

> sh.nj <- shortest.paths(g.nj,weights=nj.out$edge.length)[1:n.sp,1:n.sp]

> # 2つの距離行列の成分を比較する

> plot(c(sh.nj),c(d))

> abline(0,1,col=2)

Page 33: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.5 木 33

0 50 100 150

050

100

150

c(sh.nj)

c(d)

次に、全変異が新

規座位である場合を示す。

> library(ape)

> # 種の種類 n.sp

> n.sp <- 30

> # DNA長

> n.dna <- 10000

> # 変異箇所を格納 (0/1変異とする)

> x <- list()

> # DNA配列を 0/1で格納

> X <- matrix(0,n.sp,n.dna)

> N.total <- 0

> # n.sp種類の種を一つずつ、歴史のある時点でいずれかの種からわかれたものとしてシミュレーションする

> for(i in 1:n.sp){

+ # 第1の種は始源配列から歴史の中で変異を蓄積したものとする

+ if(i==1){

+ # 変異数は、ポアソン分布に従うものとする

+ n.mut <- rpois(1,5)

+ # すべての変異は 1番から通し番号でつける=すべての変異は新規座位の変異とみなす

+ x[[i]] <- 1:n.mut

+ N.total <- n.mut

+ }else{

+ # 第2番目以降は、それまでの種に親を求める

Page 34: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

34 第 1章 グラフ

+ parent <- sample(1:(i-1),1)

+ # 親から別れた時刻を歴史の全行程の長さを 1とした時刻としてランダムに決める

+ time.bifurc <- runif(1)

+ # 親から別れて以降の変異数をポアソン分布で決める

+ n.mut <- rpois(1,100*time.bifurc)

+ # 親と分かれる前の変異を受け継ぐ

+ # すべての変異は 1番から通し番号でつける=すべての変異は新規座位の変異とみなす

+

+ x[[i]] <- c(x[[parent]][1:(length(x[[parent]])*time.bifurc)],(1+N.total):(N.total+n.mut))

+ N.total <- N.total + n.mut

+ }

+ # 変異箇所を dna配列で1とする

+ X[i,x[[i]]] <- 1

+ }

> # 距離行列をハミング距離・マンハッタン距離で計算し行列に納める

> d <- as.matrix(dist(X,method="manhattan"))

> # Neighbor-Joining法で木構造化してプロットする

> nj.out <- nj(d)

> #plot(nj.out,"u")

> # nj()関数の出力のエッジ情報からグラフオブジェクトを作る

> g.nj <- graph.edgelist(nj.out$edge)

> # nj()関数の出力からエッジの重みを指定し、その上で葉ノード間の距離行列を作成する

> sh.nj <- shortest.paths(g.nj,weights=nj.out$edge.length)[1:n.sp,1:n.sp]

> # 2つの距離行列の成分を比較する

> plot(c(sh.nj),c(d))

> abline(0,1,col=2)

Page 35: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.6 木に変換する 35

0 100 200 300 400

010

020

030

040

0

c(sh.nj)

c(d)

1.6 木に変換する

重み付きグラフのエッジを重みの軽重で選別してエッジ数を減らす例を 1.4.1節で扱っ

た。その後、木グラフを扱ってきた。実は、距離行列から Neighbor-Joining法で木グラ

フを作った作業も、重み付きグラフのエッジの選別をしたものである。ただし、NJ法の

場合には、節となるノードを追加している点が異なる。木構造は、計算機が走査するのに

好都合な構造であるが、実際、ヒトにとっても全体を見渡しやすい形式である。従って、

データセットを木構造に変換することは、全体を理解しやすくするための手法とも言え

る。データセットを木に変換して理解しやすくする2つの手法をここで扱う。一つは、多

要素をクラスタ分けする手法であり、もう1つは多要素が作る分布を捉えるための手法で

ある。

1.6.1 階層的クラスタリング

要素間の距離行列からスタートして、近いものから順にまとめて行き、最終的にすべて

の要素が1つの塊にまとめ上げる手法である。

複数の要素の亜集合ができたときにその亜集合と他の亜集合・要素との距離をどのよう

に定めるかによって方法が細分される。Neighbor-Joining も葉ノードを束ねた後、それ

を束ねる根元にノードを発生させる方法であるので、階層的クラスタリングの一種とも言

えるが、NJ法の場合は、クラスタに分けることに主眼があるというよりも、「木」の枝分

かれ具合 (トポロジー)に大きな興味があり、その上で、木の枝の長さにも興味があるの

に対し、階層的クラスタリングでは、亜集団に分けることに興味が強いと言うニュアンス

Page 36: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

36 第 1章 グラフ

の違いがある。データは先にエッジ数を減らす例で用いたものに、Rの階層的クラスタリ

ング関数を適用する。

> # 相関のあるデータを作る

> n.gene <- 100 # たとえば遺伝子数

> n.sample <- 200 # たとえばサンプル数

> X <- matrix(rnorm(n.gene*n.sample),ncol=n.gene)

> n.shu�e <- 10

> for(i in 1:n.shu�e){

+ gr <- sample(1:n.gene,sample(10:20,1))

+ k <- sample(1:n.sample,3*n.sample/4)

+ for(j in 1:length(gr)){

+ X[k,gr[j]] <- sort(X[k,gr[j]])

+ }

+ }

> par(mfcol=c(1,2))

> d <- dist(X)

> d.t <- dist(t(X))

> plot(hclust(d))

> plot(hclust(d.t))

> par(mfcol=c(1,1))

70 133

110

130

26 158

8411

214

716

166 13

728 14

882

111

194

2940

539 19

019

746 12

519

969 16

312

243 78

2510

612

3 7 8597 17

4 41 49

8316

918

112

819

61

32 34 74

182

146

37 193 1

3419

286 11

49

51 195 8

073 91

90 159

154

170

92 186

118

13 149

143

7256 15

318 35 15

017

22 23

131

93 144 12

010

018

313

616

517

720

015

20 167

2745 18

810

8817

117

88 38

98 117

89 168

164

21 6852 12

4 50

176 5

718

936

95 119

139

17 184

115

94 162 5

9 6410

953 14

513

812 12

915

215

13

126

101

3381

160

610

24

166

198 67

76 142

30 113

127

5418

711 10

8 180

77 105

71 156

48 7955 17

9 42 47 173

62 132

135

96 104

4458

60 155

191

31 107

19 75 141

6399 18

515

714

017

512

124 11

665

16 6114 22

87 1035

1015

2025

Cluster Dendrogram

hclust (*, "complete")d

Hei

ght

9419 56

54 81 13

25 9395

2926 66

2482 1 91

57 9937 47

27 7217

8684

32 7959

22 3387

60 6780

62 71 16 432 773 42 78 85

46 5135 63

5 88 39 75 83

45 64 20 539

14 446

9741 48 58 74 7 7092 10

08 10

3649 98

3130 4

76 8915 68 6

911 38

7318 50 90

9612 52

2855 61 23 40

2134 65

05

1015

20

Cluster Dendrogram

hclust (*, "complete")d.t

Hei

ght

縦と横のそれぞ

れでクラスタリングをしたが、その両方の結果を用いて行列型データの行と列を再配列し

てプロットしたものがヒートマップである。

Page 37: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.6 木に変換する 37

> heatmap(X)45 64 83 75 39 44 14 9 20 53 88 5 85 78 46 51 35 63 70 7 10

0 92 97 41 48 74 58 6 38 11 69 68 15 36 98 49 31 30 76 89 4 8 10 28 23 40 34 65 21 61 55 50 90 18 96 12 52 73 99 57 47 37 19 56 94 95 29 26 66 13 93 25 81 54 24 82 1 91 27 72 17 33 22 59 87 60 67 79 32 84 86 62 71 80 43 16 42 3 77 2

103872214611665241161211751401571556058443110719163991851417519135961046213217347424879551791567111108187771051805412710261608133101419816676142671133015131261521629411559641718410913812129531451391199536571895017612452891681642168981178381831001201651771361449313122320167152002745188881781711091955190159807391170154118131491869215017235181437215356133701301106613716111119482294051903914828261588414711216369199197125462512310612243788574941971748316918114619337134192114861961281741823234

1.6.2 最小全域木と多様体推定

最小全域木

データ空間に雲がたなびく用に要素が分布を形成しているとする (point-cloud デー

タ)。その雲がどのような形をしているのかに興味があるときには、点同士が相互に近接

している部分はつなぎ、そうでない部分はつながないことでその形に類似した形状を描く

ことができる。その一つのやり方は、グラフのエッジの部分集合を木になるように選び、

その選んだエッジの長さの和が最小にするもので、最小全域木と呼ばれる。以下はその例

である。

> library(vegan)

> n.pt <- 200

> t <- sort(runif(n.pt))

> x <- t * sin(t*pi) + rnorm(n.pt,0,0.05)

> y <- t * cos(t*pi) + rnorm(n.pt,0,0.05)

> xy <- cbind(x,y)

> d <- as.matrix(dist(xy))

> mst.out <- mst(d)

> ed.list <- which(mst.out==1,arr.ind=TRUE)

> par(mfcol=c(1,2))

> plot(x,y)

Page 38: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

38 第 1章 グラフ

> plot(x,y)

> segments(xy[ed.list[,1],1],xy[ed.list[,1],2],xy[ed.list[,2],1],xy[ed.list[,2],2])

> par(mfcol=c(1,1))

0.0 0.2 0.4 0.6

−1.

0−

0.8

−0.

6−

0.4

−0.

20.

00.

2

x

y

0.0 0.2 0.4 0.6

−1.

0−

0.8

−0.

6−

0.4

−0.

20.

00.

2

x

y

多様体推定

高次元空間に point cloudがありながら、その cloudの次元が低いことがある。3次元

空間に曲面がたなびいているときには、曲面は2次元なので、うまく座標変換することで

二次元平面にプロットすることができる。cloudは全体として見れば曲がっているが、局

所的にはまっすぐな空間とみなせるならば (それが多様体)、局所における距離を定め、そ

れをグラフに反映させるべき点間距離として採用することで、局所の遠近関係のみを採用

した距離行列が作成できる。距離行列からMDSで座標に対応づけられるから『統計量・

距離・順序』を参照、任意次元空間に「広げ」ることができる。以下、Rで実施してみる。

> # ロールケーキ状の cloudを作る

> library(vegan)

> library(rgl)

> n.pt <- 1000

> t <- seq(from=0,to=1,length=n.pt)*4*pi

> r <- seq(from=0,to=1,length=n.pt)

> x <- r*cos(t)

> y <- r*sin(t)

> z <- runif(n.pt)*max(r)

> xyz <- cbind(x,y,z)

Page 39: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.6 木に変換する 39

> # 多様体推定する。ndim=2次元に展開する

> result <- isomap(dist(xyz),ndim=2,k=5)

> # 3次元プロットする

> m.abs <- max(abs(xyz))

> xyz.2 <- xyz

> xyz.2 <- rbind(xyz.2,rep(m.abs,3))

> xyz.2 <- rbind(xyz.2,rep(-m.abs,3))

> col <- rainbow(n.pt)

> plot3d(xyz.2,col=col)

> rgl.postscript("role.eps","eps")

10.5

y0

-0.5-1

z

-0.5 0 0.5 1

-1

-0.5

0

0.5

1

x

-1

多様体推定した

結果はこの通り。

> plot(result,col=col)

Page 40: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

40 第 1章 グラフ

−2 −1 0 1 2 3 4

−3

−2

−1

01

23

Dim1

Dim

2

1.7 ネットワーク

重み付きグラフをネットワークと呼ぶこともある。空間をグラフに見立てて、その空間

における動きをモデル化することもできる。また、条件付き確率をネットワーク上に表現

したものがベイジアン・ネットワークである。

1.7.1 状態推移

三塁ベースの無い野球 (三角野球)を考える。イニングの交代は2アウトで交代とする。

アウトカウントとランナーの状態は、(x, y, z)の xを1塁ベースのランナーの有無、y を

2塁ベースのランナーの有無、z をアウトカウントとすると

S =

0 0 00 0 10 0 21 0 01 0 11 0 20 1 00 1 10 1 21 1 01 1 11 1 2

の 12通りの状態がある。状態を変化させるのは、バッターが、1塁打、2塁打、ホーム

ランのいずれかを打つか、アウトになるかの4通りの事象である。走塁の上手下手やフォ

Page 41: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.7 ネットワーク 41

アボールなど細かいことは抜きにして、12通りの状態と4通りの状態変化事象のみで考

えてみる。4つの事象の生起確率が p = (p1, p2, p3 = ph, p4 = pO);∑4

i=1 pi = 1とする

と、12通りの状態が x(t)から x(t+ 1)に変化することは

x(t+ 1) =

ph 0 pO ph 0 pO ph 0 pO ph 0 pOpO ph 0 0 ph 0 0 ph 0 0 ph 00 pO ph 0 0 ph 0 0 ph 0 0 php1 0 0 0 0 0 p1 0 0 0 0 00 p1 0 pO 0 0 0 p1 0 0 0 00 0 p1 0 pO 0 0 0 p1 0 0 0p2 0 0 p2 0 0 p2 0 0 p2 0 00 p2 0 0 p2 0 pO p2 0 0 p2 00 0 p2 0 0 p2 0 pO p2 0 0 p20 0 0 p1 0 0 0 0 0 p1 0 00 0 0 0 p1 0 0 0 0 pO p1 00 0 0 0 0 p1 0 0 0 0 pO p1

x(t)

と表される。このような行列を推移行列と言う。推移行列の第1列を見る。

phpO0p100p200000

この第1行目は、「ノーアウト・ランナーなし (0 0 0)でホームランを打つと、ノーアウト・

ランナーなし (0 0 0)になる (ただし、1点入る)」確率が ph であることを表している。同

様に、第2、3、…12行目はすべて「ノーアウト・ランナーなし (0 0 0)」からバッター1

人が起こす事象の結果、生じる状態の確率を表している。「(0 0 0)の状態からバッター1

人が起こす事象の確率の和」は1だから、この列ベクトルの和は ph + pO + p1 + p2 = 1

になっている。他の列も同様に、ある状態 (x, y, z)において4つの事象が起きて生じる状

態に対応して列ベクトルが決まるが、その和は1である。なお、ツーアウトからアウトの

事象が発生したときは、攻守交代であるので、ノーアウト・ランナーなしに推移すること

として行列は作成してある。

この状態推移行列を作成して、状態がどのように推移するかをシミュレーションして

みる。

> p <- c(0.25,0.04,0.01,0.7)

> M <- matrix(0,12,12)

> M[cbind(c(4:6,10:12,4:6,10:12),1:12)] <- p[1]

> M[cbind(c(7:9,7:9,7:9,7:9),1:12)] <- p[2]

> M[cbind(c(1:3,1:3,1:3,1:3),1:12)] <- p[3]

> M[cbind(c(2,3,1,5,6,1,8,9,1,11,12,1),1:12)] <- p[4]

> M

Page 42: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

42 第 1章 グラフ

[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]

[1,] 0.01 0.00 0.70 0.01 0.00 0.70 0.01 0.00 0.70 0.01 0.00 0.70

[2,] 0.70 0.01 0.00 0.00 0.01 0.00 0.00 0.01 0.00 0.00 0.01 0.00

[3,] 0.00 0.70 0.01 0.00 0.00 0.01 0.00 0.00 0.01 0.00 0.00 0.01

[4,] 0.25 0.00 0.00 0.00 0.00 0.00 0.25 0.00 0.00 0.00 0.00 0.00

[5,] 0.00 0.25 0.00 0.70 0.00 0.00 0.00 0.25 0.00 0.00 0.00 0.00

[6,] 0.00 0.00 0.25 0.00 0.70 0.00 0.00 0.00 0.25 0.00 0.00 0.00

[7,] 0.04 0.00 0.00 0.04 0.00 0.00 0.04 0.00 0.00 0.04 0.00 0.00

[8,] 0.00 0.04 0.00 0.00 0.04 0.00 0.70 0.04 0.00 0.00 0.04 0.00

[9,] 0.00 0.00 0.04 0.00 0.00 0.04 0.00 0.70 0.04 0.00 0.00 0.04

[10,] 0.00 0.00 0.00 0.25 0.00 0.00 0.00 0.00 0.00 0.25 0.00 0.00

[11,] 0.00 0.00 0.00 0.00 0.25 0.00 0.00 0.00 0.00 0.70 0.25 0.00

[12,] 0.00 0.00 0.00 0.00 0.00 0.25 0.00 0.00 0.00 0.00 0.70 0.25

> # 各列の和が 1であることの確認

> apply(M,2,sum)

[1] 1 1 1 1 1 1 1 1 1 1 1 1

> # 20回の試行

> ts <- 0:20

> X <- matrix(0,length(ts),12)

> # 初期状態はノーアウト・ランナーなし

> X[1,1] <- 1

> for(i in 2:length(ts)){

+ X[i,] <- M %*% X[i-1,]

+ }

> matplot(ts,X,type="l")

Page 43: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.7 ネットワーク 43

0 5 10 15 20

0.0

0.2

0.4

0.6

0.8

1.0

ts

X

グラフで描くと

次のようになる。

> library(igraph)

> g <- graph.adjacency(M,weighted = TRUE)

> plot(g,layout=layout.circle(g),vertex.label.family="Helvetica",edge.width= E(g)$weight*10,edge.arrow.size=E(g)$weight*100)

Page 44: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

44 第 1章 グラフ

1

2

34

5

6

7

8

910

11

12

グラフの行列の固有値分解と指数行列

上のシミュレーションでは、x(t + 1)の状態ベクトルを x(t)に推移ベクトルをかける

ことで算出している。

x(t+ 1) = M tx(1)

と考えてもよい。ただしM t = M ×M × ... ×M とM を t回掛けたものである。野球

の打順のような状態推移の場合は tが整数値のみを取る離散的時間推移であるので、計算

方法がどちらでも構わないが、時間について連続的に取り扱いたいこともある。

単位時間当たり k倍になるとき、連続的な時間 t後には、kt 倍になる、というのは指数

関数と言う。推移行列M の指数対応の計算ができるとよいことがわかる。推移行列が次

のように3つの行列の積であらわせるとして

M = V SU

UV = I (I は単位行列)であるとすると、自然数 tについて

M t = V SUV SU...UV SU = V StU

となる。tを自然数としたが、UV = I は何回登場しても省略できるから、tは自然数に

限らなくてもよさそうであり、実際、そうだと納得することにする。そうすると St が実

数 tで計算可能であるとよい。対角成分以外がすべて 0であるような行列の場合、自然数

tについて

St =

st1 0 0 ... 00 st2 0 ... 0... ... ... ... ...0 00 ... stn

Page 45: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.7 ネットワーク 45

であり、ここでも tを実数に拡張しても成り立つと納得することにすれば UV = I (I は

単位行列)であるとすると、自然数 tについて

M t = V StU = V

st1 0 0 ... 00 st2 0 ... 0... ... ... ... ...0 00 ... stn

U

とできる。実際、このようなM = V SU の分解は可能であり、固有値分解と呼ぶ。

時間経過が連続なので、次のような時間に関する微分方程式が立つ。

dx(t)

dt= M ′x(t)

この書き方に離散の場合の式

x(t+ 1) = Mx(t)

を合わせて書きなおせばx(t+ 1)− x(t)

1= Mx(t)

となる。

簡単な例でやってみる。

dx(t)

dt=

( √32

12

−√32

23

)x(t)

> M <- matrix(c(sqrt(3)/2,1/2,-sqrt(3)/2,2/3),byrow=TRUE,2,2)

> eigen.out <- eigen(M)

> V <- eigen.out[[2]]

> U <- solve(V)

> V%*% diag(eigen.out[[1]]) %*% U - M # 0になる

[,1] [,2]

[1,] 1.110223e-16+0i 2.220446e-16+0i

[2,] -1.110223e-16+0i 1.110223e-16+0i

> t <- seq(from=0,to=20,length=100)

> X <- matrix(0,length(t),2)

> X[1,] <- runif(2)

> for(i in 1:length(t)){

+ X[i, ] <- Re(V%*% diag(eigen.out[[1]]^t[i]) %*% U %*% X[1,])

+ }

> matplot(X,type="l")

Page 46: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

46 第 1章 グラフ

0 20 40 60 80 100

−0.

6−

0.4

−0.

20.

00.

20.

40.

6

X

グラフの隣接行列の固有値分解とノードの重要度

ネットワークがあり、対応する有向グラフの隣接行列が得られているとする。インター

ネットではウェブサイト同士がリンク・被リンクの関係で結びついており、それを隣接行

列で表すこともあるが、そのようなものを考える。今、流入エッジ本数 (被リンク)が多

いほどノードの重要度が高いと考えることとし、同じ流入であるなら、重要度の高いノー

ドからの流入を大きくカウントし、さらに、たくさんのエッジが流出している場合より少

数のエッジが流出しているときに、そのエッジは重要度が高いものとする。

これは、グラフを閉じたフローネットワークとみなし、すべての流出エッジに等しいフ

ローを想定したものとなる。

Rでのグラフの隣接行列とは行と列とがさかさまになるが、第 (i,j)成分を第 jノードか

ら第 iノードへのエッジとすれば、流入元のノードの重要度 xを加味したノードの重みづ

け値は

Mx

と式で書ける。しかしながら、xを求めたいのに、求めるためにそれを使っているので、

よくない。書くならば

x = kMx

として解く必要がある。

また、ノードからの流出エッジ数が少ないほど、そのエッジのもたらす重要度を高くす

るためには、個々のノードの流出エッジに関して標準化する必要がある。

例に沿ってやってみる。

> # 木グラフを作り

Page 47: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.7 ネットワーク 47

> g <- graph.tree(20,4)

> # その隣接行列を取り出し

> M <- as.matrix(get.adjacency(g))

> # グラフにエッジを適当に加えて、木でないグラフにする

> add.n <- 10

> for(i in 1:add.n){

+ j <- sample(1:20,2)

+ M[i,j] <- 1

+ }

> diag(M) <- 0

> # 新しくした隣接行列でグラフを作る

> g <- graph.adjacency(M)

> plot(g,vertex.label=NA)

今、各ノードの流出エッジ数は隣接行列を転置すれば、行列の行和である。

> # Mx = lambda xに合わせるためにMを転置する

> M.t <- t(M)

> # 各ノードの流出エッジ数

> n.out.edge <- apply(M.t,1,sum)

> n.out.edge[which(n.out.edge==0)] <- 1

> # 流出エッジ数で補正する

> Ms <- M.t/n.out.edge

> Ms

Page 48: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

48 第 1章 グラフ

[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]

[1,] 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0 0.0000000

[2,] 0.5000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0 0.0000000

[3,] 1.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0 0.0000000

[4,] 0.3333333 0.3333333 0.0000000 0.0000000 0.3333333 0.0000000 0.0000000 0.0 0.0000000

[5,] 0.3333333 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.3333333 0.0 0.0000000

[6,] 0.5000000 0.5000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0 0.0000000

[7,] 0.0000000 0.3333333 0.0000000 0.3333333 0.3333333 0.0000000 0.0000000 0.0 0.0000000

[8,] 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0 0.0000000

[9,] 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0 0.0000000

[10,] 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0 0.0000000

[11,] 0.0000000 0.0000000 0.5000000 0.0000000 0.0000000 0.5000000 0.0000000 0.0 0.0000000

[12,] 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0 0.0000000

[13,] 0.0000000 0.0000000 0.3333333 0.0000000 0.0000000 0.3333333 0.3333333 0.0 0.0000000

[14,] 0.0000000 0.0000000 0.0000000 0.5000000 0.0000000 0.0000000 0.0000000 0.5 0.0000000

[15,] 0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 0.0 0.0000000

[16,] 0.3333333 0.0000000 0.0000000 0.3333333 0.0000000 0.0000000 0.0000000 0.0 0.3333333

[17,] 0.0000000 0.0000000 0.5000000 0.5000000 0.0000000 0.0000000 0.0000000 0.0 0.0000000

[18,] 0.0000000 0.0000000 0.0000000 0.0000000 0.5000000 0.0000000 0.0000000 0.0 0.5000000

[19,] 0.0000000 0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0 0.0000000

[20,] 0.0000000 0.0000000 0.0000000 0.0000000 0.5000000 0.0000000 0.0000000 0.5 0.0000000

[,10] [,11] [,12] [,13] [,14] [,15] [,16] [,17] [,18] [,19] [,20]

[1,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[2,] 0.5000000 0 0 0 0 0 0 0 0 0 0

[3,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[4,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[5,] 0.3333333 0 0 0 0 0 0 0 0 0 0

[6,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[7,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[8,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[9,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[10,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[11,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[12,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[13,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[14,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[15,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[16,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[17,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[18,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[19,] 0.0000000 0 0 0 0 0 0 0 0 0 0

[20,] 0.0000000 0 0 0 0 0 0 0 0 0 0

Page 49: グラフ - 京都大学2 第1 章 グラフ 1.1.1 ノードとエッジの関係 エッジは2つのノードを端点として有する。ただし、2つのノードが同じノードである

1.7 ネットワーク 49

> # 補正後行列の固有値分解

> eigen.out <- eigen(Ms)

> # 最大固有値に対応する固有ベクトル

> eigen.out[[2]][,1]

[1] 0.0000000+0i 0.0000000+0i 0.0000000+0i 0.1845784+0i 0.2445143+0i 0.0000000+0i 0.3239125+0i

[8] 0.0000000+0i 0.0000000+0i 0.0000000+0i 0.0000000+0i 0.0000000+0i 0.2445143+0i 0.2090012+0i

[15] 0.4180023+0i 0.1393341+0i 0.2090012+0i 0.2768676+0i 0.5537352+0i 0.2768676+0i

> # ノードランクをノードサイズに反映させてプロットする

> #plot(g,vertex.size=round((Re(eigen.out[[2]][,1])+0.1)*50),vertex.label=NA)

流出エッジばか

りのノードサイズは小さく、流入エッジの多いノードが大きく描かれている。

この最大固有値をもたらす固有ベクトルでノードの重要度を得点化したのが、Google

の page rankである。