StatsBeginner: 初学者の統計学習ノート

統計学およびR、Pythonでのプログラミングの勉強の過程をメモっていくノート。たまにMacの話題。

質問紙(リッカート尺度)のデータってふつうに分析して大丈夫なのか

 アンケート調査なんかで、

Q1. STAP細胞はあります。


□ とてもそう思う
□ そう思う
□ ややそう思う
□ どちらでもない
□ あまりそう思わない
□ そう思わない
□ 全くそう思わない


 というような質問紙を作成してデータをとり、「とてもそう思う」を7点、「全くそう思わない」を1点としてスコア化するようなことをよくやりますね。心理学では*1「尺度開発」と言って、こういう質問紙を用いて個人の心理的な傾向を測定する「尺度」を作成し、その妥当性や信頼性を確かめるためのノウハウが、色々蓄積されています。多くの場合、1つの傾向を測定するために、5〜10個ぐらいの質問項目が並べられて、その平均値がスコアとして扱われます。


 こういう尺度は「リッカート尺度(Likert scales)」と呼ばれるんですが、リッカート尺度に果たして1点から7点のスコアを付けて、単純に足したり割ったりという処理をしていいんだろうか?というのは誰もが疑問に思うところです。
 Stevensの尺度水準というものがあって知ってる人も多いと思いますが、おそらく「順序尺度」だとは言っていいものの、間隔尺度として扱えるか(「全くそう思わない」と「そう思わない」の間隔が、他のところの間隔と同じかどうか)はわからないし、比率尺度として扱えるか(「とてもそう思う」は「全くそう思わない」の7倍強く思っているのか)も分からない。
 リッカート尺度の得点が順序尺度としてしか扱えないのであれば、平均値を求めるのも変だし、相関係数として最も一般的に使われているピアソンの積率相関係数なんかを計算しても意味ないというような話になってきます。


 しかしこの問題には長年に渡る論争の歴史があって、完全に決着しているわけではないんですが、経験に照らして「べつに間隔尺度として扱っといていい」という意見が強いからこそ、心理学等の研究でこういう手法が多様されてるわけですね。
そういう話を書いた文章を今日たまたま読みました。


Carifio, J., & Perla, R. (2008). Resolving the 50‐year debate around using and misusing Likert scales. Medical education, 42(12), 1150-1152.


 コメンタリーなので、証拠を挙げて論証されているというよりは評論的に「俺が正しい」みたいなことが書き連ねてある文章で、本当に議論の中身が気になる場合は、ここに引用されてる文献を見ていく必要があります。私はめんどくさいので読んでませんが、なんか論文書く時とかに必要性が発生したら確認しようと思います。


 有料なので以下要約しておきます。

 リッカート尺度(Likert scales)がどのように使われ、どのように分析されるべきなのかについては、50年以上にわたって議論されてきた。ありがちな指摘として、「リッカート尺度は順序尺度なので、ノンパラメトリックな手法で分析されるべき」というものがある。しかしノンパラメトリックな手法は検出力が低いので、相対的に弱い効果が見逃されてしまう可能性がある。
 歴史的には、ordinalist(順序尺度派)とintervalist(間隔尺度派)の間で論争が行われてきている。個々のLikert items(1個1個の質問)とは対照的に、それを束ねたLikert scalesについては、順序尺度ではなく間隔尺度とみなしてパワフルなパラメトリック分析な手法で分析されるべきという人もいれば、それに反対する人もいるのである。


 あるシミュレーション結果によると、F検定は、仮定が破られた場合でもかなり頑健(結果が歪まない)であることが明らかになっている(ただし等分散性の仮定についてはそうでもない)。これまでに行われたけっこう多くの研究が、リッカート尺度は間隔尺度として扱うことができるということを示しており、100mmの線上にマークさせるような方法(SD法みたいな?)だとほぼ比率尺度として扱うことすらできるようだ。
 特に、大雑把な経験則として言われているように、だいたい8項目以上を束ねた尺度であれば、間隔尺度として扱って問題ないというのが、これまでの実証研究から言えることだ。


 しかし50年以上にわたって、順序尺度派の意見も根強くあるのも確かだ。彼らにはけっこう誤解があるというか、理論的な議論にとらわれて経験的なデータを無視する傾向がある。我々がこれまで書いてきた論文を読んでもらえれば、順序尺度派の見解には誤解がたくさん含まれていること、そして複数のアイテムを束ねる場合に関しては間隔尺度として扱ってパラメトリックな分析を行うのは完全に適切であるということが理解されるはずだ。


 リッカート尺度を間隔尺度とみなして、平均や標準偏差を算出したり、分散分析を行ったり、相関係数を計算したり、相関に基づく様々な多変量解析(重回帰分析や因子分析)を行って、データや仮説をより強力で繊細な方法により分析するのは、完全に適切なことである。リッカート尺度を順序尺度として扱うと、こういう洗練された強力な分析手法の利用や、それによる強力で繊細な理解を妨げることになってしまう。


 要するに、理屈としてはStevensがいうように順序尺度と間隔尺度の間には違いがあって、リッカート尺度は厳密には順序尺度というべきなんだろうけど、少なくともいくつかの質問を束ねて平均を取る方法であれば、間隔尺度とみなして分析してもほとんど結果が歪まないことが経験的・実証的に明らかになっているのだから、間隔尺度として扱うことによる分析の柔軟性などのメリットを享受したほうがいいだろと。

*1:私は心理学専攻ではないけど知ったかぶっておくと

Pythonのリスト内包表記みたいなのをRで書く方法(とおまけ)

Rの小技

 Rでデータ分析するときに最近よくやる書き方があるのですが、よくやるといいながら1か月とか間が開くと忘れているので、メモしておきます。
 2個あるのですが、1個目は将来ネットで検索して役に立ててくれる人がいるかもしれないので、1個目の小技をこのエントリのタイトルにして2個目をおまけ扱いにしました。


 私はべつに、Rにもプログラミングにも詳しい人間ではないので、妥当なやり方かどうかは知りません。
 というか、もっと良いやり方あればご教示いただけると・・・。(とくに2個目)
 
 

1.Pythonのリスト内包表記みたいなやつをRで書く

 Pythonだと、リストに入っている要素について、連続的に何かの処理をして、その結果をリストで返すという処理のために、リスト内包表記というのがありますね。
 ある教科書に載ってた簡単な例でいうと、

cities = ["OSAKA", "TOKYO", "KYOTO"]


 というリストがあったとして、これを全部小文字に変えたいとします。
 そういう時、

cities_new = []
for city in cities:
	city = city.lower()
	cities_new.append(city)


 みたいなことをやらずに(やってもいいが)、

cities_new = [city.lower() for city in cities]


 と書けば終了というやつです。
 実際やってみると、

>>> cities_new = [city.lower() for city in cities]
>>> print(cities_new)
['osaka', 'tokyo', 'kyoto']


 ちゃんとできてますね。
 意味合いとしては、citiesというリストに入っている(in cities)個々の要素にcityという仮の名前をつけて、このcityに対して(for city)、順番にcity.lower()という処理を施して、返される結果が並んだリストを得る、ということですね。


 ちなみに、in citiesのあとにif文をつなげて、条件付きにすることもできます。

>>> cities_new = [city.lower() for city in cities if city.startswith("T")]
>>> print(cities_new)
['tokyo']

 
 
 ところでRでも、リスト*1やベクトル*2に入っている要素に対して、順番に何かの処理を施して、その結果をリストやベクトルで得たいときはあります。
 それでオライリーの『入門機械学習』という、機械学習の基本的な処理をRで演習する教科書を以前読んだのですが、その中ではapplyファミリーを使った↓のような書き方が多用されてました。
 上記の例でやるとすると、

cities <- c("OSAKA", "TOKYO", "KYOTO")
cities_new <- sapply(cities, function(p) {tolower(p)})


 というふうに書きます。ベクトルに対してつかうときはsapply()、リストに対してつかうときはlapply()になります。
 sapply()やlapply()は、1つめの引数で与えたベクトルやリストの要素1個1個について、2つめの引数で与えた関数を適用した結果をまとめて返します。で、この第2引数のところにpという仮の引数を持つ関数定義を埋め込んでやれば、既存の関数を使わなくても色々な処理を自由に書いて、リスト内包表記みたいな処理を一発で書くことができるわけですね。


 上記コードをRで動かしてみたら、

> print(cities_new)
  OSAKA   TOKYO   KYOTO 
"osaka" "tokyo" "kyoto" 


 なんか要素名称もついてきましたが・・・まあやりたいことはできています。
 
 

2.lm()やglm()を内包する関数定義をするとき

 lmでもglmでもいいんですが、関数定義の中に埋め込みたい場合があります。
 たとえば回帰モデルを作って当てはめを行い、その結果を報告しやすい形にまとめて返す関数を作っておきたいとかですね。具体的には、データフレームと、目的変数の列名称、説明変数の列名称を引数として与えると、重回帰分析を行って、summary(lm())で出てくる結果の表の一部を取り出してCSVで出力する・・・みたいな。
 ちなみに私は、たとえばp値に対応する記号を、lm()のデフォルトの"***", "**", "*", "."ではなく、"**", "*", "†"に書き換える、みたいなことを一括でやりたい場合とかがありました(†はascii文字ではないので文字コードの問題に注意が必要ですが)。


 以下では面倒なので、単純にsummary(lm())で出てくる表を表示するところまででやってみますが、そういう関数定義をやろうとした場合に、たとえば、

get_lmsummary1 <- function(df, res, v1, v2) {
	model <- lm(formula=res ~ v1 + v2, data=df)
	result <- summary(model)$coefficients
	return(result)
}


 みたいな書き方をしてもきちんと動きません。以下実際に試してみます。


 まず実験用のデータを乱数で適当につくります。

> set.seed(1)
> predictor_1 = rnorm(n=100, mean=5, sd=3)
> predictor_2 = rnorm(n=100, mean=10, sd=5)
> response =  3.0 + (1.5 * predictor_1) + (2.0 * predictor_2) + rnorm(n=100, mean=0, sd=10)
> 
> d <- data.frame(p_1 = predictor_1, 
+                 p_2 = predictor_2, 
+                 r    = response)
> 
> head(d)
       p_1       p_2         r
1 3.120639  6.898167 25.571309
2 5.550930 10.210579 48.636287
3 2.493114  5.445392 33.496339
4 9.785842 10.790144 35.949973
5 5.988523  6.727077  2.584583
6 2.538595 18.836436 69.457381


 一応、ためしにふつうに回帰分析してみます。

> model <- lm(formula=r ~ p_1 + p_2 , data=d)
> summary(model)

Call:
lm(formula = r ~ p_1 + p_2, data = d)

Residuals:
     Min       1Q   Median       3Q      Max 
-29.4359  -4.3645   0.0202   6.3692  26.3941 

Coefficients:
            Estimate Std. Error t value          Pr(>|t|)    
(Intercept)   3.9710     3.1638   1.255          0.212440    
p_1           1.5704     0.3892   4.035          0.000109 ***
p_2           1.8931     0.2190   8.646 0.000000000000112 ***
---
Signif. codes:  0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 10.43 on 97 degrees of freedom
Multiple R-squared:  0.4839,	Adjusted R-squared:  0.4733 
F-statistic: 45.48 on 2 and 97 DF,  p-value: 0.00000000000001164


 さてこのデータフレームにさっきの関数を適用すると、
 

> get_lmsummary1(df=d, res=r, v1=p_1, v2=p_2)
Error in eval(expr, envir, enclos) : object 'r' not found


 rという名前のオブジェクトはない、とエラーがでます。まぁ、なるほど。
 引数を"r"のように文字列で与えるようにしてもダメでした。


 で、なんかいい解決方法あるのかもしれませんが、よくわからないので、私はとりあえず変数名を文字列で与えて、dfの列名称を検索して列番号を得ることにしました。ムリヤリ感がかなりあります。

> get_lmsummary2 <- function(df, res, v1, v2) {
+ 	res_n <- grep(res, colnames(df))
+ 	v1_n <- grep(v1, colnames(df))
+ 	v2_n <- grep(v2, colnames(df))
+ 	model <- lm(formula=df[,res_n] ~ df[,v1_n] + df[,v2_n])
+ 	result <- summary(model)$coefficients
+ 	rownames(result)[2:3] <- c(colnames(df)[v1_n], colnames(df)[v2_n])
+ 	return(result)
+ }
> 
> get_lmsummary2(df=d, res="r", v1="p_1", v2="p_2")
            Estimate Std. Error  t value           Pr(>|t|)
(Intercept) 3.971034  3.1637959 1.255149 0.2124402378606492
p_1         1.570367  0.3891757 4.035111 0.0001089365648451
p_2         1.893066  0.2189571 8.645832 0.0000000000001118


 これで思った通りの動きにはなりました。


 注意点として、grep()で当てているので、似たような列名称がある場合(1つの列名称をと同じ文字列を含む別の列が存在する場合)に、エラーが起きます。
 たとえば2個目の説明変数の列名称を"p_1_2"にしてみると、"p_1"というパターンは"p_1"にも"p_1_2"にもマッチするので、困ったことになります。

> d2 <- data.frame(p_1 = predictor_1, 
+                 p_1_2 = predictor_2, 
+                 r    = response)
> 
> get_lmsummary2(df=d2, res="r", v1="p_1", v2="p_1_2")
Error in model.frame.default(formula = df[, res_n] ~ df[, v1_n] + df[,  : 
  invalid type (list) for variable 'df[, v1_n]'


 幸い、grep()は正規表現が使えるので、"^"と"$"で挟んで完全一致にしてやると、

> get_lmsummary2(df=d2, res="r", v1="^p_1$", v2="p_1_2")
            Estimate Std. Error  t value           Pr(>|t|)
(Intercept) 3.971034  3.1637959 1.255149 0.2124402378606492
p_1         1.570367  0.3891757 4.035111 0.0001089365648451
p_1_2       1.893066  0.2189571 8.645832 0.0000000000001118


 というふうにちゃんと動きます。
 とりあえず、何かムダなことをやってる感があるので、もうちょっと綺麗な方法があれば知りたいです。
 

*1:Pythonのリストとは意味合いが違う

*2:Pythonには組み込みでベクトルという型はない

統計的仮説検定の「p値」にこだわってはいけないのか

 (タイトルにやや語弊ありますが、「こだわってはいけない」というような禁止的な議論をしている人とか、「p値はもはや完全に無意味である」というような極端な主張をしている人がいるわけではないということは理解しています。)
 

検定のロジックはけっこう凄い

 さっき、

http://www.anlyznews.com/2016/03/p.html 確かにp値に「こだわりすぎる」のはよくないんだと思いますが、伝統的に教えられている、F分布やt分布などに従う検定統計量に持ち込んで議論する方法それ自体は、なかなかよくできたものですよね。
 
よく出来ています。理解せずに振り回す人がいけないのです。
 
https://ask.fm/uncorrelated/answers/134682189373


 というコメントを読んだんですが、確かに、統計学初学者として検定のロジックにはけっこう感心します。
 「その大小が人間にとって直感的な意味も持ち得るもので、かつ、ある定型的な分布に従うことが証明されている検定統計量」が発見されているというのは、凄いことなんじゃないでしょうか。


 私は初歩的な教科書に載っていることしか知りませんが、たとえばカイ二乗検定で適合度を判断する場合、手元のデータからカイ二乗統計量というのを算出しますね。この統計量は、「大きければ大きいほど、モデル=基準値からデータが乖離していること」を示しているので直感的に理解がしやすく、かつ、それがカイ二乗分布に従うことが知られているので*1、「これだけ乖離するのは、どのぐらい稀なことなのか」を議論することが可能になってるわけです。


 分散分析の時に計算されるF統計量も、あれは説明変数によって説明されるデータのバラつきと、残差として扱われるバラつきの、比のようなものを計算しています。すると、「F統計量が大きければ大きいほど、仮説として設定した説明変数の説明力が高い」という直感的な理解が可能で、かつそれがF分布に従うことが知られているので、「帰無仮説の下で、説明変数の説明力がこれだけ高くなるのは、どれぐらい稀なことなのか」を議論することが可能になっているわけですね。
 
 

P値は上手くできた「統一評価指標」

 つまり統計的仮説検定のロジックはある意味、データに向き合う時の、統一評価指標みたいなものを提供してくれているということだと思います。かなり多くの種類のデータを統一的に評価することを可能にする指標として、F値とかt値とかカイ二乗値とかが発見されており、さらにもっと統一的な評価・判断の基準として、p値というものを考えることもできる。
 ほんとに勉強し始めの頃は、そういうもんだという理解すらできていなかったのですが、なんとなくそういう議論の立て付けになっていることが理解できてくると、「こんな統計量を発見した人たちはスゲーわ」と感心するとともに、「統一的な指標の下での議論」をするための洗練されたロジックであるように感じました。
 
 
 もちろんこれらの統計量も、簡単なものかというとそうではなくて、たとえば上述のような議論をするにあたっては色々な前提条件が置かれているので(◯◯は分散の等しい正規分布に従うとする、みたいことが教科書に必ず書かれてある)、それを理解してないと大きな間違いをおかしかねない。「よくわからないけど統計ソフトが出してくれるp値が0.05未満なら、意味ある情報として論文に書いていいらしい」みたいなのはもちろん論外でしょう。
 
 
 しかし「p値にこだわること」自体について言うと、その意味をちゃんと理解している限りは、「メチャメチャこだわったって別にいい」んだと思います。
 私自身は、上述のような統計量に持ち込めることの証明とかをちゃんと読んでいない場合がほとんどなので、「その意味をちゃんと理解している人間」であるかは疑わしいですが。


 「検定」一本槍から「統計モデリング」等へと視野を広げることはとても大事なことなのは確かですし、研究対象、理論的な仮説、データの性質などによって、p値をみても意味がないようなケースは多々あると思います。しかしそれは、p値が劣った指標であることを意味するわけではなく、理解せずに使うと無意味になるというのは他の指標でも同様だと思います。
 「p値偏重」が問題視される背景は何となく分かるのですが、p値に基づく検定のロジックが「割とスゲー」もんであるということは、忘れないようにしたいところですね。いわば、「p < .05」を無駄に追い求める人が発生するのは、p値が非常にうまくできた指標であることの裏返しである、というぐらいに思っといたほうが良いんでしょう。
 
 

「5%基準」の歴史的由来

 ところで、「p < .05」を統計的有意性の基準にするという習慣が問題視されているわけですが、そもそもなんで5%が基準になったんでしょうか。
 帰無仮説が正しいときにそれを棄却してしまう(第一種の過誤と呼ばれる)危険率が5%未満であれば、「統計的に有意」とか言われるわけですが、この5%という閾値に研究上の必然性がないことは誰でも分かります。しかし実際には様々な分野で、5%基準で検定結果を報告(あわせて1%基準や10%基準での有意性も報告されたりはする)している研究が多数存在していると思います。


 この5%という基準の由来について、フィッシャーが「20年に1回ぐらいは間違っても研究者として許されるだろ」と発言した*2のが始まりであるという説をよく聞きますが、これは都市伝説のようです。


 5%基準の由来を調べた論文を以前読みました。
 On the Origins of the .05 Level of Statistical Significance


 アブストラクトを適当に訳しておくと、

フィッシャーの『Statistical Methods for Research Workers』よりも昔の、統計や確率に関する文献を調査すると、確かに統計的有意性に関する"5%"基準を正式に唱えたのはフィッシャーが最初であることは確かなのだが、この考え方自体はもっと昔に遡ることが分かる。
偶然性の仮説を棄却するための習慣的な基準は、世紀の変わり目ぐらいから徐々に形成されていった。統計的有意性に関する初期の言及は、「確率誤差」の観点から行われている。これら初期の習慣が、フィッシャーによって採用され言及されたのである。


 この論文によると、昔は「確率誤差」(ここに解説があった。)3つ分という基準がよく使われていたらしく、これは標準偏差2つ分に相当し、だいたい95%ぐらいになる。これが「5%基準」の由来のようです*3
 
 

参考

 「p値叩き」を逆に問題視する人もいるようです。

d.hatena.ne.jp

*1:従うための仮定がまたあるわけですが。

*2:フィッシャーは植物学者なので、実験みたいなのは1年に1回ペースだったから、20回に1回ぐらい間違った結論を出してもいい=20年に1回は間違ってもいい、ということらしい。

*3:これで「5%」が基準にされていることの理由が説明できたことになるのかというと、微妙だけど

ネットワーク分析ライブラリiGraphをPythonから使うための準備(Macの場合)

ネットワーク分析のツール

 ネットワーク分析ってありますよね。
 表面的な理解としては、こんな風情の図を描いたりするやつです。


f:id:midnightseminar:20160321030214p:plain


 頂点(node, vertex)と、頂点を結ぶ線(link, edge)の集まりとしての「グラフ」の性質を記述するグラフ理論というものがあって、これに基づいてたとえばソーシャル・ネットワーク上の人間関係とかが分析されたりするわけです。
 ネットワーク分析のツールとしては、


igraph + Pythonによるネットワーク分析 ~ Blog of an immature researcher
Python/NetworkXで簡単ネットワーク分析 - あんちべ!
2章グラフ理論スピード入門


 このあたりの記事を拝読すると、RやPythonのユーザとしては、iGraphってやつとNetworkXってやつを知っておけばよさそうです。
 
 
 iGraphは(たぶん)Rのものが有名で、↑の図もRのigraphパッケージで適当に描画したものです。
 一応どういうもんなのかイメージするために書いておくと、こういうネットワーク分析には専用のファイル形式もあるようですが、単純化して言えば全てのノード間の関係を表す総当りの行列か、リンクされてるノードとノードの1つの組み合わせを1行とする行列というかリストみたいなものが元データになります。
 Rでやる場合、たとえば1組1行のリストをデータフレームにしておくと、

> head(d)  # 最初の6行を表示
  from to
1    A  B
2    A  D
3    A  E
4    A  N
5    B  C
6    B  F
> g <- graph.data.frame(d, directed=T)  # グラフ型のデータにする
> plot(g)  # 描画


 みたいに、2行書くだけでさっきのようなグラフが描画されます。A→B、A→Dとなるわけで、分かりやすいですね。


 iGraphはPython版が昔はなかったみたいですが(?)、今はPythonから使えるインターフェイスが提供されています。今回はとりあえずNetworkXは置いといて、iGraphをPythonから使うべくライブラリをインストールしてみました。
 RのigraphパッケージはCRANからインストールすればいいので何も悩むことはないんですが、Python版では何回か失敗したので、インストールの過程をメモしておきます。OSはMacのEl Capitanで、$で始まるのはターミナルから打ってるbashのコマンドです。
 
 

落とし穴

 まず大前提として、いきなり

$ pip install igraph


 で行けるかな?みたいなテキトーなことをやると、落とし穴にハマります(ハマりました)。
 インストールには成功するので「よっしゃ」とか思ってしまうのですが、これでインストールされるのはpipに存在している全然別のパッケージですw
 まぎらわしいのでjgraphという名前でインポートしてもらうようにしました、みたいな注意書きが出ますが。


 ネットワーク分析で使うiGraphを使うのに必要なライブラリは、"igraph"ではなくて、"python-igraph"です。


 なお、結論から言えばpython-igraphをpipでインストールすればいいんですが、いきなりこれをやるのもダメです。python-igraphはigraph本体ではなくて、igraphをPythonから動かすためのインターフェイスにすぎないので、igraph本体を先に入れておく必要があります。
 
 

インストール手順

 iGraphの公式サイトにインストールの説明がありますが、

http://igraph.org/python/

Installation on Mac OS X
Instead of letting pip compile the C core for you, you can install Homebrew and the homebrew/science/igraph formula. This will ensure that the C core is found by pip so running pip install python-igraph will compile the Python interface only and link it to the C core.


 と簡単に書いてあるだけで、細かいことは書かれていませんね・・・。


 手順としてはまず、パッケージ管理ソフトのHomebrewでigraph本体をインストールします。
 ちなみに余談ですが、私は今回、自分のMacBookにHomebrewが入ってないことに気づきました。そういえば去年、OS XがEl Capitanになったときに、usr/localのパーミッションがどうのこうのという問題でHomebrewまわりに不具合が起きていて、よく分からなかったのでひとまずアンインストールしたんでした。(Homebrewが入ったままEl Capitanにアップグレードすると何か危険なことが起きるよという脅しみたいな記事を読んだ記憶がある。)
 なのでまずHomebrewのインストールからやり直しだったんですが、ここに書かれてあるとおりやったらいけました。

$ sudo chown $(whoami):admin /usr/local && sudo chown -R $(whoami):admin /usr/local  # パーミッションを与えてる
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"  # homebrewのインストール


 さて次に、Homebrewでigraphをインストールします。

$ brew install homebrew/science/igraph


 "homebrew/science/"ってのは、homebrew-scienceというもの(参考リンク)の管理下に入れるための記述みたいですが、一応試してみたら別にこれを付けずに"brew install igraph"だけでもインストールはできました。


 で、次にpython-igraphをインストールしようとさっそくpipで、

$ pip install python-igraph


 とやってみると、ものすごい分量のログの後に下記のようなエラーメッセージが出ました。
 

    grep: /usr/lib/libiconv.la: No such file or directory
    sed: /usr/lib/libiconv.la: No such file or directory
    libtool: link: `/usr/lib/libiconv.la' is not a valid libtool archive
    make[3]: *** [libigraph.la] Error 1
    make[2]: *** [all] Error 2
    make[1]: *** [all-recursive] Error 1
    make: *** [all] Error 2
    Could not download and compile the C core of igraph.

    ----------------------------------------
Command "/Users/yk/anaconda/bin/python -u -c "import setuptools, tokenize;__file__='/private/var/folders/rw/7zgjy_d15vn9xvn_dbtm2_vc0000gn/T/pip-build-am6dhjuy/python-igraph/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /var/folders/rw/7zgjy_d15vn9xvn_dbtm2_vc0000gn/T/pip-u7joobhl-record/install-record.txt --single-version-externally-managed --compile" failed with error code 1 in /private/var/folders/rw/7zgjy_d15vn9xvn_dbtm2_vc0000gn/T/pip-build-am6dhjuy/python-igraph/

("/Users/yk/anaconda/"ってのは私がそこに入っているPythonを使ってるからそうなっています。)


 このエラーについては、ここに書かれてあるのと同じで、この質問者が自己解決した方法でいけました。先にhomebrewで"pkg-config"ってのを入れとけってことのようです。
 つまり、

$ brew install homebrew/science/igraph  # まずigraphをインストール


 をやった後に(べつにその前でもいいが)、

$ brew install pkg-config  # これを先に入れないと正常にインストールされない


 とした上で、

$ pip install python-igraph


 とすれば成功しました。
 
 

描画用のライブラリ

 さて、インストールできたのでさっそくテストに使ってみようと思いましたが、準備が必要です。
 公式サイトの説明によると、iGraphでのグラフの描画用の関数plotを使うためには、pycairoっていうライブラリを入れておかなければならないようです。
 ちなみにこのpycairoってのも、cairo(カイロ)というC言語のライブラリをPythonから操作するためのラッパーみたいなやつなので、pycairoの前にまずはcairo本体をインストールしないといけないわけですが、これもhomebrewでいけました。


 ここの説明をみてやりましたが、
 

$ brew install cairo --use-clang


 これでcairoはインストールは完了。
 次にこれをPythonから使えるようにするため、pycairoをインストールしますが、これは若干面倒です。
 まず、

$ cd ~/Dropbox/Python/MBA2012/iGraph/  # 私はここで作業するというだけの話
$ git clone git://git.cairographics.org/git/pycairo
$ cd pycairo


 このように、作業用に適当なディレクトリに移動してから、gitコマンドでソースをダウンロードして、自動的に展開されるディレクトリに入ります。
 ここから先のやり方は、このダウンロードしてきたファイル一式の中に入っている、"INSTALL"っていうテキストファイルを開くと書いてあるのですが、

$ ./waf configure
$ ./waf build
$ ./waf install


 というふうにやればインストール完了です。
 
 
 ・・・しかしこのやり方だと、私の場合は困ったことになりました。インストール自体は正常に完了してるのですが、インストール場所が/usr/local/配下となっています。私はAnacondaからPythonを使っているのですが、Anaconda配下のPythonからはそのままでは呼べないみたいです。インストーラの中身をいじってインストール場所を変えるというのは私の知識でやるのは危険そうだし、Anaconda配下のPythonから呼ぶためのパスの設定もよく分かりません。


 それで面倒だな〜と思ってググってみたら、なんとそもそも、Anacondaが正式にpycairoを配布してくれていて、condaコマンドでインストールできることが判明w


 Pycairo :: Anaconda Cloud


 つまり、

conda install -c https://conda.anaconda.org/vgauthier pycairo


 この一撃で終了です。
 cairoも一緒にインストールされるので、上述のようなHomebrewでのcairoのインストールも不要です。あくまでAnacondaユーザの場合の話ではありますが。
 
 

テスト

 最後に動作確認しておきます。
 Pythonで以下のようなコードを実行します。

from igraph import *
vertices = ["Yamada", "Tanaka", "Suzuki", "Sato", "Ito", "Obokata"]
edges = [(0,1),(1,3),(1,4),(1,5),(2,5),(3,5),(4,3),(0,5)]
g = Graph(vertex_attrs={"label": vertices}, edges=edges, directed=True)
plot(g)


 verticesはノード名のリストで、edgesはエッジのリストです。エッジは、verticesの要素のインデックスを使って、要素0と要素1、要素1と要素3……がつながっていることを表しています。
 これらを用いてGraphというクラスのインスタンスを生成してgという変数に入れています。directedのところがTrueになっていると、「有向」グラフになります。


 これをplot関数にわたすと、めでたく以下のような図が描画されます。


f:id:midnightseminar:20160321031352p:plain 
 
 
 しかしきちんと調整しないと、なんとなく図がダサいですね。
 とりあえず動作確認までできたので、寝ます。


 最近、「インストールしてみました」みたいな記事しか書いてないですが(私の知識では他に意味のある記事が書けるわけでもないですが)、後にインストールを試みる人の検索に引っかかるかもという意味で、こういうつまらない記事の蓄積はとても大事だと思っております。
 だいたい、インストール時にエラーが出て腹が立つ現象は、だれかのブログ記事かStackoverflowによって解決されることが多いし(プログラマの人とかは根本原因を自分でつぶせるんでしょうけど)。エラーのログを掲載しておくことも大事ですね。私も実際、エラーメッセージでググッて解決方法を見つけましたし。

日本語と英語の、難易度が高い形態素解析の例

 
 小ネタです。
 

すもももももももものうち

 昨日、日本語形態素解析エンジンMeCabに関するエントリを書きました。


statsbeginner.hatenablog.com


 ところで、MeCabの公式サイト(リンク)にいくと、インストール完了後のテストとして「すもももももももものうち」の解析が行われています。"Hello world!"のノリで。

$ mecab
すもももももももものうち
すもも 名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
うち 名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS


 これを受けて、MeCabの使い方を解説するブログなんかでも、よく「すもももももももものうち」が例文として使われていますね。
 
 
 ひらがなの「も」がこれだけ連続していても正確に解析できるのは凄いですね。
 MeCabは教師あり学習のモデルになっているらしいので、教師データにこの例文が含まれてるのかもしれませんが、公式サイトの例文に使われるぐらいだからそんなオチではないと信じます。
 
 

英語の例

 それで、似たような例文がもっとないだろうかと考えてみたのですが、思いついたのは昔、スティーブン・ピンカーという心理言語学者の本を読んでたら出てきた、

Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo.


 という英文でしたw
 Buffaloには、ニューヨーク州にある市の名前のBuffalo、動物の種類のbuffalo、「怖がらせる」という意味の動詞のbuffaloという3つの意味があって、上の文は
 
 
 市・動物・市・動物・怖がらせる・怖がらせる・市・動物
 
 
 の順番に並んでいます。文全体としてのメインの動詞は後ろから3語目のbuffaloで、2語目と3語目の間に関係代名詞が省略されています。関係代名詞を補いつつ階層構造をカッコでくくって表すと、


 {(Buffalo buffalo) who (Buffalo buffalo) buffalo} buffalo (Buffalo buffalo).
 
 
 みたいな感じでしょうか。訳すとすれば、「バッファロー市のバッファローが怖がらせるバッファロー市のバッファローが、バッファロー市のバッファローを怖がらせる」となります。
 
 
 MeCabは日本語形態素解析のソフトなので、英語のソフトはどうなってるんだろうかとググってみたら、TreeTaggerというのが有名だそうで、そのWeb版がありました。品詞を判定するというやつです。


TreeTagger Online

 
 ここに"Buffalo"の例文を入れてみたところ・・・

Buffalo NP Buffalo
buffalo NN buffalo
Buffalo NP Buffalo
buffalo NN buffalo
buffalo NNS buffalo
buffalo VVP buffalo
Buffalo NP Buffalo
buffalo NN buffalo


 ダメだったようですw
 NPは固有名詞、NNは名詞の単数形、NNSは名詞の複数形、VVPは他動詞です(意味はこの記事に一覧が載っている)。つまり、5語目がVVPになってないとダメなんですよね。
 
 
 このBuffaloの例文はWikipediaの記事にもなっていて(リンク)、そこにもう1個、興味深い例文が載っていました。
 

"Don't trouble trouble until trouble troubles you"(訳:迷惑に迷惑するまで迷惑を迷惑がるな。つまり「取り越し苦労はするな」ということ)


 これをさっきのTreeTagger Onlineにかけてみます。

Do VV do
n't RB n't
trouble NN trouble
trouble NN trouble
until IN until
trouble NN trouble
troubles NNS trouble
you PP you
. SENT .


 うーんこれもダメですね。INは前置詞、PPは人称代名詞です。
 ついでに、チョムスキーの有名な"Colorless green ideas sleep furiously."(無色の緑色の考えが猛烈に眠る。)も解析してみます。これは、文法的には正しいけど意味をなさない文としてチョムスキーが例示したものです。

Colorless JJ colorless
green JJ green
ideas NNS idea
sleep VVP sleep
furiously RB furiously
. SENT .


 これは余裕でいけました。JJは形容詞、RBは副詞です。
 
 

日本語の他の例文

 日本語の例文を探していたら、Yahoo!知恵袋にこんな記事がありました。


detail.chiebukuro.yahoo.co.jp


 ひらがなで与える必然性がない文も多く、解析できなくても仕方ないという気がしますが。

はははははじょうぶだ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
じょうぶ 名詞,形容動詞語幹,*,*,*,*,じょうぶ,ジョウブ,ジョーブ
だ 助動詞,*,*,*,特殊・ダ,基本形,だ,ダ,ダ
EOS


 ダメでした。

$ mecab
ぶたがぶたをぶったので、ぶたれたぶたがぶったぶたをぶった。
ぶた 名詞,一般,*,*,*,*,ぶた,ブタ,ブタ
が 助詞,格助詞,一般,*,*,*,が,ガ,ガ
ぶた 名詞,一般,*,*,*,*,ぶた,ブタ,ブタ
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
ぶっ 動詞,自立,*,*,五段・タ行,連用タ接続,ぶつ,ブッ,ブッ
た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
ので 助詞,接続助詞,*,*,*,*,ので,ノデ,ノデ
、 記号,読点,*,*,*,*,、,、,、
ぶた 動詞,自立,*,*,五段・タ行,未然形,ぶつ,ブタ,ブタ
れ 動詞,接尾,*,*,一段,連用形,れる,レ,レ
た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
ぶた 名詞,一般,*,*,*,*,ぶた,ブタ,ブタ
が 助詞,格助詞,一般,*,*,*,が,ガ,ガ
ぶっ 動詞,自立,*,*,五段・ラ行,連用タ接続,ぶる,ブッ,ブッ
た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
ぶた 名詞,一般,*,*,*,*,ぶた,ブタ,ブタ
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
ぶっ 動詞,自立,*,*,五段・タ行,連用タ接続,ぶつ,ブッ,ブッ
た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
。 記号,句点,*,*,*,*,。,。,。
EOS


 これはいけました。しかしこの文は動詞が活用してることもあり、そんなに難易度高くない気もしますね。


 他に何か面白い例文ないかな。
 なお、MeCabの性能は、Web版形態素解析ツールの「Web茶まめ」(リンク)でも簡単に試せます。

形態素解析エンジンMeCabをPython3でも使えるようにする(Macの場合)

MeCabのPythonバインディングはPython3で使えない?

 日本語の文章を解析する際には欠かせない、形態素解析エンジン"MeCab"の導入に関するエントリを以前書きました(過去エントリ)。
 MeCabの公式サイトにいくとPythonバインディングというのが配布されていて、Python上でMeCabを使うことができるのですが、これだとPython2系でしか使えません。
 もともとこのPythonバインディングは、MeCabのプログラムからSWIGというツールを用いて自動生成したものらしく、この生成をやり直せばPython3系でも使うことができるようになります。


 [追記]じつは、コメント欄で指摘を頂き、pipでmecab-python3というのが配布されていることが分かったので、公式サイトに置いてあるやつを使わずに、後述のとおりpipからインストールすれば全て解決しますw[/追記]
 ここでは一応、SWIGを(中途半端に)使う方法を試してみたので、以下その手順をメモしておきます。また、MeCab本体と辞書のインストールについてもメモしておくので、このエントリをみるだけでMeCabの導入ができるようにもなっています


 MeCabそのもののインストールができている場合は、↓この辺のページを参考にすれば、SWIGを使った方法が実行できると思います。


 SWIGのインストール方法の参考
 Install Swig for Mac OS X | Professional Programmer
 Python3バインディングの生成方法の参考
 Ubuntu14.04とPython3でMeCabを使う方法 | トライフィールズ



 後者はUbuntu用の説明なんですが、詳しく説明してあって、これを少し改変することでMacでもできました。


 以下では一応、MeCabのインストールから全部順番にやっていくことにします。
 なお私はMacで使っていて、OSはEl Capitan、Pythonのバージョンは3.4.3です。
 
 
 MeCab本体、解析に使うIPA辞書とUniDic辞書、SWIGを使うのに必要なPCRE、そしてSWIGをインストールし、MeCabが公式に配布しているPython(2)バインディングをダウンロードしてきて、MeCab本体とSWIGで新たに生成するプログラムを挿入することで、Python3バインディングを作成するという手順です。
 辞書は、公式にはIPA辞書が推奨されているらしいですが、いま友人と読書会をやっている『言語研究のためのプログラミング入門』という教科書ではUniDicが使われていたので、一応UniDicも試しています。


言語研究のためのプログラミング入門: Pythonを活用したテキスト処理

言語研究のためのプログラミング入門: Pythonを活用したテキスト処理


 今回の本題と直接は関係ないですが、この教科書は、初歩中の初歩しかやらないものの、プログラミング自体まったくやったことがないという人(まぁ私もそれに近いですが)が、テキストデータの処理を通じてPythonに入門するというのにはいい内容だと思います。章末問題の回答が載ってないのが、ホントに初めてPythonを触るという人にとってはきついと思いますが。
 
 

MeCab本体と辞書の導入

 さて、今回は基本的に、全てターミナルからコマンドラインで操作していきます。
 ファイルをダウンロードしてきて解凍するところ(curlコマンドとtar、unzipコマンドのところ)は、ブラウザでアクセスしてダウンロードしてきて、マウスで適切な場所に移動し、ダブルクリックで解凍してももちろん良いです。その方が早いかもしれんw


 まず、MeCab用のディレクトリを作ってそこにカレントディレクトリを移動しておきます。
 私は全てDropbox同期フォルダ内でやるという、危険なことをやってますが、今のところ特に問題はおきてないです。複数台のMacから使っているので、何か不整合とか起きてもおかしくなさそうですがw

$ cd ~/Dropbox/Python/  # Pythonフォルダに移動
$ mkdir MeCab           # MeCabというフォルダを作成
$ cd MeCab              # そこに入る


 MeCab本体を、ダウンロードしてインストールします。MeCab本体についてもUTF-8の設定をしている記事をみたので、念のため私もそうしています。

$ curl -O https://mecab.googlecode.com/files/mecab-0.996.tar.gz
$ tar zxfv mecab-0.996.tar.gz       # 解凍
$ cd mecab-0.996                    # できたディレクトリに入る
$ ./configure --with-charset=utf8   # UTF-8前提で設定
$ make               # コンパイル
$ sudo make install  # インストール
$ cd ..         # 元のディレクトリに移動


 IPA辞書をダウンロードしてインストールします。

$ curl -O https://mecab.googlecode.com/files/mecab-ipadic-2.7.0-20070801.tar.gz
$ tar zxfv mecab-ipadic-2.7.0-20070801.tar.gz
$ cd mecab-ipadic-2.7.0-20070801
$ ./configure --with-charset=utf8   # UTF-8前提で設定
$ make
$ sudo make install
$ cd ..


 ついでにUniDicもインストールします。前述のとおり、勉強会で使っている教科書はこの辞書でやってたので。
 ちなみにUniDicはファイルのサイズがけっこうデカいので注意。(解凍前140MBくらいで解凍すると500MB以上になったw)

$ curl -O http://iij.dl.osdn.jp/unidic/58338/unidic-mecab-2.1.2_src.zip
$ unzip unidic-mecab-2.1.2_src.zip
$ cd unidic-mecab-2.1.2_src
$ ./configure  # UniDicはもともとUTF-8なので指定不要
$ make
$ sudo make install
$ cd ..


 ちなみにもうこの時点で、MeCabそのものは使えるようになっています。
 ターミナル上で試してみます。

$ mecab
剣道は剣の理法の修練による人間形成の道である
剣道	名詞,一般,*,*,*,*,剣道,ケンドウ,ケンドー
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
剣	名詞,一般,*,*,*,*,剣,ケン,ケン
の	助詞,連体化,*,*,*,*,の,ノ,ノ
理法	名詞,一般,*,*,*,*,理法,リホウ,リホー
の	助詞,連体化,*,*,*,*,の,ノ,ノ
修練	名詞,サ変接続,*,*,*,*,修練,シュウレン,シューレン
による	助詞,格助詞,連語,*,*,*,による,ニヨル,ニヨル
人間	名詞,一般,*,*,*,*,人間,ニンゲン,ニンゲン
形成	名詞,サ変接続,*,*,*,*,形成,ケイセイ,ケイセイ
の	助詞,連体化,*,*,*,*,の,ノ,ノ
道	名詞,一般,*,*,*,*,道,ミチ,ミチ
で	助動詞,*,*,*,特殊・ダ,連用形,だ,デ,デ
ある	助動詞,*,*,*,五段・ラ行アル,基本形,ある,アル,アル
EOS


 きちんと分解出来ましたね。
 辞書にUniDicを使うなら、たとえば以下のようにします。

$ mecab -d 辞書を置いてあるディレクトリ/unidic-mecab-2.1.2_src
剣道は剣の理法の修練による人間形成の道である
剣道	ケンドー	ケンドウ	剣道	名詞-普通名詞-一般		
は	ワ	ハ	は	助詞-係助詞		
剣	ケン	ケン	剣	名詞-普通名詞-一般		
の	ノ	ノ	の	助詞-格助詞		
理法	リホー	リホウ	理法	名詞-普通名詞-一般		
の	ノ	ノ	の	助詞-格助詞		
修練	シューレン	シュウレン	修練	名詞-普通名詞-サ変可能		
に	ニ	ニ	に	助詞-格助詞		
よる	ヨル	ヨル	因る	動詞-一般	五段-ラ行	連体形-一般
人間	ニンゲン	ニンゲン	人間	名詞-普通名詞-一般		
形成	ケーセー	ケイセイ	形成	名詞-普通名詞-サ変可能		
の	ノ	ノ	の	助詞-格助詞		
道	ミチ	ミチ	道	名詞-普通名詞-一般		
で	デ	ダ	だ	助動詞	助動詞-ダ	連用形-一般
ある	アル	アル	有る	動詞-非自立可能	五段-ラ行	終止形-一般
EOS


 UniDicを上記のようにオプションで指定して使う方法以外に、デフォルトの辞書をIPAからUniDicに変更してしまうことも可能です。
 デフォルトを変更するには、"/usr/local/etc/"というディレクトリに入っている"mecabrc"というファイルの中の、

dicdir =  /usr/local/lib/mecab/dic/ipadic


 となっている箇所が辞書の在り処を参照しているので、これを

dicdir =  /usr/local/lib/mecab/dic/unidic


 に書き換えればOKなのですが、特権が必要なのでここではvimで編集します。

$ sudo vi /usr/local/etc/mecabrc 


 と打ってvimからこのファイルを開き、あとはvimの操作方法なのでググると色々でてきますが、たとえばaキーで入力モードに切り替えて、ターミナル上で該当箇所を書き換え、escキーでコマンドモードに戻ってZZで保存終了すればいいです。
 
 

Python3バインディングの生成

 さて、Pythonバインディングの生成をやっていきます。
 SWIGをインストールするためには前提としてPCREというツールが必要らしいので、PCREをインストールした後にSWIGをインストールします。
 (なお、PCREを入れるためのさらなる前提としてXcodeとCommand Line Toolsが必要なので、入れてなかった場合は、Apple のAppStoreからXcodeを無料インストールした後、Command Line Toolsをインストールしておきます。)


 まずPCREのダウンロード、コンパイル、インストール。

$ curl -O http://jaist.dl.sourceforge.net/project/pcre/pcre/8.38/pcre-8.38.tar.bz2
$ tar zxfv pcre-8.38.tar.bz2
$ cd pcre-8.38
$ ./configure
$ make
$ sudo make install
$ cd ..


 次にSWIGのダウンロード、コンパイル、インストール。

$ curl -O http://jaist.dl.sourceforge.net/project/swig/swig/swig-3.0.8/swig-3.0.8.tar.gz
$ tar zxfv swig-3.0.8.tar.gz
$ cd swig-3.0.8
$ ./configure
$ make
$ sudo make install
$ cd ..


 これでSWIGが導入できたので、いよいよPythonバインディングの生成を行います。
 まずmecab-pythonをダウンロードしてきて解凍します。

$ curl -O https://mecab.googlecode.com/files/mecab-python-0.996.tar.gz
$ tar zxfv mecab-python-0.996.tar.gz


 SWIGを動かします。以下のようにwarningが一杯出ましたが、無視しても大丈夫でした。

$ swig -python -shadow -c++ mecab-0.996/swig/MeCab.i
mecab-0.996/swig/../src/mecab.h:136: Warning 302: Identifier 'surface' redefined by %extend (ignored),
mecab-0.996/swig/MeCab.i:74: Warning 302: %extend definition of 'surface'.
mecab-0.996/swig/../src/mecab.h:848: Warning 302: Identifier 'set_sentence' redefined by %extend (ignored),
mecab-0.996/swig/MeCab.i:95: Warning 302: %extend definition of 'set_sentence'.


 上の処理によって"mecab-0.996/swig/"というディレクトリにできたファイルがあるので、これを"mecab-python-0.996"ディレクトリ内に移動して(同じ名前のファイルがあるので)上書きします。要は、落としてきたmecab-python-0.996は中身が古いので、必要なところを、MeCab本体とSWIGによって今回生成したファイルに置き換えるってことでしょう。

$ mv mecab-0.996/swig/MeCab.py mecab-python-0.996
$ mv mecab-0.996/swig/MeCab_wrap.cxx mecab-python-0.996


 次に、"mecab-python-0.996"ディレクトリ内の"setup.py"というPythonスクリプトを、一箇所だけ修正します。
 具体的には、

def cmd2(str):
    return string.split (cmd1(str))


 という箇所を、

def cmd2(str):
    return cmd1(str).split()


 に書き換えます。
 スクリプトをテキストエディタで開いてもいいし、さっきみたいにvimから編集しても、どっちでもいいと思います。


 この編集が終わったら、Pythonバインディングのインストールを行います。

$ cd mecab-python-0.996
$ python3 setup.py build
$ sudo python3 setup.py install


 自分の環境で、pythonと打ったときにPython2ではなくPython3が起動するように既になっているのであれば、上記のpython3のところはpythonでも良いはずです。
 
 

使ってみる

 これでPython3バインディングの導入は完了したので、試しに分析してみます。ターミナルからPythonを立ち上げて、PythonからMeCabを使ってみます。

python
>>> import MeCab
>>> m = MeCab.Tagger('')
>>> text = '有効打突は、充実した気勢、適正な姿勢をもって、竹刀の打突部で打突部位を刃筋正しく打突し、残心あるものとする。'
>>> text_parsed = m.parse(text)
>>> print(text_parsed)
有効	名詞,形容動詞語幹,*,*,*,*,有効,ユウコウ,ユーコー
打	名詞,接尾,一般,*,*,*,打,ダ,ダ
突	名詞,一般,*,*,*,*,*
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
、	記号,読点,*,*,*,*,、,、,、
充実	名詞,サ変接続,*,*,*,*,充実,ジュウジツ,ジュージツ
し	動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
た	助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
気勢	名詞,一般,*,*,*,*,気勢,キセイ,キセイ
、	記号,読点,*,*,*,*,、,、,、
適正	名詞,形容動詞語幹,*,*,*,*,適正,テキセイ,テキセイ
な	助動詞,*,*,*,特殊・ダ,体言接続,だ,ナ,ナ
姿勢	名詞,一般,*,*,*,*,姿勢,シセイ,シセイ
を	助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
もっ	動詞,自立,*,*,五段・タ行,連用タ接続,もつ,モッ,モッ
て	助詞,接続助詞,*,*,*,*,て,テ,テ
、	記号,読点,*,*,*,*,、,、,、
竹刀	名詞,一般,*,*,*,*,竹刀,シナイ,シナイ
の	助詞,連体化,*,*,*,*,の,ノ,ノ
打	接頭詞,名詞接続,*,*,*,*,打,ダ,ダ
突部	名詞,一般,*,*,*,*,*
で	助詞,格助詞,一般,*,*,*,で,デ,デ
打	接頭詞,名詞接続,*,*,*,*,打,ダ,ダ
突部	名詞,一般,*,*,*,*,*
位	名詞,接尾,一般,*,*,*,位,イ,イ
を	助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
刃	名詞,一般,*,*,*,*,刃,ハ,ハ
筋	名詞,接尾,一般,*,*,*,筋,スジ,スジ
正しく	形容詞,自立,*,*,形容詞・イ段,連用テ接続,正しい,タダシク,タダシク
打	接頭詞,名詞接続,*,*,*,*,打,ダ,ダ
突	名詞,一般,*,*,*,*,*
し	動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
、	記号,読点,*,*,*,*,、,、,、
残	名詞,一般,*,*,*,*,残,ザン,ザン
心	名詞,接尾,一般,*,*,*,心,シン,シン
ある	動詞,自立,*,*,五段・ラ行,基本形,ある,アル,アル
もの	名詞,非自立,一般,*,*,*,もの,モノ,モノ
と	助詞,格助詞,一般,*,*,*,と,ト,ト
する	動詞,自立,*,*,サ変・スル,基本形,する,スル,スル
。	記号,句点,*,*,*,*,。,。,。
EOS


 「打突」「刃筋」「残心」といった剣道用語が学習されていないようで、少し変になっていますね。
 mという変数名で、MeCab.Taggerクラスのインスタンスを生成し、.parseメソッドの引数に解析したい日本語文を与えています。
 上のような表形式の解析結果が欲しいというよりも、日本語の文章を単純に分かち書きして単語の登場頻度などを分析したいということも多いと思うので、その場合は以下のようにします。(長文を与えるならファイルで与えたほうがいいと思うけど。)

>>> m = MeCab.Tagger('-Owakati')
>>> text = '「剣道修錬の心構え」剣道を正しく真剣に学び、心身を錬磨して旺盛なる気力を養い、剣道の特性を通じて礼節をとうとび、信義を重んじ誠を尽して、常に自己の修養に努め、以って国家社会を愛して、広く人類の平和繁栄に、寄与せんとするものである。'
>>> text_parsed = m.parse(text)
>>> print(text_parsed)
「 剣道 修錬 の 心構え 」 剣道 を 正しく 真剣 に 学び 、 心身 を 錬磨 し て 旺盛 なる 気力 を 養い 、 剣道 の 特性 を通じて 礼節 を とうとび 、 信義 を 重んじ 誠 を 尽し て 、 常に 自己 の 修養 に 努め 、 以 って 国家 社会 を 愛し て 、 広く 人類 の 平和 繁栄 に 、 寄与 せ ん と する もの で ある 。 


 よし、これで無事、Python3でMeCabが使えるようになりました。
 認識されない単語があった場合は、これなどを参考に自分でユーザ辞書に登録したほうがいいかもですね。 
 

メモ

冒頭で紹介したUbuntu向けの解説記事がとても親切ですが、そのやり方との違いは以下のような点です。

  • インストールのコマンドが違う。
  • libmecab-devは別個にインストールする必要はなかった。
  • 一時的な作業フォルダを設ける必要はとくにない。
  • mecab-pythonのアンインストール時に削除するファイルの場所が、私の場合は(Anacondaを使ってPythonをインストールしてるので)anaconda配下にあって、以下のようなコマンドになる
$ sudo rm ~/anaconda/lib/python3.4/site-packages/MeCab.py 
$ sudo rm ~/anaconda/lib/python3.4/site-packages/_MeCab.so
$ sudo rm ~/anaconda/lib/python3.4/site-packages/mecab_python-0.996-py3.4.egg-info
$ sudo rm /Users/yk/anaconda/lib/python3.4/site-packages/__pycache__/MeCab.cpython-34.pyc 

 
 

追記:pipにmecab-python3ってあるやん!

 コメント欄で指摘を頂いて知ったのですが、pipにmecab-python3ってのがあったらしいです……。
 なので、これをインストールすれば、上述のようなややこしいことはせずにすみます。

$ pip install mecab-python3


 これでPythonバインディングのインストールは終了ですw

Rメモ: 分散分析で交互作用を可視化するときのあのグラフを、Rで描きたい

交互作用の可視化

 心理学(に限らないが)で分散分析を行う場合に、交互作用を可視化することなどを目的として、折れ線のグラフが作られることがありますよね。
 主に2要因の場合で、水準数もさほど多くないときに、第1の要因をX軸に、第2の要因は線の種類でかき分けて、Y軸に各群の従属変数の平均値を取る。

 こんな感じで。


http://kogolab.chillout.jp/elearn/hamburger/chap7/fig_kougo_ari.GIF
(出典:7.3 交互作用とは?より。)


 線の傾きが違えば、交互作用があるってことです。
 これ非常によく見かける図ですが、Rでどうやって作図すれば良いか、パッとはわからなかった。
 何か良いパッケージがあるのかもしれないけど調べるのもめんどうなので、以下のようにx軸に配置したい要因の各水準にダミーで数字を当ててムリヤリ折れ線グラフとして描いてみた。

作図

 たとえば英語の点数(平均)が、


f:id:midnightseminar:20160106042140p:plain


 というような結果になっているとする。
 男性より女性のほうが成績がよく、かつ理系より文系のほうが成績が良い。
 各セルにはサンプルが何十人かいてその平均値が上記の通りとなっており、その平均値が男女間、文系理系間で有意に異なるのかどうかを、2要因の分散分析によって検定する。
 で、その前に、まず平均値を表すグラフを描いておきたいと思ったとする。


 以下のように書くと、組み込みの関数でそれっぽいグラフが描けました。
 x軸をいったん消しといて、axisで上書きするところがポイント。(あまり調べてないので、もっと良いやり方があるのかも知れない。)

Male <- c(81.3, 85.5)
Female <- c(83.7, 91.8)

# 男性のほうのグラフを描く
plot(x=c(1,2),           # ダミーで数字を入れている
     y=Male,  
     type="b",           # 点と線をプロット
     lty=1,              # 線を実線に
     lwd=1.5,            # 線の太さ1.5倍
     xlim=c(0.7, 2.3), 
     ylim=c(80, 95), 
     xlab="文理", 
     ylab="点数", 
     xaxt="n",           # x軸を消すという意味
     pch=21,             # 白丸
     cex=1.5             # 丸の大きさ1.5倍
     )

axis(side=1,  # x軸を描くという意味
     at=1:2,  # 目盛り
     labels=c("理系", "文系")  # 目盛ラベル
     )

# 女性のほうのグラフを重ねる

par(new=TRUE)  # 上から重ねるときのパラメータ設定

plot(x=c(1,2),
     y=Female,  
     type="b",
     lty=2,             # 線を破線に
     lwd=1.5, 
     xlim=c(0.7, 2.3), 
     ylim=c(80, 95), 
     xlab="",           # x軸のタイトルなし
     ylab="",           # y軸のタイトルもなし 
     axes=FALSE,        # 軸なしという意味
     pch=21,
     cex=1.5            # 丸の大きさ1.5倍
     )

legend("bottomright",            # 凡例の位置
       legend=c("男性", "女性"),  # 凡例の文字
       lty=c(1, 2),              # 凡例の線の種類
       lwd=c(1.5, 1.5)           # 太さ
       )


f:id:midnightseminar:20160106043103p:plain


 凡例が、この例だと線の描き分けだけ表現すればいいので楽だけど、たとえば黒丸と白丸を使い分けたりして、丸の種類についての凡例も載せなければならなくなったりすると、割とめんどくさい。
 何がめんどくさいって、凡例の位置を座標で指定してりして微妙に調整するのがダルい。たいてい、思った通りのポジションに表れてくれないし。
 凡例が1種類だけでいいなら、この例のように、「右下」みたいな指定をしておけば、ずれたりすることもなく安心。


 話がそれますが、凡例のことを英語でlegendと言い、legendには言うまでもなく「伝説」という意味もあります。なんで凡例と伝説が同じ単語なのかについて、「手本」みたいな意味があるのかと思ってたのですが、語源辞典によればラテン語で「読むべきもの」という意味の言葉からきているようです。
 
 

まんま使える関数が存在した(2016.1.8追記)

 このエントリアップ後、Twitterで教えてもらったのですが、interaction.plot()という、そのまんま上記目的のための組み込みの*1関数があったようです。
 以下のように使います。
 

# テスト用サンプルデータを乱数で生成

set.seed(1)

Score <- c(
	rnorm(100, 81.3),  # 男性・理系
	rnorm(100, 85.5),  # 男性・文系
	rnorm(100, 83.7),  # 女性・理系
	rnorm(100, 91.8)  # 女性・文系
	)


# 因子をつくっておく

Sex <- c(
	rep("Male", 200), 
	rep("Female", 200)
	)

Class <- c(
	rep("Rikei", 100), 
	rep("Bunkei", 100), 
	rep("Rikei", 100), 
	rep("Bunkei", 100)
	)


# データフレームにまとめる
# 理系→文系の順に並べたいので水準ベクトルの順番を自分で指定している

df <- data.frame(
	ID=1:400, 
	Sex, 
	Class=factor(as.factor(Class), levels=c("Rikei", "Bunkei")), 
	Score)


# 作図する

quartz(width=8, height=6)    # 作図デバイスのサイズ指定(Macの場合)
par(mai=c(1, 1, 0.7, 0.7))   # 余白の指定

interaction.plot(
	x.factor=df$Class,       # x軸の変数
	trace.factor=df$Sex,     # 線で描き分ける変数
	response=df$Score,       # y軸の変数
	fun=mean,                # 平均値を図示するならmeanを指定
	type="b",                # 点と線で表すグラフを指定
	legend=TRUE,             # 凡例あり
	ylim=c(80, 94),          # y軸の範囲指定
	xlab = "Class",          # x軸タイトル
	ylab = "mean of Score",  # y軸タイトル
	trace.label = "Sex",     # 線で描き分ける変数タイトル(凡例に出る)
	pch=c(19, 21),           # 丸の記号の種類
	cex.lab=1.2              # 軸タイトルの文字サイズ
	)

 
 こんな図が出来ます。
 
f:id:midnightseminar:20160109091748p:plain
 
 めちゃめちゃ綺麗やん!!!!!!!
 4つの平均値しか情報がないグラフなので、文字を大きくしてグラフ自体はもっと小さくして簡単な感じにした方がいいけど。


 ついでに、グラフが交差するパターンも描いておいた。乱数のところを、

set.seed(1)
Score <- c(
	rnorm(100, 82.9),  # 男性・理系
	rnorm(100, 87.5),  # 男性・文系
	rnorm(100, 81.1),  # 女性・理系
	rnorm(100, 91.8)  # 女性・文系
	)

 に変更。


f:id:midnightseminar:20160117120302p:plain

*1:標準パッケージの、と言うほうが正確なのかな?