このワークショップで用いるデータは、ここからダウンロードするか、http://kateto.net/netscix2016 から入手してください。
このチュートリアルではRのパッケージであるigraphを用いてネットワーク解析とネットワークの可視化の基本を学びます。igraphは Gabor Csardi と Tamas Nepusz によってメンテナンスされています。igraph ライブラリを使うことで、R, Python, C/C++ 言語において様々な方法でネットワークの解析や可視化ができます。このワークショップでは R での igraph を扱います。RとRStudioのインストールが必要です。また、R用のigraphの最新版もインストールしてください。Rから以下のようにタイプしていただければインストールできます。
install.packages("igraph")
ネットワークを扱う前に、Rの基本を簡単に紹介/おさらいしておきましょう。
あるオブジェクトに値を代入したい時には assign()
, <-
, =
のいずれかを使います。
x <- 3 # xに3を代入します
x # 式の値を求め、結果を出力します
y <- 4 # yに4を代入します
y + 5 # 式の値を求めますが、yは4のままです
z <- x + 17*y # 代入
z # 式の値を求めます
rm(z) # remove z: zというオブジェクトを消去します
z # zを出力しようとするとエラーが出ます!
標準的な演算子である <
, >
, <=
, >=
, ==
(等しい), !=
(等しくない)を使うことができます。比較はブーリアン値、すなわち TRUE
か FALSE
を返します(単に T
と F
で略されることが多いです)。
2==2 # 等しい
2!=2 # 等しくない
x <= y # 以下:"<"や">"、">="も使えます
特殊な定数には以下のようなものがあります。
# NA - データが欠損しているか不詳な場合
5 + NA # 式の中で使われた場合、結果は基本的にNA
is.na(5+NA) # 欠損がないかをチェック
# NULL - NULLは空のオブジェクト(例えば、ヌル/空リスト)
10 + NULL # 空のオブジェクトを戻り値として使います(長さ0)
is.null(NULL) # NULLかどうかチェックできます
Infや-Infは正と負の無限大を意味します。ある数字をゼロで割った時のような数学的演算で返されます。
5/0
is.finite(5/0) # その数字が有限か確認します(この場合は違います)
NaN (Not a Number)は、ゼロをゼロで割るといった適切でない演算を行った結果として出力されます。
0/0
is.nan(0/0)
ベクトルはRにおける重要な関数である c()
を使って作図できます。
v1 <- c(1, 5, 11, 33) # 要素数4の実数ベクトル
v2 <- c("hello","world") # 要素数2の文字列ベクトル
v3 <- c(TRUE, TRUE, FALSE) # 論理値ベクトルで、c(T, T, F)と同じ
単一のベクトルに異なった要素の型が混じっていると、最も制限の緩い型の要素になります:
v4 <- c(v1,v2,v3,"boo") # 全ての要素は文字列として扱われます
ベクトルを生成するその他の方法として以下の方法があります:
v <- 1:7 # c(1,2,3,4,5,6,7)と同じ
v <- rep(0, 77) # 0を77回繰り返す:vは77個のゼロを持ったベクトル
v <- rep(1:3, times=2) # 1,2,3を2回繰り返す
v <- rep(1:10, each=2) # 各々の要素を2回繰り返す
v <- seq(10,20,2) # sequence関数;10から20の間で2ずつ異なる数列
v1 <- 1:5 # 1,2,3,4,5
v2 <- rep(1,5) # 1,1,1,1,1
あるベクトルの長さを確認したい時は:
length(v1)
length(v2)
要素ごとの操作:
v1 + v2 # 要素ごとの足し算
v1 + 1 # それぞれの要素に1を加えます
v1 * 2 # それぞれの要素に2をかけます
v1 + c(1,7) # (1,7) はv1と長さの異なるベクトルであるためうまくいきません
数式操作:
sum(v1) # 全ての要素の合計
mean(v1) # 全ての要素の平均
sd(v1) # 標準偏差
cor(v1,v1*5) # v1とv1*5の相関
論理操作:
v1 > 2 # それぞれの要素を2と比較し、論理値ベクトルを返します
v1==v2 # それぞれの要素の一致を照合し, 論理値ベクトルを返します
v1!=v2 # それぞれの要素の不一致を照合します(!(v1==v2)と同様)
(v1>2) | (v2>0) # |はブーリアンのORを意味し、ベクトルを返します
(v1>2) & (v2>0) # &はブーリアンのANDを意味し、ベクトルを返します
(v1>2) || (v2>0) # ||はブーリアンのORを意味し、単一値を返します
(v1>2) && (v2>0) # &&はブーリアンのANDを意味し、単一値を返します
ベクトルの要素:
v1[3] # v1の3番目の要素
v1[2:4] # v1の2から4番目の要素
v1[c(1,3)] # 要素1と3(note that your indexes are a vector)
v1[c(T,T,F,F,F)] # 要素1と2(TRUE値のみ返す)
v1[v1>3] # v1>3は3より大きい要素をTRUEとする論理値ベクトル
Rにおけるインデックスは1から始まりますので、0からインデックスする言語を使っていた人たちは混乱したり動揺したりするかもしれません。 あるベクトルにさらなる要素を付け加える場合は、単純に値を割り当てればよいです。
v1[6:10] <- 6:10
ベクトルに長さをそのまま割り当てることもできます。
length(v1) <- 15 # the last 5 elements are added as missing data: NA
要因ベクトルはカテゴリーデータを格納する時に用います。
eye.col.v <- c("brown", "green", "brown", "blue", "blue", "blue") #ベクトル
eye.col.f <- factor(c("brown", "green", "brown", "blue", "blue", "blue")) #要因ベクトル
eye.col.v
## [1] "brown" "green" "brown" "blue" "blue" "blue"
eye.col.f
## [1] brown green brown blue blue blue
## Levels: blue brown green
## [1] brown green brown blue blue blue
## Levels: blue brown green
Rでは要因factorの異なる水準level(それぞれ別の値:blue, brown, green)を特定します。個々の水準は整数値として内的に格納され、各々の数字と要素の水準が対応しています。
levels(eye.col.f) # 要因(カテゴリカル変数)における各水準(別々の値)
## [1] "blue" "brown" "green"
as.numeric(eye.col.f) # 数値としては 1がblue, 2がbrown, 3がgreenに対応
## [1] 2 3 2 1 1 1
as.numeric(eye.col.v) # 文字ベクトルを強制的に数値にすることはできない
## Warning: 強制変換により NA が生成されました
## [1] NA NA NA NA NA NA
as.character(eye.col.f)
## [1] "brown" "green" "brown" "blue" "blue" "blue"
as.character(eye.col.v)
## [1] "brown" "green" "brown" "blue" "blue" "blue"
行列は次元をもったベクトルです。
m <- rep(1, 20) # 全てが1の20個の要素からなるベクトル
dim(m) <- c(5,4) # 次元は5と4に設定されているので、ここでのmは5行4列の行列
matrix()を使って、ある行列を作ってみましょう。
m <- matrix(data=1, nrow=5, ncol=4) # 上の行列と同じで、5行4列の全てが1の行列
m <- matrix(1,5,4) # 同上
dim(m) # mの次元はいくつでしょうか?
## [1] 5 4
ベクトルを組合せて行列を作ってみましょう。
m <- cbind(1:5, 5:1, 5:9) # 3つのベクトルを列としてまとめ、5行3列 の行列にする
m <- rbind(1:5, 5:1, 5:9) # 3つのベクトルを行としてまとめ、3行5列 の行列にする
行列の要素を選びましょう。
m <- matrix(1:10,10,10)
m[2,3] # 行列mの第2行、第3列にある単一のセルを選択
m[2,] # 行列mの2行目全体を1つのベクトルとして
m[,2] # 行列mの2列目全体を1つのベクトルとして
m[1:2,4:6] # 1行目と2行目、4列目から6列目の部分行列
m[-1,] # 最初の1行"を除く"すべての行
行列を用いたその他の操作です。
m[1,]==m[,1] # 1行目の要素が、対応する1列目の要素と等しいか
m>3 # 行列mの要素が3より大きければTRUEを、さもなくばFALSEを返す論理値行列
m[m>3]# TRUE要素、すなわち3より大きい要素のみ選択
t(m) # 行列mの行と列を入れ替え (移項transpose)
m <- t(m) # 移項したmを新たな行列mとする
m %*% t(m) # %*%で行列の乗算
m * m # *で要素ごとの乗算
配列は、次元が2以上の時に用います。array()機能を用いて作成できます:
a <- array(data=1:18,dim=c(3,3,2)) # 3x3x2の3次元配列
a <- array(1:18,c(3,3,2)) # 上と同様の配列を意味
リストはオブジェクトの集合です。1つのリストにあらゆる種類の要素、すなわち文字列、数値ベクトル、行列、別のリストなどを入れることができます。こうすることで、リストにある要素に簡単にアクセスできます。
l1 <- list(boo=v1,foo=v2,moo=v3,zoo="Animals!") # 4つのコンポーネントからなるリスト
l2 <- list(v1,v2,v3,"Animals!")
空リストを作ってみましょう。
l3 <- list()
l4 <- NULL
リストの要素にアクセスしてみましょう。
l1["boo"] # シングルブラケットでbooにアクセスすると、リストを返す
l1[["boo"]] # ダブルブラケットでbooにアクセスすると、数値ベクトルを返す
l1[[1]] # リストの最初のコンポーネントを返すので、上と同じ意味
l1$boo # $作用素を用いると、ダブルブラケットと同じように名前のついた要素にアクセスできる
リストにさらなる要素を付け加えましょう。
l3[[1]] <- 11 # 空のリストl3に要素を加える
l4[[3]] <- c(22, 23) # 空のリストl4の3つ目の要素としてベクトルを加える
上でリストl4の3つ目の要素にベクトルを加えたので、1つ目と2つ目の要素が生成され、空値(NULL)が与えられます。
l1[[5]] <- "More elements!" # リストl1には4つの要素があったので、ここで5つ目を加えた
l1[[8]] <- 1:11
上で8つ目の要素を加えましたが、6・7番目はなかったので、空値(NULL)が生成されます。
l1$Something <- "A thing" # 9番目の要素に"something"と名付けられた"A thing"が加えられた
データフレームとは、データセット表を保存するために用いられる特殊なリストです。行をケース、列を変数として捉えます。各々の列は単一のベクトルもしくは要因ベクトルになります。
データフレームを作ってみましょう:
dfr1 <- data.frame( ID=1:4,
FirstName=c("John","Jim","Jane","Jill"),
Female=c(F,F,T,T),
Age=c(22,33,44,55) )
dfr1$FirstName # dfr1の2列目にアクセスします。
## [1] John Jim Jane Jill
## Levels: Jane Jill Jim John
Rでは dfr1$FirstName
を1つのカテゴリー変数として認識しますので、文字列ベクトルではなく、1つの要因ベクトルとして扱われる点に注意しましょう。それではRに’FirstName’が単一ベクトルであることを伝え、この問題を解決しましょう。
dfr1$FirstName <- as.vector(dfr1$FirstName)
その他に、stringsAsFactors=FALSE
を使うことで、最初からRに要因ベクトルはイヤだよと伝えることもできます。
dfr2 <- data.frame(FirstName=c("John","Jim","Jane","Jill"), stringsAsFactors=F)
dfr2$FirstName # 成功:要因ベクトルではない
## [1] "John" "Jim" "Jane" "Jill"
データフレームの要素にアクセスしてみましょう。
dfr1[1,] # 1行目の全列
dfr1[,1] # 1列目の全行
dfr1$Age # Age列の全行
dfr1[1:2,3:4] # 1・2行目の3・4列、つまりJohnとJimのgenderとage
dfr1[c(1,3),] # 1行目と3行目の全列
データの中から、年齢が30を超えた人の名前を探し出しましょう。
dfr1[dfr1$Age>30,2]
## [1] Jim Jane Jill
## Levels: Jane Jill Jim John
データにある全ての女性の平均年齢を求めてみましょう。
mean ( dfr1[dfr1$Female==TRUE,4] )
Rにおける制御とループはすごく単純です (下記参照)。前者は一連のコード (コードブロック) を実行するかを、後者はそれを何回実行するかを決定します。Rにおけるコードブロックは中カッコ{}で囲まれています。
# if (条件) 処理1 else 処理2
x <- 5; y <- 10
if (x==0) y <- 0 else y <- y/x #
y
## [1] 2
# for (連続する変数) 処理
ASum <- 0; AProd <- 1
for (i in 1:x)
{
ASum <- ASum + i
AProd <- AProd * i
}
ASum # sum(1:x)と同義
## [1] 15
AProd # prod(1:x)と同義
## [1] 120
# while (条件) 処理
while (x > 0) {print(x); x <- x-1;}
# 式を繰り返し、, ループを終えるためにbreakを使う
repeat { print(x); x <- x+1; if (x>10) break}
大半のRの関数では、色の名称、16進数、RGB値で色を指定できます。下のように単にRで図をプロットする時は、x
とy
が点の座標で、pch
は点の形、cex
が点のサイズ、col
が色を表わします。Rでプロットする際にパラメータを確認したい時は、?par
で確認しましょう。
plot(x=1:10, y=rep(5,10), pch=19, cex=3, col="dark red")
points(x=1:10, y=rep(6, 10), pch=19, cex=3, col="557799")
points(x=1:10, y=rep(4, 10), pch=19, cex=3, col=rgb(.25, .5, .3))
RGBのレンジが0から1になっていることにお気づきでしょう。Rのデフォルトではこうなっていますが、rgb(10, 100, 100, maxColorValue=255)
のようにセットすることもできます。 不透明度/透過率はalpha
のパラメータ(0から1までの範囲)で設定できます。
plot(x=1:5, y=rep(5,5), pch=19, cex=20, col=rgb(.25, .5, .3, alpha=.5), xlim=c(0,6))
16進法で色彩表現するなら、grDevices
パッケージのadjustcolor
を使って透過率alphaを設定できます。遊びがてら、グラフのパラメータとして用いるpar()
の機能を使って、グラフの背景をグレーにしてみましょう。
par(bg="gray40")
col.tr <- grDevices::adjustcolor("557799", alpha=0.7)
plot(x=1:5, y=rep(5,5), pch=19, cex=20, col=col.tr, xlim=c(0,6))
ビルトインされている色の名前を使いたいなら、これが全色の名前をリストアップする方法です:
colors() # 全ての既定色をリストアップ
grep("blue", colors(), value=T) # 名前に"blue"の付く色
たくさんの対比色や色合いが必要になる場合も多いです。Rにはいくつか前もって設定されている色の組み合わせ(パレット)が搭載されていて、自動で生成してくれます。例えば:
pal1 <- heat.colors(5, alpha=1) # heatパレットから5色、不透明
pal2 <- rainbow(5, alpha=.5) # rainbowパレットから5色、半透明
plot(x=1:10, y=1:10, pch=19, cex=10, col=pal1)
plot(x=1:10, y=1:10, pch=19, cex=10, col=pal2)