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

初学者が統計学、機械学習、R、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:標準パッケージの、と言うほうが正確なのかな?

作業&勉強メモ: Pythonで指定したディレクトリ配下のディレクトリ名とファイル名を取得する

 ちょっとした作業のメモです。わたし初心者ですのでヘンなことをやってる可能性あります。
 
 
 指定したディレクトリ配下のディレクトリ名やファイル名を取得しようと思い、↓のページを参考に作業しました。


qiita.com


 ただ、以下のような変更を行いました。

  • リストが欲しかったのでリストを返すようにした。
  • 指定したディレクトリそのものは要素として必要ないので、出力しないようにした。
  • オプションで、フルパスを返すか、指定したディレクトリからの相対パスだけ返すか選択できるようにした。
  • オプションで、"."で始まるシステムファイルやシステムフォルダを除くかどうか選択できるようにした。


 os.walk()の公式な説明は、
 16.1. os — 雑多なオペレーティングシステムインタフェース — Python 3.3.6 ドキュメント
 に載っている。

 

留意点

 こんなんでいいんだろうかと自分でも思っている留意点をいくつか挙げておきます。
 
 
 まず、(os.walkが返してくる)ジェネレータとかいうものの操作方法がよくわからなかったので、関数定義の中にまた関数定義を入れているのだが、もっと簡単な方法ないのだろうか。
 ジェネレータについて、以下の記事は斜め読みしてフムフムとおもた。
 Python のジェネレータ (1) - 動作を試す | すぐに忘れる脳みそのためのメモ
 関数として定義されるがオブジェクトのように動くみたいなことが書かれているので、関数定義の中に関数定義でもこれは仕方のないことなのかな?


 相対パスだけ取りたい場合ですが、os.walk()のfilenamesだけ取ると指定したディレクトリ配下のディレクトリ構造が無視されてファイル名だけ返る。どうすればいいかよく分からなかったので、とりあえず、
 yield os.path.join()
でフルパスを得た後で、指定ディレクトリまでのパスをテキストとして削除することにした。下の階層で同じパスが出てこないとも限らないので、左端からみていって1回だけ削除することにしている(re.sub()のcount=1)。
 このやり方だと、ディレクトリを指定するときに最後に"/"を書かない場合、返ってくるファイル名等が"/"で始まるようになってしまうので、最後に.lstrip('/')で取り除いている。

 フルパスについてはos.walk()が返す"dirpath"と"filenames"を連結し、指定ディレクトリからの相対パスはos.path.relpath()で取ることにした。


 システムファイルの指定は'/.'が含まれるという条件でいいのかは自信がない。


 わたしMacなんですが、.appとかはファイルではなくディレクトリとして認識されて中身が全部一覧化されます。オプションで一覧化させないようにするための処理はまた後で付け足したほうがいいのかもしれない。

コード(修正あり)

 以下のようなコードを書きました。コメント欄で戴いた指摘を反映して修正しました。
 なお、Macでやってますが、Windowsでやるときはパスの書き方が違うので修正が必要なので後述します。
 あと、私はPython3でやっています。

def listdir_all(path, fullpath=False, sysfile=False):
    import os
    
    # 指定ディレクトリの配下にあるディレクトリやファイルのパスを生成する
    def gene(filedir):
        if fullpath == True:
            for dirpath, dirnames, filenames in os.walk(filedir):
                yield dirpath
                for name in filenames:
                    yield os.path.join(dirpath, name)
        else:
            for dirpath, dirnames, filenames in os.walk(filedir):
                yield os.path.relpath(dirpath, filedir)
                for name in filenames:
                    full = os.path.join(dirpath, name)
                    yield os.path.relpath(full, filedir)
    
    # リストにする
    list = [file for file in gene(path)]
    
    # 1個目は指定ディレクトリ自身であり不要なので消す
    list = list[1:]
    
    # .で始まるシステムファイルを除去する
    if sysfile == False:
        for file in list:
            if '/.' in file:
                list.remove(file)
    
    # フルパス又は指定ディレクトリからの相対パスを返す
    return(list)


 これでインタラクティブシェルで、以下のように操作すると取得できました。

>>> filedir = '/Users/yk/Dropbox/Python/Test/Tree/sample_files'
>>> result = listdir_all(filedir, fullpath=False, sysfile=True)
>>> 
>>> for x in result:
...     print(x)
... 
20151008_戦略メモ.docx
20151008_戦略メモ_rev.pptx
パワポ追記分.ppt
下書き.txt
送付資料.zip
新組織
新組織/.DS_Store
新組織/組織のあり方.docx
新組織/組織のあり方.txt
新組織/組織イメージ図.pptx
新組織/画像素材
新組織/画像素材/IMG_0336_2.jpg
新組織/画像素材/IMG_0452_2.jpg
新組織/画像素材/最終版画像.png
新組織/画像素材/素材2.JPG
新組織/画像素材/素材4.JPG
新組織/画像素材/編集中画像.pxm
>>> 


>>> result = listdir_all(filedir, fullpath=True, sysfile=False)
>>> 
>>> for x in result:
...     print(x)
... 
/Users/yk/Dropbox/Python/Test/Tree/sample_files/20151008_戦略メモ.docx
/Users/yk/Dropbox/Python/Test/Tree/sample_files/20151008_戦略メモ_rev.pptx
/Users/yk/Dropbox/Python/Test/Tree/sample_files/パワポ追記分.ppt
/Users/yk/Dropbox/Python/Test/Tree/sample_files/下書き.txt
/Users/yk/Dropbox/Python/Test/Tree/sample_files/送付資料.zip
/Users/yk/Dropbox/Python/Test/Tree/sample_files/新組織
/Users/yk/Dropbox/Python/Test/Tree/sample_files/新組織/組織のあり方.docx
/Users/yk/Dropbox/Python/Test/Tree/sample_files/新組織/組織のあり方.txt
/Users/yk/Dropbox/Python/Test/Tree/sample_files/新組織/組織イメージ図.pptx
/Users/yk/Dropbox/Python/Test/Tree/sample_files/新組織/画像素材
/Users/yk/Dropbox/Python/Test/Tree/sample_files/新組織/画像素材/IMG_0336_2.jpg
/Users/yk/Dropbox/Python/Test/Tree/sample_files/新組織/画像素材/IMG_0452_2.jpg
/Users/yk/Dropbox/Python/Test/Tree/sample_files/新組織/画像素材/最終版画像.png
/Users/yk/Dropbox/Python/Test/Tree/sample_files/新組織/画像素材/素材2.JPG
/Users/yk/Dropbox/Python/Test/Tree/sample_files/新組織/画像素材/素材4.JPG
/Users/yk/Dropbox/Python/Test/Tree/sample_files/新組織/画像素材/編集中画像.pxm
>>> 

 

追記:Windowsの場合

 Windowsの場合、とにかくパスの問題がめんどくさいです。
 とりあえず、


cocodrips.hateblo.jp


 ↑の記事を参考にして、無理やりな方法で動かしてみました。
 関数定義は次のとおりです。

def listdir_all(filedir, fullpath=False, sysfile=False):
    # パスは/で区切っても\で区切ってもよいが、\の場合は重ねてエスケープするか、
    # raw string(r'hoge')で与えること!

    import os, re
    
    # 一旦パスの分割記号を変換
    filedir2 = filedir.replace('/', os.sep)  # /で入力された場合
    filedir3 = filedir.replace(os.sep, '/')  # 最後に使う
    
    # 指定ディレクトリの配下にあるディレクトリやファイルのフルパスを生成する関数
    def gene(path):
        for dirpath, dirnames, filenames in os.walk(path):
            yield dirpath
            for name in filenames:
                yield os.path.join(dirpath, name)
    
    # リストにする
    list = [file for file in gene(filedir2)]  # ここでは\を使う
    
    # 1個目は指定ディレクトリ自身であり不要なので消す
    list = list[1:]
    
    # \だとあとでパターンマッチが難しいので/に戻す
    list = [file.replace(os.sep, '/') for file in list]

    # .で始まるシステムファイルを除去する
    if sysfile == False:
        for file in list:
            if '/.' in file:
                list.remove(file)
    
    # フルパス又は指定ディレクトリからの相対パスを返す
    if fullpath == True:
         return(list)
    else:
        # ここでは/でマッチする
        list = [re.sub(filedir3, '', file, count=1) for file in list]
        list = [file.lstrip('/') for file in list]
        return(list)


 Windowsのパスは"\"で区切って指定するわけですが、"\"はPythonのコード上では特殊文字として扱われてしまう。
 だから、'hoge\\hoge'というふうに重ねる*1か、r'hoge\hoge'というraw stringの形式で入力する必要がある。
 なお、"/"で区切ってもじつはPythonは認識してくれる。上のコードでは、どっちで入力されても大丈夫。
 os.sepがディレクトリの分割記号を表すことを利用することもできる。
 os.walkとos.pathの組み合わせで生成されるパスは"\\"と重ねてエスケープする方式で返ってきたのだが、上述のように最後に文字列置換するやり方だと、検索でちゃんと当てるのが難しかったので、最後にos.sepを"/"に置き換えている。


 実行してみます。

>>> # これでいける
... filedir = 'D:/sample/folder1'
>>> list = listdir_all(filedir, fullpath=False, sysfile=True)
>>> print(list)
['20151001_○○に関するメモ.txt', '20151015_○○会議議事録.txt', '○○パスワード設定.txt', '○○利用ルール.txt', '20151125_△△打合せ資料', '20151125_△△打合せ資料/20151000_△△イメージ(A3).pptx', '20151125_△△打合せ資料/20151125_△△打合せ.pptx', '20151125_△△打合せ資料/△△方式比較表.xlsx', '20151125_△△打合せ資料/参考資料', '20151125_△△打合せ資料/参考資料/テンプレ.pptx', '20151125_△△打合せ資料/参考資料/名簿.xlsx']
>>> 
>>> # これでもいける
... filedir = r'D:\sample\folder1'
>>> list = listdir_all(filedir, fullpath=False, sysfile=True)
>>> print(list)
['20151001_○○に関するメモ.txt', '20151015_○○会議議事録.txt', '○○パスワード設定.txt', '○○利用ルール.txt', '20151125_△△打合せ資料', '20151125_△△打合せ資料/20151000_△△イメージ(A3).pptx', '20151125_△△打合せ資料/20151125_△△打合せ.pptx', '20151125_△△打合せ資料/△△方式比較表.xlsx', '20151125_△△打合せ資料/参考資料', '20151125_△△打合せ資料/参考資料/テンプレ.pptx', '20151125_△△打合せ資料/参考資料/名簿.xlsx']
>>> 
>>> # これもいける
... filedir = 'D:\\sample\\folder1'
>>> list = listdir_all(filedir, fullpath=False, sysfile=True)
>>> print(list)
['20151001_○○に関するメモ.txt', '20151015_○○会議議事録.txt', '○○パスワード設定.txt', '○○利用ルール.txt', '20151125_△△打合せ資料', '20151125_△△打合せ資料/20151000_△△イメージ(A3).pptx', '20151125_△△打合せ資料/20151125_△△打合せ.pptx', '20151125_△△打合せ資料/△△方式比較表.xlsx', '20151125_△△打合せ資料/参考資料', '20151125_△△打合せ資料/参考資料/テンプレ.pptx', '20151125_△△打合せ資料/参考資料/名簿.xlsx']
>>> 
>>> # これはダメ
... filedir = 'D:\sample\folder1'
>>> list = listdir_all(filedir, fullpath=False, sysfile=True)
>>> print(list)
[]
>>> 


 行けました。

注意:時間がなくて後回しにしていること

  • 上のコードだと、Windowsの場合に、一番左の"/"があったりなかったりで変になってると思うので修正する必要があるが、やってない。
  • コメント欄で凡さんが教えてくれているコードを試してみる必要があるが、やってない。

*1:1個目の\がエスケープを表し、2個目の\が\として認識される。ネットワークドライブなどでもともと\\が入ったパスを表現するときは\\\\と打たなければならない

Macに入れた各種Pythonを全部アンインストールしてAnacondaを入れなおす

Pythonいろいろ入れすぎた

 Python3を単独で入れてみたり、Canopyを入れたりAnacondaを入れたり、Spyderを入れたらその中にもPythonが入っていたり・・・と、わけがわからなくなってきたので、いったんMacに標準で入っているもの以外は全部アンインストールして、Anacondaを基本にすることにしました。
 Spyderも、前のエントリではSpyder単独でインストールしたのですが、Anacondaにパッケージされたものを使えばいいことが判明したので、今のうちにやり直したい。

Apple が提供している Python は /System/Library/Frameworks/Python.framework と /usr/bin/python にそれぞれインストールされています。これらは Apple が管理しているものであり Apple やサードパーティのソフトウェアが使用するので、編集したり削除してはいけません。

Macintosh で Python を使う — Python 2.7ja1 documentation


 と言われるように、"/System/Library/Frameworks/"配下のPythonについては残しておくことにします。
 ちなみにこのOS X標準のPythonを動かすためのコマンドは、"/usr/bin/"に入っており、これらも削除せずに残します。
 
 
 一方、自分でインストールしたPython(たとえばOS Xに標準では入っていないPython3とか)ってのはどこに入っているかというと、"/Library/Frameworks/Python.framework/"の中です。コマンドは、"/usr/local/bin/"の中にある。
 また、私の場合はこれの他に、Canopyの中に入っているPythonとAnacondaに入っているPythonとSpyder.appの中に入っているPythonがあった。それぞれ、"~/Library/Enthought/"及び"~/Library/Canopy"、"~/anaconda/"、"~/Applications/Spyder.app/"及び"~/Applications/Spyder-Py2.app/"の配下に置かれているやつです。Canopy関係のものは前のエントリで一掃してあるけど。
 
 
 以下、いろいろ削除しまくりますが、消してはいけないものを消してる可能性もあります。
 私は、なんか不具合が起きたらTimemachineで元に戻せばいいだろぐらいに思ってるので気軽にやってるのですが、バックアップ取らずにやるのはやめたほうがいいと思います。
 今のところ、何も問題は起きてないです。一瞬、bashの環境変数がキャッシュされてていくつかのコマンドが無効と言われましたが、後述の通りターミナルを再起動するか、

$ hash -r

で削除すればOK。


いろいろ削除していく

 さてまず自分で入れたPython3とかがあるので、これを消します。


 "/Library/Frameworks/Python.framework"


 を思い切ってまるごと削除。そして


 "/Library/Python/"


 も消す。
 次にこれらに関連するコマンド("/usr/local/bin/"内にある)を削除するんですが、何を削除すればいいのかはターミナルでlsとgrepを組み合わせて探す。

$ ls -l /usr/local/bin/ | grep Python
lrwxr-xr-x  1 root    wheel        66 10  1 01:24 2to3 -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/2to3
lrwxr-xr-x  1 root    wheel        70 10  1 01:24 2to3-3.5 -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/2to3-3.5
lrwxrwxr-x  1 root    admin        78 10  1 01:24 easy_install-3.5 -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/easy_install-3.5
lrwxr-xr-x  1 root    wheel        67 10  1 01:24 idle3 -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/idle3
lrwxr-xr-x  1 root    wheel        69 10  1 01:24 idle3.5 -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/idle3.5
lrwxrwxr-x  1 root    admin        66 10  1 01:24 pip3 -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/pip3
lrwxrwxr-x  1 root    admin        68 10  1 01:24 pip3.5 -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/pip3.5
lrwxr-xr-x  1 root    wheel        68 10  1 01:24 pydoc3 -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/pydoc3
lrwxr-xr-x  1 root    wheel        70 10  1 01:24 pydoc3.5 -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/pydoc3.5
lrwxr-xr-x  1 root    wheel        69 10  1 01:24 python3 -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/python3
lrwxr-xr-x  1 root    wheel        72 10  1 01:24 python3-32 -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/python3-32
lrwxr-xr-x  1 root    wheel        76 10  1 01:24 python3-config -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/python3-config
lrwxr-xr-x  1 root    wheel        71 10  1 01:24 python3.5 -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5
lrwxr-xr-x  1 root    wheel        74 10  1 01:24 python3.5-32 -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5-32
lrwxr-xr-x  1 root    wheel        78 10  1 01:24 python3.5-config -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5-config
lrwxr-xr-x  1 root    wheel        72 10  1 01:24 python3.5m -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5m
lrwxr-xr-x  1 root    wheel        79 10  1 01:24 python3.5m-config -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5m-config
lrwxr-xr-x  1 root    wheel        68 10  1 01:24 pyvenv -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/pyvenv
lrwxr-xr-x  1 root    wheel        72 10  1 01:24 pyvenv-3.5 -> ../../../Library/Frameworks/Python.framework/Versions/3.5/bin/pyvenv-3.5


 ここで出てきた"2to3"以下を全部消します。


 Spyderについては、Python3用の"Spyder.app"とPython2用の"Spyder-Py2.app"を両方入れていたので、両方消すことにします。
 AppCleanerで探索すると、


 "/Applications/Spyder.app"
 "/Applications/Spyder-Py2.app"


 の他に関連ファイル、ディレクトリとして、


 "/private/var/db/BootCaches/C70FF7C7-CA68-4BBA-A7BB-AA9ABD504ADB/app.org.spyder-ide.playlist"
 "~/Library/Saved\ Application\ State/org.spyder-ide.savedState"
 "~/Library/Preferences/org.spyder-ide.plist"


 が挙げられたので、これらを全部消します。


 次に、Anacondaを削除します。Anacondaの公式サイトにも書いてあるように、"~/anaconda/"ディレクトリをまるごと削除すればOK。


 そしてSpyderの設定関係のファイルが入っている、"~/.spyder2"、"~/.spyder2-py3"も削除。これらは、Spyder.app等を入れた時にできたものだと思ったのですが、後で試したらAnacondaの中に入っているSpyderを起動した時にも作成されるもののようです。それはともかく、削除。
 "~/ipython/"も削除。
 何も入ってないけど"~/continuum/"も削除。
 
 
 次に、"~/.bash_profile"をテキストエディタで開いて、

# Setting PATH for Python 3.5
# The orginal version is saved in .bash_profile.pysave
PATH="/Library/Frameworks/Python.framework/Versions/3.5/bin:${PATH}"
export PATH

# added by Anaconda3 2.3.0 installer
export PATH="/Users/ユーザ名/anaconda/bin:$PATH"


 という記述を削除。これらは、Python3.5を独自インストールした時と、Anacondaをインストールした時に書き込まれた、bashのPATH環境変数にパスを追加するための記述ですね。


 "~/.bash_profile.pysave"
 "~/.bash_profile-anaconda.bak"


 というバックアップもあったので消しました。これらは、PATH環境変数への追加を行った際に、それ以前のbash_profileを自動でバックアップしてたものですね。
 bash_profileが書き換わっているので、いったんターミナルを終了してbashを再起動します。
 PATH変数を確認すると、

$ echo $PATH
/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/texbin


 となっており、さっき消したパスがなくなっていることが分かります。

Anacondaを改めてインストール

 さて、改めてAnacondaをインストールします。
 Anacondaさえ入れておけば、Pythonもインストールされるし、科学計算に使われる各種ライブラリや、pipやIPythonやSpyderも一気にインストールされます。
 やることは単に、
 
 
 Download Anaconda now! | Continuum
 ここへ行ってOS X用のインストーラをダウンロードし、インストールするだけですね。


 インストールするとbashのPATH環境変数に"~/anaconda/bin"が追加され、各種コマンドはそこに置かれています。
 モジュールは"~/anaconda/lib/python3.5/site-packages"などにインストールされます。 
 
 
 インストール後、Launcher.appのショートカットがデスクトップに置かれますが、これは削除して私はDockに置きました。
 

f:id:midnightseminar:20151105020410p:plain
 
 
 では起動します。


f:id:midnightseminar:20151105020435p:plain
 
 
 Spyderを選択します。
 

f:id:midnightseminar:20151105020455p:plain

 
 起動できました。
 Spyder.appを入れた場合に比べて、少し起動が遅いような気はします。AnacondaのLauncher起動とあわせて、私のMacBook Air(2012年のもの)で20〜30秒かかりますね。iMacだと、少し早いような気がしたりしなかったりというレベルです。
 とりあえず使えるので満足です。


 (2015.11.6追記)
 ブコメで

Macに入れた各種Pythonを全部アンインストールしてAnacondaを入れなおす - StatsBeginner: 初学者の統計学習ノート

pyenvを使えば後から消すのも楽だし他のバージョンが必要になった時も簡単に入れられて便利です。

2015/11/05 21:09
b.hatena.ne.jp
 というアドバイスを頂いたので、あとでためしてみます。ありがとうございます。pyenvからAnacondaを入れることもできるみたいだし。


 なお本エントリは、そういうツールを使わずに既に無秩序に色々入れてしまったのを掃除するという目的の作業でした。