日本語を文節単位で分かち書きする【Python】【MeCab】

#はじめに
Pythonで、単語（品詞）の単位ではなく、文節単位で分かち書き（下記表の一番下の行）する関数をつくりました。

|元文  |今日はよく寝ました|
|---|---|
|単語分かち書き|今日/は/よく/寝/まし/た|
|文節分かち書き|今日は/よく/寝ました|
#環境
- macOS Catalina 10.15.4
- python3.8.0

下記を参考にMeCab、mecab-ipadic-neologd、mecab-python3をインストールしました。
https://qiita.com/taroc/items/b9afd914432da08dafc8

下記を参考にpythonからimportするMeCabのデフォルト辞書をmecab-ipadic-neologdにしておきました。
https://qiita.com/shimajiroxyz/items/3b475da3b6cd850d2c22

なお、設定が面倒な人は、mecab-ipadic-neologdではなく、初期インストール時の辞書を使ってもおおよそうまくいくと思います。ただし、サ変接続周りの処理の精度が少し下がります。
#方針
文節の切れ目の判定にはMeCab（辞書はmecab-ipadic-neologd）の形態素解析の結果を利用します。MeCabの出力結果はデフォルトでは下記のようになっています。

```
表層形\t品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用型,活用形,原形,読み,発音
```

基本は<b>品詞が名詞、動詞、副詞、感動詞、形容詞、形容動詞、連体詞、接頭詞のとき、その単語の始まりを文節の切れ目と定義</b>しました。

ただし以下の単語の直前は例外として、文節の切れ目とはしないようにしました。

|種類|例|判断方法|
|---|---|---|
|接尾辞|「痛さ」の「さ」など|品詞が「名詞」かつ品詞細分類に「接尾」が含まれている|
|接頭詞の直後の単語|「お美しい」の「美しい」など（「お」が接頭詞）|直前の単語の品詞が「接頭詞」である|
|サ変接続名詞の直後のサ変接続動詞|「勉強する」の「する」など|品詞が「動詞」、かつ、活用型が「サ変・スル」、かつ、直前の単語の品詞細分類に「サ変接続」が含まれている（注１）|
|非自立な名詞（注２）|動詞や形容詞などの直後の「もの」「こと」や、「やったのか？」の(「もの・こと」という意味の）「の」など|品詞が「名詞」かつ品詞細分類に「非自立」が含まれている|
|非自立な動詞（注２）|「やっている」の「いる」など|品詞が「動詞」かつ品詞細分類に「非自立」が含まれている|

注１：mecab-ipadic-neologdではなく、mecabの初期辞書を使うと、記号が「サ変接続」になって、その周辺の処理の精度が悪くなることがあるので注意してください。
注２：非自立な名詞、非自立な動詞は文法的には文節の切れ目にするのが正しいのですが、出力結果を眺めたときに細かく切れすぎて、違和感があったので、今回は文節の切れ目としないことにしました。
#コード
```Python
import MeCab

m = MeCab.Tagger('') #mecabのtagger objectの宣言

def bunsetsuWakachi(text):
    m_result = m.parse(text).splitlines()
    m_result = m_result[:-1] #最後の1行は不要な行なので除く
    break_pos = ['名詞','動詞','接頭詞','副詞','感動詞','形容詞','形容動詞','連体詞'] #文節の切れ目を検出するための品詞リスト
    wakachi = [''] #分かち書きのリスト
    afterPrepos = False #接頭詞の直後かどうかのフラグ
    afterSahenNoun = False #サ変接続名詞の直後かどうかのフラグ
    for v in m_result:
        if '\t' not in v: continue
        surface = v.split('\t')[0] #表層系
        pos = v.split('\t')[1].split(',') #品詞など
        pos_detail = ','.join(pos[1:4]) #品詞細分類（各要素の内部がさらに'/'で区切られていることがあるので、','でjoinして、inで判定する)
        #この単語が文節の切れ目とならないかどうかの判定
        noBreak = pos[0] not in break_pos
        noBreak = noBreak or '接尾' in pos_detail
        noBreak = noBreak or (pos[0]=='動詞' and 'サ変接続' in pos_detail)
        noBreak = noBreak or '非自立' in pos_detail #非自立な名詞、動詞を文節の切れ目としたい場合はこの行をコメントアウトする
        noBreak = noBreak or afterPrepos
        noBreak = noBreak or (afterSahenNoun and pos[0]=='動詞' and pos[4]=='サ変・スル')
        if noBreak == False:
            wakachi.append("")
        wakachi[-1] += surface
        afterPrepos = pos[0]=='接頭詞'
        afterSahenNoun = 'サ変接続' in pos_detail
    if wakachi[0] == '': wakachi = wakachi[1:] #最初が空文字のとき削除する
    return wakachi
```

出力は下記のような感じです。

```Python
>>> bunsetsuWakachi('今日はよく寝ました')
['今日は', 'よく', '寝ました']
>>> bunsetsuWakachi('あなたはとても良く勉強しているのですね')
['あなたは', 'とても', '良く', '勉強しているのですね']
>>> bunsetsuWakachi('私がすることを覚えておいてください')
['私が', 'することを', '覚えておいてください']
>>> bunsetsuWakachi('あらゆる現実を自分の方へ捻じ曲げたのだ')
['あらゆる', '現実を', '自分の方へ', '捻じ', '曲げたのだ']
>>> bunsetsuWakachi('とてもお美しいお着物ですね')
['とても', 'お美しい', 'お着物ですね']
>>> bunsetsuWakachi('ありえん良さみが深い')
['ありえん', '良さみが', '深い']
>>> bunsetsuWakachi('おまえの次のセリフは「赤子を殺すより楽な作業よ」だ')
['おまえの', '次の', 'セリフは「', '赤子を', '殺すより', '楽な', '作業よ」だ']
```
非自立な名詞や動詞の扱いは少し悩ましいです。「することを」が一つの文節になっているのはスッキリしていて良い気がしますが、「勉強しているのですね」や「覚えておいてください」は一つの文節としては長すぎる気もします。実際のアプリケーションに応じて、非自立な名詞、動詞の扱いを決めたほうがよいかもしれません。