R Advent Calenderの18日目の記事です
「お手持ちのデータからネットワークを作成をしてみよう」というテーマです。
Rを使ったネットワークの解析(次数や媒介中心性、コミュニティ検出etc...)のやり方については様々な方が本やブログにまとめてらっしゃいます。ところが、いざ手持ちのデータをネットワーク解析しようとしても、ネットワークの形式に変換する方法が分からず、手が付けられていないという方も多いのではないでしょうか?
そういう人の手助けになるような記事を目指しました。
解析方法についてはこういうとこ見てください。
R+igraph問題「友好関係ネットワークから派閥を検出」 @kztakemoto さんによる解説記事 #R #igraph #sna - CodeIQ Blog
ネットワーク分析をもうちょっと勉強 - でたぁっ 感動と失敗の備忘録
ネットワークの基本的な知識はこの本読めば身につきます。
ネットワークを扱うパッケージはPythonのnetworkxやRのigraphがよく使われます。
プログラミング言語の特性もあり、ネットワークの生成にはnetworkx、次数や中心性などのインタラクティブな解析にはigraphのほうが向いている気がします。pythonでもigraphパッケージが使えますが、Rとは関数が違っていたりしてややこしいです。
PythonのnetworkxとRのigraphの比較は次の記事に詳しいです。
Python/NetworkXで簡単ネットワーク分析 - あんちべ!
枝リストの考え方とネットワーク化
複雑ネットワークは「隣接行列」か「枝リスト」の形で表現されます。データを隣接行列の形にするのはめんどくさい&ネットワークがsparse(頂点の数に対して枝の数が少ない)の場合、枝リストのほうがデータが軽くなるため、枝リストを用いたほうがベターです。
例えばアニメのキャラクターの人間関係ネットワークなら、データを次のように表現すると分かりやすいでしょう。とりあえず枝の向きを考えない場合を扱います。
キャラクター1 | キャラクター2 | 枝の重み |
星宮いちご | 霧矢あおい | 10 |
星宮いちご | 紫吹蘭 | 7 |
霧矢あおい | 藤堂ユリカ | 5 |
… | … | … |
さっき調べて知ったのですが、igraphにはgraph.data.frameっていう超便利な関数があるんですね。これを使えば上の形式のデータフレームを一発でネットワークにできます。
詳しい使い方はココで。有向グラフの場合も載っています。
> aikatsu <- read.csv("aikatsu.csv",header=T)
> print(aikatsu)
キャラクター1 キャラクター2 枝の重み
1 星宮いちご 霧矢あおい 10
2 星宮いちご 紫吹蘭 7
3 霧矢あおい 藤堂ユリカ 5
> library(igraph)
> ai.net<-graph.data.frame(aikatsu,directed=F)
> plot(ai.net)
Pythonのnetworkxを使って同じことをするならこんな感じでしょうか?ちなみにpandasはRのデータフレームと同じようなことができるようになるパッケージです。(ちなみに『Pythonによるデータ解析入門――NumPy、pandasを使ったデータ処理』という邦訳本が12/26に発売するそうです)
In [1]: import pandas as pd
In [2]: import networkx as nx
In [3]: ai_net = nx.Graph()
In [4]: aikatsu = pd.read_csv("aikatsu.csv",encoding="shift-jis")
In [5]: print aikatsu
キャラクター1 キャラクター2 枝の重み
0 星宮いちご 霧矢あおい 10
1 星宮いちご 紫吹蘭 7
2 霧矢あおい 藤堂ユリカ 5
In [6]: #graph.data.frameの代わりに一行ずつ参照して枝を追加(add_edge)
In [7]: for line in range(0,len(aikatsu)):
...: temp = aikatsu.ix[line]
...: ai_net.add_edge(temp[u"キャラクター1"],temp[u"キャラクター2"],weight=temp[u"枝の重み"])
...:
In [8]: nx.draw(ai_net)
スミマセン、文字化けしちゃってます。pythonのnetworkx(というか描画に利用しているmatplotlib)でunicodeをプロットする方法が分かりません。どなたかご存じの方がいらっしゃれば教えていただけると嬉しいです。
日本語を表示したければ、エクスポートしたファイルをigraphもしくはGephiやCytoscapeなどの描画ソフトで読み込んことしか思いつきません。
ついでに、networkxではnx.Graph()で空のネットワークを作成してadd_edge()で新しい頂点との間に張ることができます。igraphでは関数で新しい頂点を追加してからでないと枝を張れません。
↑のプログラムを見るとnetworkxのほうがややこしそうに見えますが、実際にはいちいち新しい頂点を追加しなくていいのが便利なことも多いです。Twitterのフォロー関係のネットワークを描く場合など。
枝リストのデータフレームにするまで
イラストコミュニケーションサイトのpixivのデータを取ってきたやつがあるので、枝リストのデータフレームの形にしてみます(直接ネットワークにしたほうが楽な気もしますが)。
具体的には下のような形です。picIDがユニークなイラストのID、userIDが描いた人のID、dateが投稿日です。それぞれのキャラクターのタグがある場合フラグが立っています(つまり、イラストにそれぞれのキャラクターが描かれている場合に1、描かれていない場合0です。タグのつけ忘れなど例外もありますが)
picID | userID | date | 星宮いちご | 霧矢あおい | 紫吹蘭 | 有栖川おとめ | 藤堂ユリカ | 北大路さくら | 一ノ瀬かえで | 神崎美月 |
40333631 | 204601 | 2013/12/17 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
40332903 | 5071100 | 2013/12/17 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
40332253 | 5071100 | 2013/12/17 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
40331783 | 6125338 | 2013/12/17 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 |
40331561 | 1948681 | 2013/12/17 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
… | … | … | … | … | … | … | … | … | … | … |
このとき、同じ絵に描かれているキャラクターにリンクを張った「共演ネットワーク」を作ってみましょう。
具体的なコードはこちらの記事にまとめました。Pythonのpandasで書きましたが、同じようなことをRやSQL、SPSSなどでも出来ると思います。
Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理
- 作者: Wes McKinney,小林儀匡,鈴木宏尚,瀬戸山雅人,滝口開資,野上大介
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/12/26
- メディア: 大型本
- この商品を含むブログを見る
まずは次のような形式にデータを変換します。キャラクターがどの絵に描かれているのかを一行ごとに表示しています。キャラクターごとにIDを振っている(例えば「藤堂ユリカ」のIDは「4」)のは、後で重複を消すためです。
picID | キャラクター | ID |
40333631 | 藤堂ユリカ | 4 |
40332903 | 紫吹蘭 | 2 |
40332253 | 藤堂ユリカ | 4 |
40331783 | 有栖川おとめ | 3 |
40331783 | 北大路さくら | 5 |
40330932 | 藤堂ユリカ | 4 |
40330688 | 星宮いちご | 0 |
… | … | … |
次にpicIDをキーにして内部結合(inner join)。こうすると絵ごとのキャラクターの全ての組み合わせを抽出することができます。
picID | キャラクター_x | ID_x | キャラクター_y | ID_y |
40332253 | 藤堂ユリカ | 4 | 藤堂ユリカ | 4 |
40331783 | 有栖川おとめ | 3 | 有栖川おとめ | 3 |
40331783 | 有栖川おとめ | 3 | 北大路さくら | 5 |
40331783 | 北大路さくら | 5 | 有栖川おとめ | 3 |
40331783 | 北大路さくら | 5 | 北大路さくら | 5 |
40330932 | 藤堂ユリカ | 4 | 藤堂ユリカ | 4 |
… | … | … | … | … |
そして、今回は枝の向きのない共演ネットワークを考えるので、同じキャラクターの組み合わせと重複した組み合わせ(上の例では「有栖川おとめ×北大路さくら」と「北大路さくら×有栖川おとめ」は同じ)を削除しましょう。「ID_x < ID_y」という条件で削除すればOKです。もしも向きありのネットワークの場合、時間や順番を条件にします。
あとはキャラクターの組み合わせを集計すれば、枝リストの形のデータが出てきます。
キャラクター_x | キャラクター_y | picID |
一ノ瀬かえで | 神崎美月 | 37 |
北大路さくら | 一ノ瀬かえで | 36 |
北大路さくら | 神崎美月 | 22 |
星宮いちご | 一ノ瀬かえで | 39 |
星宮いちご | 北大路さくら | 42 |
星宮いちご | 有栖川おとめ | 162 |
… | … | … |
これでめでたしめでたし。これを枝リストの処理にかければネットワークが完成します。これをcostar.csvという名前で出力し、Rのigraphで描画します。
library(igraph)
aikatsu <- read.csv("costar.csv",header=T)
ai.net<-graph.data.frame(aikatsu,directed=F)
plot(ai.net,edge.width=E(ai.net)$picID/100) #枝の太さは適宜調整