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 ]])

pydotのdot_parserエラー

Couldn't import dot_parser, loading of dot files will not be possible.

というエラーが出る場合は、/usr/local/lib/python2.7/dist-packages/dot_parser.pyとかのdot_parser.pyを

$ diff -ub dot_parser.py.orig dot_parser.py
--- dot_parser.py.orig	2014-12-20 21:21:51.864889677 +0900
+++ dot_parser.py	2014-12-20 21:22:15.938683519 +0900
@@ -23,8 +23,9 @@
 from pyparsing import ( nestedExpr, Literal, CaselessLiteral, Word, Upcase, OneOrMore, ZeroOrMore,
     Forward, NotAny, delimitedList, oneOf, Group, Optional, Combine, alphas, nums,
     restOfLine, cStyleComment, nums, alphanums, printables, empty, quotedString,
-    ParseException, ParseResults, CharsNotIn, _noncomma, dblQuotedString, QuotedString, ParserElement )
+    ParseException, ParseResults, CharsNotIn, dblQuotedString, QuotedString, ParserElement )
+_noncomma = "".join( [ c for c in printables if c != "," ] )
 class P_AttrList:

という感じで修正しておく。

PythonとElasticsearchで形態素解析

Elasticsearchから取り出してsklearnでテキストデータをゴニョゴニョするときにPython側ではIgoを使って形態素解析とかしていたけど、Elasticsearchでやっている形態素解析と一致しないのもなんだし、Igoを直読みするよりパフォーマンスは落ちると思うけど、辞書管理等々考えるとElasticsearchのanalyze APIを使って形態素解析しちゃった方がシンプルかなっと思い、まとめておく。
まず、PythonでElasticsearchのAnalyze APIが呼べることを以下で確認する。ここでは、Python 2.7を利用している。elasticsearch-pyもインストール済みな想定で、Elasticsearchも起動しておく必要がある。

$ 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.
>>> import sys
>>> reload(sys)
<module 'sys' (built-in)>
>>> sys.setdefaultencoding("utf-8")
>>> from elasticsearch import Elasticsearch
>>> es = Elasticsearch(hosts="localhost:9200")
>>> text=u"今日の天気は晴れです。"
>>> es.indices.analyze(index=u"sample",body=text,params={u"field":u"message"})
{u'tokens': [{u'end_offset': 2, u'token':...

Python2の問題だけど、setdefaultencodingでセットしないとエラーになる。
エンコーディングさえ問題なければ、上記ではsampleインデックスのmessageフィールドのAnalyzerでtextをトークナイズできる。
まぁ、Ubuntuとかであれば、/etc/python2.7/sitecustomize.py の先頭に

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

を追加してdefaultencodingをutf-8にしてしまっても良いのかも。(最適な対応方法はわからない…) ほかの環境であれば、/usr/lib/python*/site-packages/sitecustomize.pyあたりに記述すればよい。
ここまで、できてしまえば、sklearnとかで使いたいなら、

class Analyzer:
    """Analyzer"""
    def __init__(self, es, index, field):
        self.es = es
        self.index = index
        self.field = field
    def __call__(self, text):
        if not text:
            return []
        data = self.es.indices.analyze(index=self.index,
                                       body=text, params={"field":self.field})
        tokens = []
        for token in data.get("tokens"):
            tokens.append(token.get("token"))
        return tokens

というような感じで、Analyzerクラスを作っておいて

es = Elasticsearch(hosts="localhost:9200")
analyzer = Analyzer(es, "sample", "message")
vectorizer = TfidfVectorizer(analyzer=analyzer)

みたいな感じで呼べば、いろいろなVectorizerで利用できると思います。