Python製プログレスバーを3行で書く

for文の中に標準出力を書くと、ターミナルが文字で埋め尽くされるので回避したいが、 for文の中身が重くて無反応になるのも困る。 なのでプログレスバーを書けば良い、という結論に至るわけだが、 さくっと色んなfor文に書きたいので、3行で書く方法を説明する。

インストール

progressbarというそのままな名前のライブラリーをインストールする。 2016年10月現在は、progressbar2 3.10.1という謎のバージョン。

# pip install progressbar2

実装

手順は次の3つ。

  1. インポート
  2. 初期化
  3. 更新

for文の外でインポートと初期化を行い、for文の中で更新を行う。 初期化の時に、繰り返し回数をパラメーターに指定する。 またfor文の中では、個々のデータではなく、全てのデータをindex(i)で指定して扱えるようにする。 なおサンプルとして、RSSを入れるとページのソースを取得してくれるメソッドget_src()を利用する。 実装は次の通り。

from progressbar import ProgressBar  # 1. インポート

def main(rss):
    pb = ProgressBar(maxval=len(rss)).start()  # 2. 初期化
    for i in range(len(rss)):
        get_src(rss[i])
        pb.update(i+1)  # 3. 更新

結果

ターミナルの幅をフルで使った出力になる。 カスタマイズしたい場合は、参考文献を参照。

f:id:Shoto:20161009165725g:plain

参考文献

Luhnの要約アルゴリズムの後処理

これまでLuhnの要約アルゴリズム前処理として基本的な自然言語処理を、 本処理としてテキストの各文のスコア付けを行った。 今回は後処理として要約テキストとして表示させる。 要約テキストは、スコア付けされた文を選択することで生成される。 ここでは2種類の選択方法について説明する。

各文のスコア

各文のスコアは以下のようになる。

index score sentence
0 5.1 スマートフォンでもPCでもオフラインでも使え、100以上の言語に対応しているGoogleの翻訳サービス「Google翻訳」が、ちょうど10周年を迎えるタイミングでより自然な翻訳を可能にする人工知能(AI)を活用した「GNMT」システムを発表しました
1 6.8 AI研究で複数の大手IT企業とのパートナーシップを結んだばかりのGoogleは、AIを駆使した翻訳システム「GNMT(Google Neural Machine Translation)」を発表しました
2 2.3 Google翻訳ではこれまでフレーズベースで機械翻訳するPBMTというシステムが採用されていましたが、このような単語やフレーズごとに機械的に文章を翻訳する方法ではなく、文章全体をひとつの翻訳単位として捉えることができるのがGNMTだそうです
3 2.3 Googleの研究者によると、「GNMTの『文章全体をひとつの翻訳単位として捉える』アプローチの長所は、工学的設計の選択肢がPBMTよりも少なくて済むことです」とのこと
4 1.3 実際にGNMTを駆使した最初の翻訳では、既存のGoogle翻訳と遜色ない翻訳精度がみられたそうです
5 1.8 さらに、何度も翻訳を重ねることで、GNMTは優れた翻訳と素早い翻訳スピードの両立が可能になっている模様
6 4.5 Googleによると、GNMTを用いることでGoogle翻訳は翻訳ミスを55~85%も軽減できるようになるとのこと
7 2.7 GoogleによるとGNMTは一部のケースでは人間レベルの翻訳が可能なレベルに達しているとのこと
8 3.2 以下のグラフは人間・GNMT・PBMTの3つによる翻訳を6段階評価して比較したもので、最も翻訳精度が高いのは人間による翻訳ですが、フランス語から英語に翻訳する場合や英語からスペイン語に翻訳する場合、人間とGNMTの間にそれほど大きな差は存在しないそうです

標準偏差による文の選択

0.5σ以上のスコアを持つ文を選択する。

# 要約アプローチ1
# 平均スコアとフィルタとしての標準偏差を使って重要ではない文を取り除く
avg = numpy.mean([s[1] for s in scored_sents])
std = numpy.std([s[1] for s in scored_sents])
mean_scored = [(sent_idx, score) \
               for (sent_idx, score) in scored_sents \
               if score > avg + 0.5 * std]

平均 3.3, 標準偏差1.7なので、0.5σは4.2となる。 そのため0, 1, 6番目の文が選択される。

>>> print mean_scored
[(0, 5.142857142857143), (1, 6.75), (6, 4.5)]

スマートフォンでもPCでもオフラインでも使え、100以上の言語に対応しているGoogleの翻訳サービス「Google翻訳」が、ちょうど10周年を迎えるタイミングでより自然な翻訳を可能にする人工知能(AI)を活用した「GNMT」システムを発表しました。AI研究で複数の大手IT企業とのパートナーシップを結んだばかりのGoogleは、AIを駆使した翻訳システム「GNMT(Google Neural Machine Translation)」を発表しました。Googleによると、GNMTを用いることでGoogle翻訳は翻訳ミスを55~85%も軽減できるようになるとのこと。

TopNの文の選択

単純に指定された数の文だけスコアが高い順に選択する。

self.TOP_SENTENCES = 5  # 「トップN」要約のために返す文の数

# 要約アプローチ2
# トップNにランクされた文だけを返す
top_n_scored = sorted(scored_sents, key=lambda s:s[1])[-self.TOP_SENTENCES:]
top_n_scored = sorted(top_n_scored, key=lambda s:s[0])

そのため、0, 1, 6, 7, 8番目の文が選択される。

>>> print top_n_scored
[(0, 5.142857142857143), (1, 6.75), (6, 4.5), (7, 2.6666666666666665), (8, 3.2)]

スマートフォンでもPCでもオフラインでも使え、100以上の言語に対応しているGoogleの翻訳サービス「Google翻訳」が、ちょうど10周年を迎えるタイミングでより自然な翻訳を可能にする人工知能(AI)を活用した「GNMT」システムを発表しました。AI研究で複数の大手IT企業とのパートナーシップを結んだばかりのGoogleは、AIを駆使した翻訳システム「GNMT(Google Neural Machine Translation)」を発表しました。Googleによると、GNMTを用いることでGoogle翻訳は翻訳ミスを55~85%も軽減できるようになるとのこと。GoogleによるとGNMTは一部のケースでは人間レベルの翻訳が可能なレベルに達しているとのこと。以下のグラフは人間・GNMT・PBMTの3つによる翻訳を6段階評価して比較したもので、最も翻訳精度が高いのは人間による翻訳ですが、フランス語から英語に翻訳する場合や英語からスペイン語に翻訳する場合、人間とGNMTの間にそれほど大きな差は存在しないそうです。

Luhnの要約アルゴリズムの本処理

Luhnの要約アルゴリズムの前処理の続き。 記事本文を"。"で分割して文のリストにし、さらに英語を小文字化したのがnormalized_sents(以下、sents)だった。 ここでは、分割された各文がどのようにスコア付けされるかを見ていく。

文を単語に分割する

今回は、以下の文の処理の流れを追っていく。

googleによるとgnmtは一部のケースでは人間レベルの翻訳が可能なレベルに達しているとのこと

nltkjp.word_tokenize()分かち書きを行うメソッド。

scores = []
sent_idx = -1
nltkjp = NLTKJP()
for s in [nltkjp.word_tokenize(sent) for sent in sents]:
    sent_idx += 1

文が単語に分割されたので、sの中身は次のようになる。

>>> for d in s:
>>>     print d,
>>> print
google に よる と gnmt は 一部 の ケース で は 人間 レベル の 翻訳 が 可能 な レベル に 達し て いる と の こと

文中の重要単語の位置を把握する

Luhnの要約アルゴリズムの前処理 で、頻出名詞Top100をimportant_wordsとして取得した。 これらが文中のどこに位置するかを把握する。

    word_idx = []

    # 単語リスト中の個々の単語について
    for w in important_words:
        try:
            # 文中の重要単語が出現した位置のインデックスを計算する
            word_idx.append(s.index(w))
        except ValueError, e:  # この文にはwが含まれていない
            pass

    word_idx.sort()

    # 一部の文は、重要単語を1つも含んでいないことがありえる
    if len(word_idx) == 0:
        continue

位置は次のようになる。

>>> print word_idx
[0, 4, 6, 8, 11, 12, 14, 16]

実際に単語に分割した文と重要単語の位置を比較すると、次のようになる。 上記のアルゴリズムでは文中に同じ重要単語が出てきた時にカウントできないことが分かる。 次の例では"レベル"が12と18にあるが、12しかカウントされていない。 意図的ではなくミスだと思うので、後で改修する。

文中の重要単語の距離からクラスタリングを行う

文中の重要単語間の距離が3以内であれば、それらを1つのクラスタとする。 オリジナルの距離は5。

    self.CLUSTER_THRESHOLD = 3  # 考慮する単語の間の距離

    # 単語のインデックスを使って2つの連続する単語に対して
    # 最大距離の閾値を使ってクラスタを計算する
    clusters = []
    cluster = [word_idx[0]]
    i = 1
    while i < len(word_idx):
        if word_idx[i] - word_idx[i-1] < self.CLUSTER_THRESHOLD:
            cluster.append(word_idx[i])
        else:
            clusters.append(cluster[:])
            cluster = [word_idx[i]]
        i += 1
    clusters.append(cluster)

実行すると3つのクラスタに分類された。

>>> print clusters
[[0], [4, 6, 8], [11, 12, 14, 16]]

クラスタのスコアを計算する

クラスタ内の重要単語数の二乗を、クラスタの最初と最後の距離で割った値を、そのクラスタのスコアとする。 最終的に各クラスタのスコアの最大値がその文のスコアになる。

    # 各クラスタのスコアを計算。クラスタのスコアの最大値がその文のスコア
    score_list = []
    max_cluster_score = 0
    for c in clusters:
        swc = len(c)  # significant_words_in_cluster
        twc = c[-1] - c[0] + 1  # total_words_in_cluster
        score = 1.0 * swc*swc / twc
        score_list.append(score)

        if score > max_cluster_score:
            max_cluster_score = score

    scores.append((sent_idx, max_cluster_score))

3つのクラスタとスコアとその最大値(文のスコア)は次のようになる。

>>> print score_list
[1.0, 1.8, 2.7]

>>> print max_cluster_score
2.7

まとめ

以上より、文のスコア付けの流れは次のようになる。

1. googleによるとgnmtは一部のケースでは人間レベルの翻訳が可能なレベルに達しているとのこと
2. google に よる と gnmt は 一部 の ケース で は 人間 レベル の 翻訳 が 可能 な レベル に 達し て いる と の こと
3. [0, 4, 6, 8, 11, 12, 14, 16]
4. [[0], [4, 6, 8], [11, 12, 14, 16]]
5. [1.0, 1.8, 2.7]
6. 2.7

スコア計算メソッドは次の通り。

def score_sentences(self, sents, important_words):
    """
    H.P. Luhn, "The Automatic Creation of Literature Abstracts"によるアプローチ
    """
    scores = []
    sent_idx = -1

    nltkjp = NLTKJP()
    for s in [nltkjp.word_tokenize(sent) for sent in sents]:
        sent_idx += 1
        word_idx = []

        # 単語リスト中の個々の単語について
        for w in important_words:
            try:
                # 文中の重要単語が出現した位置のインデックスを計算する
                word_idx.append(s.index(w))
            except ValueError, e:  # この文にはwが含まれていない
                pass
        word_idx.sort()

        # 一部の文は、重要単語を1つも含んでいないことがありえる
        if len(word_idx) == 0:
            continue

        # 単語のインデックスを使って2つの連続する単語に対して
        # 最大距離の閾値を使ってクラスタを計算する
        clusters = []
        cluster = [word_idx[0]]
        i = 1
        while i < len(word_idx):
            if word_idx[i] - word_idx[i-1] < self.CLUSTER_THRESHOLD:
                cluster.append(word_idx[i])
            else:
                clusters.append(cluster[:])
                cluster = [word_idx[i]]
            i += 1
        clusters.append(cluster)

        # 各クラスタのスコアを計算。クラスタのスコアの最大値がその文のスコア
        score_list = []
        max_cluster_score = 0
        for c in clusters:
            swc = len(c)  # significant_words_in_cluster
            twc = c[-1] - c[0] + 1  # total_words_in_cluster
            score = 1.0 * swc*swc / twc
            score_list.append(score)

            if score > max_cluster_score:
                max_cluster_score = score

        scores.append((sent_idx, max_cluster_score))

    return scores

Luhnの要約アルゴリズムの前処理

入門 ソーシャルデータ ―データマイニング、分析、可視化のテクニック

入門 ソーシャルデータ ―データマイニング、分析、可視化のテクニック

入門 ソーシャルデータ ―データマイニング、分析、可視化のテクニックの8.4章に、Luhnのプリミティブな要約アルゴリズムの解説がある。 このアルゴリズムは非常にシンプルであるものの、そこそこの精度が出せる。 解説では英語のみの対応となっていたため、日本語にも適用できるようにしてみた。

アルゴリズムは、基本的な自然言語処理を行う前処理と、Luhnの要約アルゴリズムとなる本処理、 要約を出力するための後処理に別れる。 ここではまず前処理について説明する。

要約対象テキスト

Google翻訳は人間レベルの翻訳精度を目指して人工知能を活用 の記事本文を要約対象としてみる。

text = "スマートフォンでもPCでもオフラインでも使え、100以上の言語に対応しているGoogleの翻訳サービス「Google翻訳」が、ちょうど10周年を迎えるタイミングでより自然な翻訳を可能にする人工知能(AI)を活用した「GNMT」システムを発表しました。\
AI研究で複数の大手IT企業とのパートナーシップを結んだばかりのGoogleは、AIを駆使した翻訳システム「GNMT(Google Neural Machine Translation)」を発表しました。Google翻訳ではこれまでフレーズベースで機械翻訳するPBMTという\
システムが採用されていましたが、このような単語やフレーズごとに機械的に文章を翻訳する方法ではなく、文章全体をひとつの翻訳単位として捉えることができるのがGNMTだそうです。Googleの研究者によると、「GNMTの『文章全体をひとつの翻訳単位\
として捉える』アプローチの長所は、工学的設計の選択肢がPBMTよりも少なくて済むことです」とのこと。実際にGNMTを駆使した最初の翻訳では、既存のGoogle翻訳と遜色ない翻訳精度がみられたそうです。さらに、何度も翻訳を重ねることで、\
GNMTは優れた翻訳と素早い翻訳スピードの両立が可能になっている模様。Googleによると、GNMTを用いることでGoogle翻訳は翻訳ミスを55~85%も軽減できるようになるとのこと。GoogleによるとGNMTは一部のケースでは人間レベルの翻訳が\
可能なレベルに達しているとのこと。以下のグラフは人間・GNMT・PBMTの3つによる翻訳を6段階評価して比較したもので、最も翻訳精度が高いのは人間による翻訳ですが、フランス語から英語に翻訳する場合や英語からスペイン語に翻訳する場合、\
人間とGNMTの間にそれほど大きな差は存在しないそうです。"

テキストを文に分割

自作の日本語の自然言語処理ライブラリー nltkjp.py (詳しくはここここを参照) に、「。」で分割してリスト化するだけの処理を行うメソッド sent_tokenize() を追加した。

def sent_tokenize(self, doc):
    sents = [d.decode("utf-8") for d in doc.split(u'。')]

    return sents

まずはテキストをこれにかける。

nltkjp = NLTKJP()

# テキストを文に分割
sents = nltkjp.sent_tokenize(text)
>>> for sent in sents:
>>>     print sent
スマートフォンでもPCでもオフラインでも使え、100以上の言語に対応しているGoogleの翻訳サービス「Google翻訳」が、ちょうど10周年を迎えるタイミングでより自然な翻訳を可能にする人工知能(AI)を活用した「GNMT」システムを発表しました
AI研究で複数の大手IT企業とのパートナーシップを結んだばかりのGoogleは、AIを駆使した翻訳システム「GNMT(Google Neural Machine Translation)」を発表しました
Google翻訳ではこれまでフレーズベースで機械翻訳するPBMTというシステムが採用されていましたが、このような単語やフレーズごとに機械的に文章を翻訳する方法ではなく、文章全体をひとつの翻訳単位として捉えることができるのがGNMTだそうです
...

文をすべて小文字に変換

英語の大文字を小文字に変換する。 本当は日本語の動詞もすべて基本形にするとかしないといけないのかもしれないけど、また今後。

normalized_sents = [sent.lower() for sent in sents]
>>> for ns in normalized_sents:
>>>     print ns
スマートフォンでもpcでもオフラインでも使え、100以上の言語に対応しているgoogleの翻訳サービス「google翻訳」が、ちょうど10周年を迎えるタイミングでより自然な翻訳を可能にする人工知能(ai)を活用した「gnmt」システムを発表しました
ai研究で複数の大手it企業とのパートナーシップを結んだばかりのgoogleは、aiを駆使した翻訳システム「gnmt(google neural machine translation)」を発表しました
google翻訳ではこれまでフレーズベースで機械翻訳するpbmtというシステムが採用されていましたが、このような単語やフレーズごとに機械的に文章を翻訳する方法ではなく、文章全体をひとつの翻訳単位として捉えることができるのがgnmtだそうです
...

各文を単語に分割して1つにまとめる

各文章を分かち書きにしてすべてまとめる。

words = [word.lower() for sent in normalized_sents for word in nltkjp.word_tokenize(sent)]
>>> for word in words[:10]:
>>>     print word
スマート
フォン
で
も
pc
で
も
オフライン
で
も
...

頻出単語を抽出する

ここで本家のnltkが登場。FreqDistに単語のリストを入れると、単語をカウントしてソートしてくれる。

fdist = nltk.FreqDist(words)
>>> for k,v in fdist.items()[:10]:
>>>     print k, '\t', v
printの    24
翻訳  23151414131211
gnmt    10
google  10
...

ストップワードでない上位N個の単語を抽出する

頻出単語からストップワードを除き、そのうちの上位N個を取得する。 100にしてるのは、解説がそうだったからだけど、今回の例では多い気がする。 ストップワードの取得については、以下の結果に"("が含まれていたりとまだ不備があるけど、アルゴリズム簡易的な日本語ストップワードの取得メソッドを参照。 ただし、今回はストップワードの品詞は名詞以外としている。

stopwords = nltkjp.stopwords(text)
N = 100
top_n_words = [w[0] for w in fdist.items() if w[0] not in stopwords][:N]
>>> for tnw in top_n_words[:10]:
>>>     print tnw
翻訳
gnmt
google
人間
ai
pbmt
システム
可能
文章
(
...

まとめ

以上が前処理となる。 各処理をまとめると以下のようになる。

import nltk
from nltkjp import NLTKJP

N = 100  # 考慮する単語の数

def main(text):
    nltkjp = NLTKJP()

    # テキストを文に分割
    sents = nltkjp.sent_tokenize(text)

    # 文をすべて小文字に変換
    normalized_sents = [sent.lower() for sent in sents]

    # 各文を単語に分割して1つにまとめる
    words = [word.lower() for sent in normalized_sents for word in nltkjp.word_tokenize(sent)]

    # 頻出単語を抽出する
    fdist = nltk.FreqDist(words)

    # ストップワードでない上位N個の単語を抽出する
    stopwords = nltkjp.stopwords(text)
    top_n_words = [w[0] for w in fdist.items() if w[0] not in stopwords][:N]

簡易的な日本語ストップワードの取得メソッド

それほど厳密に調査した訳ではないが、NLTKのコーパスには日本語のストップワードが存在しないようで、多くの人は SlothLib を利用している、という印象をWebから受けた。 SlothLibのように単語ベースでストップワードを定義している一方で、 IBM Content Analytics のように、シンプルに名詞か動詞以外の品詞の単語をストップワードとしているのもあった。 今回は、これら2種類の定義により、入力したテキストのストップワードを出力するメソッドを実装した。

品詞によるストップワードの取得

前回、単語と品詞のセットのリストが取得できるメソッド word_and_class() を作ったので、 そこから名詞と動詞以外の単語を取得する。

word_class = self.word_and_class(doc)
ok_class = [u"名詞", u"動詞"]
stopwords = []
for wc in word_class:
    if not wc[1] in ok_class:
        stopwords.append(wc[0])

サンプルテキストに「日本語の自然言語処理は本当にしんどい、と彼は十回言った。」を渡すと次のようになる。

>>> for sw in stopwords:
>>>    print sw,
の は 本当に しんどい 、 と は た 。

SlothLibによるストップワードの取得

SlothLibのテキストページ を取得してストップワードとする。

slothlib_path = 'http://svn.sourceforge.jp/svnroot/slothlib/CSharp/Version1/SlothLib/NLP/Filter/StopWord/word/Japanese.txt'
slothlib_file = urllib2.urlopen(slothlib_path)
slothlib_stopwords = [line.decode("utf-8").strip() for line in slothlib_file]
slothlib_stopwords = [ss for ss in slothlib_stopwords if not ss==u'']

実行結果は次の通り。

>>> for ss in slothlib_stopwords:
>>>    print ss,
あそこ あたり あちら あっち あと あな あなた あれ いくつ いつ いま いや いろいろ うち おおまか おまえ おれ がい かく かたち かやの から がら きた くせ ここ こっち こと ごと こちら ごっちゃ これ これら ごろ さまざま さらい さん しかた しよう すか ずつ すね すべて ぜんぶ そう そこ そちら そっち そで それ それぞれ それなり たくさん たち たび ため だめ ちゃ ちゃん てん とおり とき どこ どこか ところ どちら どっか どっち どれ なか なかば なに など なん はじめ はず はるか ひと ひとつ ふく ぶり べつ へん ぺん ほう ほか まさ まし まとも まま みたい みつ みなさん みんな もと もの もん やつ よう よそ わけ わたし ハイ 上 中 下 字 年 月 日 時 分 秒 週 火 水 木 金 土 国 都 道 府 県 市 区 町 村 各 第 方 何 的 度 文 者 性 体 人 他 今 部 課 係 外 類 達 気 室 口 誰 用 界 会 首 男 女 別 話 私 屋 店 家 場 等 見 際 観 段 略 例 系 論 形 間 地 員 線 点 書 品 力 法 感 作 元 手 数 彼 彼女 子 内 楽 喜 怒 哀 輪 頃 化 境 俺 奴 高 校 婦 伸 紀 誌 レ 行 列 事 士 台 集 様 所 歴 器 名 情 連 毎 式 簿 回 匹 個 席 束 歳 目 通 面 円 玉 枚 前 後 左 右 次 先 春 夏 秋 冬 一 二 三 四 五 六 七 八 九 十 百 千 万 億 兆 下記 上記 時間 今回 前回 場合 一つ 年生 自分 ヶ所 ヵ所 カ所 箇所 ヶ月 ヵ月 カ月 箇月 名前 本当 確か 時点 全部 関係 近く 方法 我々 違い 多く 扱い 新た その後 半ば 結局 様々 以前 以後 以降 未満 以上 以下 幾つ 毎日 自体 向こう 何人 手段 同じ 感じ

ストップワードの統合

上記の2種類のストップワードを取得する手法を統合して、1つのメソッドにすると次のようになる。

def stopwords(self, doc):
    """
    Get stopwords from input document.
    """
    # Judged by class
    word_class = self.word_and_class(doc)        
    ok_class = [u"名詞", u"動詞"]
    stopwords = []
    for wc in word_class:
        if not wc[1] in ok_class:
            stopwords.append(wc[0])

    # Defined by SlpothLib
    slothlib_path = 'http://svn.sourceforge.jp/svnroot/slothlib/CSharp/Version1/SlothLib/NLP/Filter/StopWord/word/Japanese.txt'
    slothlib_file = urllib2.urlopen(slothlib_path)
    slothlib_stopwords = [line.decode("utf-8").strip() for line in slothlib_file]
    slothlib_stopwords = [ss for ss in slothlib_stopwords if not ss==u'']

    # Merge and drop duplication
    stopwords += slothlib_stopwords
    stopwords = list(set(stopwords))

    return stopwords

テスト

サンプルテキストの分かち書きとそこからストップワードを除去したものを表示。

text = '日本語の自然言語処理は本当にしんどい、と彼は十回言った。'
sw = nltkjp.stopwords(text)

words = nltkjp.word_tokenize(text)
print '分かち書き:'
for w in words:
    print w,
print
print

print '分かち書き(ストップワードを除去):'
for w in words:
    if not w in sw:
       print w,
print

単語の分割方法や動詞の基本形の必要性など、まだ課題はあるが、 ストップワードを取得するという目的は達成できた。

分かち書き:
日本語 の 自然 言語 処理 は 本当に しんどい 、 と 彼 は 十 回 言っ た 。

分かち書き(ストップワードを除去):
日本語 自然 言語 処理 言っ

MeCab-Pythonで分かち書きと形態素解析

MeCabでwakatiとchasenを使うと、それぞれ分かち書き形態素解析ができる。 これらの結果を利用しやすくするために、前者は単語のリスト、後者は単語と品詞のセットのリストとして取得できるようにした。

分かち書き

まずはMeCabの使い方から。最初は分かち書き。 パラメーターに"-Owakati"を指定する。

text = '日本語の自然言語処理は本当にしんどい。'
tagger = MeCab.Tagger("-Owakati")        
result = tagger.parse(text)
print result

結果。これをリストにする。

日本語 の 自然 言語 処理 は 本当に しんどい 。

分かち書き結果をリストで取得

メソッドは以下の通り。 文字コードとか、不要な情報とかを除去してリスト化している。

def word_tokenize(self, doc):
    """
    Execute wakati.
    """
    # Convert string type in case of unicode type
    doc_ex = doc
    if type(doc) is types.UnicodeType:
        doc_ex = doc.encode("utf-8")

    # Execute wakati
    tagger = MeCab.Tagger("-Owakati")        
    result = tagger.parse(doc_ex)

    # Make word list
    ws = re.compile(" ")
    words = [word.decode("utf-8") for word in ws.split(result)]
    if words[-1] == u"\n":
        words = words[:-1]

    return words

結果は次の通り。 NLTKJPは今回作った2つのメソッドのクラス名。 nltkjp.word_tokenize()は、nltk.tokenize.word_tokenize()の日本語版のつもり。

>>> text = '日本語の自然言語処理は本当にしんどい。'
>>> from nltkjp import NLTKJP
>>> nltkjp = NLTKJP()
>>> result = nltkjp.word_tokenize(text)
>>> print result
[u'\u65e5\u672c\u8a9e', u'\u306e', u'\u81ea\u7136', u'\u8a00\u8a9e', u'\u51e6\u7406', u'\u306f', u'\u672c\u5f53\u306b', u'\u3057\u3093\u3069\u3044', u'\u3002']
>>>
>>> for r in result:
>>>     print r,
日本語 の 自然 言語 処理 は 本当に しんどい 。

形態素解析

次に形態素解析。 パラメーターに"-Ochasen"を指定。

tagger = MeCab.Tagger('-Ochasen')
result = tagger.parseToNode(text)
while result:
    print '%-10s \t %-s' % (result.surface, result.feature)
    result = result.next

surfaceで単語、featureで解析結果が得られる。 解析結果のうち、今回は品詞だけ必要になる。

              BOS/EOS,*,*,*,*,*,*,*,*
日本語    名詞,一般,*,*,*,*,日本語,ニホンゴ,ニホンゴ
の          助詞,連体化,*,*,*,*,の,ノ,ノ
自然       名詞,形容動詞語幹,*,*,*,*,自然,シゼン,シゼン
言語       名詞,一般,*,*,*,*,言語,ゲンゴ,ゲンゴ
処理       名詞,サ変接続,*,*,*,*,処理,ショリ,ショリ
は          助詞,係助詞,*,*,*,*,は,ハ,ワ
本当に    副詞,一般,*,*,*,*,本当に,ホントウニ,ホントーニ
しんどい     形容詞,自立,*,*,形容詞・アウオ段,基本形,しんどい,シンドイ,シンドイ
。          記号,句点,*,*,*,*,。,。,。
             BOS/EOS,*,*,*,*,*,*,*,*

分かち書き結果を単語と品詞のセットのリストで取得

メソッドは以下の通り。 解析結果をカンマ(,)でsplitして最初だけ取得。 あと、解析結果の最初と最後がいらないので除去。

def word_and_class(self, doc):
    """
    Get word and class tuples list.
    """
    # Convert string type in case of unicode type
    doc_ex = doc
    if type(doc) is types.UnicodeType:
        doc_ex = doc.encode("utf-8")

    # Execute class analysis
    tagger = MeCab.Tagger('-Ochasen')
    result = tagger.parseToNode(doc_ex)

    # Extract word and class
    word_class = []
    while result:
        word = result.surface.decode("utf-8", "ignore")
        clazz = result.feature.split(',')[0].decode('utf-8', 'ignore')
        if clazz != u'BOS/EOS':
            word_class.append((word, clazz))
        result = result.next

        return word_class

結果は次の通り。

>>> result = nltkjp.word_and_class(text)
>>> for r in result:
>>>     print '%-10s \t %-s' % (r[0], r[1])
日本語            名詞
の              助詞
自然           名詞
言語           名詞
処理           名詞
は              助詞
本当に            副詞
しんどい         形容詞
。              記号

新規リポジトリー同期のためのGitHub Desktop

GitHub Desktopを使って、GitHubで作った新規リポジトリーをローカルと同期させるための手順をまとめた。

手順

  1. ローカルでファイル作成
  2. GitHubリポジトリー作成
  3. GitHubリポジトリーにファイルをアップロード
  4. GitHub DesktopでGitHubリポジトリーをクローン
  5. 以下、ローカルでプロジェクトの編集

1. ローカルでファイル作成

適当な場所に作成。 今回はsrcフォルダー内に幾つかのファイルを作成した前提で話を進める。

2. GitHubリポジトリー作成

GitHubリポジトリーで[New]ボタンを押して作成。 .gitignoreも同時に作成。

3. GitHubリポジトリーにファイルをアップロード

2で作ったリポジトリーの[Upload files]ボタンを押して、 1で作ったsrcフォルダーをドラッグ・アンド・ドロップ。

4. GitHub DesktopでGitHubリポジトリーをクローン

[+] > [Clone]で、GitHub上のリポジトリーが表示されるので、 2で作ったリポジトリーを適当な場所にクローン。 1のsrcフォルダーは削除して構わない。

5. 以下、ローカルでプロジェクトの編集

ローカルでファイルを変更・追加すると、GitHub Desktopの[Uncommitted Changes]に表示される。 Commitしたいファイルにチェックボタンを付けて、SummaryとDescriptionにコメントを記入して、 [Commit to master]ボタンを押す。 この時点ではローカル保存なので、右上の[Sync]ボタンを押すとGitHubと同期される。

ちなみにコードではなくデータを保存しているフォルダーも、更新があった際は[Uncommitted Changes]に表示される。 このようなCommitしなくていいフォルダーは、.gitignoreにdata_dir/のようにと書いておくと、 [Uncommitted Changes]に表示されなくなり、変更ファイルが分かりやすくなる。