ラベル 正規化 の投稿を表示しています。 すべての投稿を表示
ラベル 正規化 の投稿を表示しています。 すべての投稿を表示

2018年9月27日木曜日

pythonで全角⇔半角変換する。unicodedateを使う方法とdict登録で文字種ごとに設定する方法。(文字列操作,正規化,unicodedata,ユニコード)

過去のエネルギー基本計画と最新分を比較しようとしたところ、文字の全角半角にばらつきがあることに気がつきました。

平成30年 第5次エネルギー計画(最新版)
平成22年 第3次エネルギー計画


最新の第5次エネルギー基本計画では、略語、数字は全角(CO2、IoT、2030年など)、英単語・熟語、単位は半角(Energy Security、klなど)。第3次エネルギー基本計画では略語、数字(1文字の数字を除く)は半角(CO2、2002 年など)、一桁の数字は全角です(9月、9割など)。各文書内では文字種ごとに全角半角は統一されているようです。

このままでは自然言語処理をする際に別の単語と認識してしまうので統一していきます。

すべて正規化する場合(全角アルファベット・数字・記号→半角、半角カタカナ→全角)はunicodedateを使うと簡単です。

・unicodedateで全角を半角に
import unicodedata

text = u'ABCabc123カキク①(!%@#$¥~ ABCabc123カキク(!%@#$\~'
unicodedata.normalize("NFKC", text)
'ABCabc123カキクガギグ1(!%@#$¥~ ABCabc123カキクガギグ(!%@#$\\~'

unicodedate.nomalizeではformを指定して文字列を正規化します。ここではNFKC(Normalization Form Compatibility Composition)を指定しています。下記に詳しい解説があります。(あまり理解できていません)

Pythonドキュメント unicodedata — Unicode データベース
文字コード地獄秘話 第3話:後戻りの効かないUnicode正規化


unicodedateの正規化(NFKC)を使うと、全角アルファベット・数字・カタカナは半角になります。全角記号も半角になりますが、正規化されて、違う記号になったりするので要注意です。上の例では丸囲み数字①が1に、~は˜に、¥は¥¥になってしまいました。これでは困ることがあるので、違う方法を考えます。

参考にしたのはこちら↓

pythonで半角英数字から全角英数字へ変換


目的の文字のユニコードを辞書登録して、translateで変換するという方法。

ユニコードを調べるのは下記ページが便利です。逆引きもできます。

うにこ~ど2/


数字、英字のユニコード範囲:
---
数字半角[0-9] 0x30~0x39
数字全角[0-9] 0xFF10~0xFF19

英字半角・大文字[A-Z] 0x41~0x5A
英字全角・大文字[A-Z] 0xFF21~0xFF3A
英字半角・小文字[a-z] 0x61~0x7A
英字全角・小文字[a-z] 0xFF41~0xFF5A
---

ユニコードは計算(足し算、引き算)ができるので、これを利用してアルファベット、数字の一覧が生成できます。

・全角アルファベット一覧の作成
for ch in range(26):
    print(chr(0xFF21 + ch))
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

大文字全角Aのユニコード[0xFF21]に26までの数字を足して、AからZまでの英字を作りました。次にこれらを辞書に登録します。

・全角半角のセットを辞書に登録
upper = dict((0xFF21 + ch, 0x0041 + ch) for ch in range(26))
lower = dict((0xFF41 + ch, 0x0061 + ch) for ch in range(26))
number= dict((0xFF10 + ch, 0x0030 + ch) for ch in range(10))

t = {**upper, **lower,**number}

大文字(upper)、小文字(lower)、数字(number)をそれぞれ全角をキーに、半角を値にして辞書を作成し、ひとつに結合しました。変換が必要な文字種だけを辞書登録するのがポイント。辞書の結合は**を使っていますが、これができるのはPython3.5以上のようです。

Python で2つの辞書をマージした新しい辞書を作りたいとき


あとはtranslateで文字列を変換するだけ。

・全角英字、数字を半角に変換
text = u'ABCabc123カキク①(!%@#$¥~ ABCabc123カキク(!%@#$\~'
print(text.translate(t))
ABCabc123カキク①(!%@#$¥~ ABCabc123カキク(!%@#$\~

全角英字・数字のみが半角に変換されました(記号・半角カタカナはそのまま)。これを使うと、半角を全角に、全角数字のみを半角数字になどもできます。ただ、記号の場合はユニコードが連続していないので、目的のユニコードを探して辞書登録する必要があります。


参考:
Pythonドキュメント unicodedata — Unicode データベース
https://docs.python.jp/3/library/unicodedata.html
文字コード地獄秘話 第3話:後戻りの効かないUnicode正規化
http://tech.albert2005.co.jp/501/
うにこ~ど2
http://unicode.ninpou.jp/

pythonで半角英数字から全角英数字へ変換
http://cuio.blog2.fc2.com/blog-entry-2165.html
[Java] 全角半角変換(英数字の場合)
https://ameblo.jp/archive-redo-blog/entry-10376390355.html
Python で2つの辞書をマージした新しい辞書を作りたいとき
https://utgwkk.hateblo.jp/entry/2017/08/04/013507
//SyntaxHighlighter CDNより https://cdnjs.com/libraries/SyntaxHighlighter // 対応言語