読者です 読者をやめる 読者になる 読者になる

歩いたら休め

If the implementation is easy to explain, it may be a good idea.

【R】【Python】igraphやNetworkXで手持ちのデータをネットワークに変換してみよう

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っていう超便利な関数があるんですね。これを使えば上の形式のデータフレームを一発でネットワークにできます。

詳しい使い方はココで。有向グラフの場合も載っています。

統計的テキスト解析(6)~語のネットワーク分析~

> 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やSQLSPSSなどでも出来ると思います。

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

 

 

まずは次のような形式にデータを変換します。キャラクターがどの絵に描かれているのかを一行ごとに表示しています。キャラクターごとに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) #枝の太さは適宜調整