pyenvでPython環境を利用する

環境によっては、Pythonが古かったり、しかもそれを更新できなかったりする場合があるかと思うけど、そんなときにpyenvでPython環境を作るとよい。
今回は、Ubuntuに入れる想定で進める。まず、pyenvをcloneする。

$ git clone git://github.com/yyuu/pyenv.git ~/.pyenv

そして、環境変数の設定。Ubuntuでなければ、.bash_profileとかに入れるのが良いでしょう。

$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(pyenv init -)"' >> ~/.bashrc

ここで、一度ターミナルを開き直す。
pyenvで利用できるバージョンを確認する。

$ pyenv install -l
Available versions:
  2.1.3
  2.2.3
  2.3.7
  2.4
  2.4.1
  2.4.2
  2.4.3
...

今回は2.7.9を入れる。

$ pyenv install 2.7.9
$ pyenv rehash

インストールしたPythonを利用するように設定する。

$ pyenv versions
* system (set by /home/taro/.pyenv/version)
  2.7.9
$ pyenv global 2.7.9
$ pyenv versions
  system
* 2.7.9 (set by /home/taro/.pyenv/version)

という感じで、環境の準備完了。
Python 2.7系であれば、~/.pyenv/versions/2.7.9/lib/python2.7/site-packages/sitecustomize.pyを作成して

import sys
sys.setdefaultencoding('utf-8')

を記述しておくとエンコーディング周りで悩まされないでよいかも。
あとは、使うモジュールをインストールしておく。(以下は個人的なメモに近いけど…)

pip install elasticsearch
pip install numpy
pip install scipy
pip install matplotlib
pip install pandas
pip install scikit-learn
pip install gensim

Wikipediaのデータをスクロールスキャンする

WikipediaをElasticsearchに取り込むの続き。
いろいろと試していくためには、Elasticsearchに取り込んだWikipediaのデータにアクセスできるようになる必要がある。Elasticsearchで全データにアクセスするためには、ScrollScanでアクセスする。
というわけで、今回はPythonで全データにアクセスしてみる。
事前にpip install ElasticsearchなどでPythonモジュールを利用可能な状況にしておく必要があるが、以下のような.pyを作成する。

# coding: utf-8
from optparse import OptionParser
import sys
from elasticsearch import Elasticsearch
import json
from elasticsearch.exceptions import NotFoundError
def main(argv=sys.argv):
    parser = OptionParser(prog="main.py")
    parser.add_option("--elasticsearch", dest="elasticsearch",
                  help="Elasticsearch", metavar="ES",
                  default="localhost:9200")
    parser.add_option("--index", dest="index",
                  help="Index", metavar="INDEX",
                  default="_all")
    parser.add_option("--source", dest="source",
                  help="Source", metavar="SOURCE",
                  default="{\"query\":{\"match_all\":{}}}")
    (options, args) = parser.parse_args()
    source = json.loads(options.source)
    es = Elasticsearch(hosts=options.elasticsearch)
    response = es.search(index=options.index,
                         scroll='1m',
                         search_type='scan',
                         size=100,
                         body=source)
    scroll_id = response['_scroll_id']
    counter = 0
    while (True):
        try:
            response = es.scroll(scroll_id=scroll_id, scroll='1m')
            if len(response['hits']['hits']) == 0:
                break
            for hit in response['hits']['hits']:
                counter = counter + 1
                if "_source" in hit:
                    if "title" in hit['_source']:
                        print hit['_source']['title']
        except NotFoundError:
            print u"Finished ({0}) documents.".format(counter)
            break
        except:
            print u"Unexpected error: {0}".format(sys.exc_info()[0])
            break
    return 0
if __name__ == "__main__":
    sys.exit(main())

上記の例ではスクロールで_sourceのtitleデータを出力している。
NotFoundErrorのエラーで止めるのも微妙だけど、何か良い方法があるのかしら…。
実行は

$ python title.py --index jawiki-pages-articles

という感じで実行すれば、タイトルが出力されていく。
というわけで、そのあたりのコードをいじればいろいろできる感じかな。

sklearnのVectorizer

sklearnのVectorizerは文を渡して、それをベクトル化してくれるものですが、sklearnに含まれているCountVectorizerとTfidfVectorizer試してみる。
まずは、適当な文の配列を用意する。

$ python
Python 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> corpus = [
... 'aaa bbb ccc ddd eee',
... 'aaa bbb ccc ddd',
... 'aaa bbb ccc',
... 'aaa bbb ddd eee',
... 'aaa ddd eee'
... ]

まずは、単語数を数えて、それをベクトルに変換する。

>>> from sklearn.feature_extraction.text import CountVectorizer
>>> count_vect = CountVectorizer(min_df=1)

という感じで、CountVectorizerを生成する。
そして、先ほど作った文の配列をまるっと渡す。

>>> X_count = count_vect.fit_transform(corpus)

X_countとして結果が返ってきますが、中身を確認すると、

>>> print X_count.todense()
[[1 1 1 1 1]
 [1 1 1 1 0]
 [1 1 1 0 0]
 [1 1 0 1 1]
 [1 0 0 1 1]]

という感じで、単語数を数えて配列のベクトルが得られる。
ベクトルがどの単語に対応しているかは、get_feature_namesで得られる。

>>> count_vect.get_feature_names()
[u'aaa', u'bbb', u'ccc', u'ddd', u'eee']
>>> count_vect.vocabulary_.get('ddd')
3
>>> count_vect.vocabulary_.get('bbb')
1

新規の文をベクトルにする場合は

>>> count_vect.transform(['aaa ccc eee']).toarray()
array([[1, 0, 1, 0, 1]])

とすればOK。
TFIDFを用いて、同じようにベクトルを生成するためにはTfidfVectorizerを利用する。

>>> from sklearn.feature_extraction.text import TfidfVectorizer
>>> tfidf_vect = TfidfVectorizer()
>>> X_tfidf = tfidf_vect.fit_transform(corpus)
>>> print X_tfidf.todense()
[[ 0.3592933   0.42480021  0.5049742   0.42480021  0.5049742 ]
 [ 0.41626575  0.49215996  0.58504698  0.49215996  0.        ]
 [ 0.47818893  0.56537308  0.67207785  0.          0.        ]
 [ 0.41626575  0.49215996  0.          0.49215996  0.58504698]
 [ 0.47818893  0.          0.          0.56537308  0.67207785]]
>>> tfidf_vect.get_feature_names()
[u'aaa', u'bbb', u'ccc', u'ddd', u'eee']
>>> tfidf_vect.vocabulary_.get('ddd')
3
>>> tfidf_vect.vocabulary_.get('bbb')
1
>>> tfidf_vect.transform(['aaa ccc eee']).toarray()
array([[ 0.44943642,  0.        ,  0.6316672 ,  0.        ,  0.6316672 ]])