pythonによる文字列の正規化

テキストマイニングなどを行うためには文書、文、単語などの文字列の正規化が重要です。
単語の大文字小文字の統一、半角全角の統一などをする必要があります。
文字列の正規化のために利用しているpythonコードを以下に書いておきます。
今後増える可能性もあります。

実行環境

Ubuntu 10.04 64ビット
python 2.6.5

unicode型に変換する

def unicode_ignore_invalid_char(text):
    if isinstance(text, str):
        return text.decode('utf-8', 'ignore')
    return text

変換不能な文字列を無視してstr型からunicode型に変換する。

str型に変換する

def str_ignore_invalid_char(text):
    if isinstance(text, unicode):
        return text.encode('utf-8', 'ignore')
    return text

変換不能な文字列を無視してunicode型からstr型に変換する。

入出力の文字列型を統一する

from functools import wraps

def consistent_texttype(function):
    @wraps(function)
    def _consistent_texttype(*args, **kwargs):
        assert(1 <= len(args))
        input_text = args[0]
        is_unicode = False
        if isinstance(input_text, unicode):
            is_unicode = True
        elif not isinstance(input_text, str):
            is_unicode = isinstance(input_text[0], unicode)  # for collections
        output_text = function(*args, **kwargs)
        if isinstance(output_text, unicode) or isinstance(output_text, str):
            if is_unicode:
                return unicode_ignore_invalid_char(output_text)
            return str_ignore_invalid_char(output_text)
        if is_unicode:
            return map(unicode_ignore_invalid_char, output_text)
        return map(str_ignore_invalid_char, output_text)
    return _consistent_texttype

入力文字列がstr型であるとき出力文字列もstr型にし、入力文字列がunicode型であるとき出力文字列もunicode型にするデコレータ。

unicodeを正規化する

import unicodedata

@consistent_texttype
def normalize_unicode(text, form='NFKC'):
    assert(form in ('NFC', 'NFKC', 'NFD', 'NFKD'))
    unicode_text = unicode_ignore_invalid_char(text)
    normalized_text = unicodedata.normalize(form, unicode_text)
    return normalized_text

半角カタカナを全角カタカナに変換したりする。
例えば㌻をページ、ハンカクカナをハンカクカナに変換する。

HTMLエンティティを変換する

from BeautifulSoup import BeautifulSoup

@consistent_texttype
def unescape_entities_with_beautifulsoup(htmltext, prettify=False):
    soup = BeautifulSoup(htmltext, convertEntities=BeautifulSoup.HTML_ENTITIES)
    if prettify:
        return soup.prettify()
    return soup.__repr__()

BeautifulSoupを利用してHTMLエンティティを変換する。
例えば&gt;を>に変換する。

from BeautifulSoup import BeautifulStoneSoup

@consistent_texttype
def unescape_entities_with_beautifulstonesoup(htmltext, prettify=False):
    soup = BeautifulStoneSoup(htmltext,
                              convertEntities=BeautifulStoneSoup.HTML_ENTITIES)
    if prettify:
        return soup.prettify()
    return soup.__repr__()

BeautifulStoneSoupを利用してHTMLエンティティを変換する。
BeautifulSoupを利用した場合と同じかもしれない。

from htmlentitydefs import name2codepoint
import re

# derived from BeautifulSoup
# __author__ = "Leonard Richardson (leonardr@segfault.org)"
# __version__ = "3.1.0.1"
# __copyright__ = "Copyright (c) 2004-2009 Leonard Richardson"
# __license__ = "New-style BSD"
def _unescape_entity(match):
    x = match.group(1)
    if x in name2codepoint:
        return unichr(name2codepoint[x])
    elif 0 < len(x) and x[0] == '#':
        if 1 < len(x) and x[1] == 'x':
            return unichr(int(x[2:], 16))
        return unichr(int(x[1:]))
    return u'&{0};'.format(x)


@consistent_texttype
def unescape_entities(htmltext):
    unicode_htmltext = unicode_ignore_invalid_char(htmltext)
    unescaped_text = re.sub(u'&(#\d+|#x[0-9a-fA-F]+|\w+);',
                            _unescape_entity, unicode_htmltext)
    assert(isinstance(unescaped_text, unicode))
    return unescaped_text

BeautifulSoupのHTMLエンティティ変換部分を抽出し、少し変更を加えたもの。
HTMLエンティティ変換のためだけにBeautifulSoupを利用するのは高価すぎると考えるときはこちらを利用する。
BeautifulSoupはNew-style BSDライセンスです。

語幹を抽出する(ステミング)

import nltk

@consistent_texttype
def stem_term(term, porter=True):
    if porter:
        return nltk.PorterStemmer().stem(term)
    return nltk.LancasterStemmer().stem(term)

英語用。例えばinitial, initializeをinitiにする。

見出し語化・レンマ化(lemmatization)

import nltk
from nltk.corpus import wordnet

@consistent_texttype
def lemmatize_term(term, pos=None):
    if pos is None:
        synsets = wordnet.synsets(term)
        if not synsets:
            return term
        pos = synsets[0].pos
        if pos == wordnet.ADJ_SAT:
            pos = wordnet.ADJ
    assert(pos in (wordnet.NOUN, wordnet.VERB, wordnet.ADJ, wordnet.ADV))
    return nltk.WordNetLemmatizer().lemmatize(term, pos=pos)

英語用。WordNetを用いて単語の見出し語化を行う。
例えばis, areをbeに、potatosをpotatoにする。
品詞(pos)の指定がなければsynsetsのうち、一番最初に現れる品詞を使用する。

小文字にする

text.lower()

もしくは、

import string

def lower_text(text):
    return string.lower(text)

大文字にする

text.upper()

もしくは、

import string

def upper_text(text):
    return string.upper(text)

先頭のみ大文字にする

text.capitalize()

もしくは、

import string

def capitalize_text(text):
    return string.capitalize(text)

参考文献

入門 自然言語処理

入門 自然言語処理