言語処理100本ノック 2015の81本目「複合語からなる国名への対処」の記録です。
今回も前回の「コーパスの整形」に引き続き前処理系で、主な処理は正規表現を使った文字置換です。ただ、国名リストを作る部分で手作業で面倒なことをしています。そのせいでプログラミング自体は難しくないのですが、時間がかかってしまいました。
参考リンク
リンク | 備考 |
---|---|
081.複合語からなる国名への対処.ipynb | 回答プログラムのGitHubリンク |
素人の言語処理100本ノック:81 | 言語処理100本ノックで常にお世話になっています |
言語処理100本ノック 2015年版 (80~82) | 第9章は参考にしました |
環境
種類 | バージョン | 内容 |
---|---|---|
OS | Ubuntu18.04.01 LTS | 仮想で動かしています |
pyenv | 1.2.15 | 複数Python環境を使うことがあるのでpyenv使っています |
Python | 3.6.9 | pyenv上でpython3.6.9を使っています 3.7や3.8系を使っていないことに深い理由はありません パッケージはvenvを使って管理しています |
課題
第9章: ベクトル空間法 (I)
enwiki-20150112-400-r10-105752.txt.bz2は,2015年1月12日時点の英語のWikipedia記事のうち,約400語以上で構成される記事の中から,ランダムに1/10サンプリングした105,752記事のテキストをbzip2形式で圧縮したものである.このテキストをコーパスとして,単語の意味を表すベクトル(分散表現)を学習したい.第9章の前半では,コーパスから作成した単語文脈共起行列に主成分分析を適用し,単語ベクトルを学習する過程を,いくつかの処理に分けて実装する.第9章の後半では,学習で得られた単語ベクトル(300次元)を用い,単語の類似度計算やアナロジー(類推)を行う.
なお,問題83を素直に実装すると,大量(約7GB)の主記憶が必要になる. メモリが不足する場合は,処理を工夫するか,1/100サンプリングのコーパスenwiki-20150112-400-r100-10576.txt.bz2を用いよ.
今回は*「1/100サンプリングのコーパスenwiki-20150112-400-r100-10576.txt.bz2」*を使っています。
81. 複合語からなる国名への対処
英語では,複数の語の連接が意味を成すことがある.例えば,アメリカ合衆国は"United States",イギリスは"United Kingdom"と表現されるが,"United"や"States","Kingdom"という単語だけでは,指し示している概念・実体が曖昧である.そこで,コーパス中に含まれる複合語を認識し,複合語を1語として扱うことで,複合語の意味を推定したい.しかしながら,複合語を正確に認定するのは大変むずかしいので,ここでは複合語からなる国名を認定したい.
インターネット上から国名リストを各自で入手し,80のコーパス中に出現する複合語の国名に関して,スペースをアンダーバーに置換せよ.例えば,"United States"は"United_States","Isle of Man"は"Isle_of_Man"になるはずである.
「インターネット上から国名リストを各自で入手し」が面倒です・・・
回答
国名リスト作成
1. 国名リスト取得
適当にググって「Country codes/names」なるページが良さそうと思ったのですが、問題文にある"Isle of Man"がありません。
"Isle of Man"はISO 3166-1にあるようなので、[WikipediaのISO 3166-1]
(https://en.wikipedia.org/wiki/ISO_3166-1)からリストを取得しました。
つまり、以下の3つから国名リストを作成しています。
-
「Country codes/names」の
Short name
列 -
「Country codes/names」の
Official name
列 - [「Wikipedia ISO 3166-1」]
(https://en.wikipedia.org/wiki/ISO_3166-1)の`English short name`列
2. 先頭 the の除去
「Country codes/names」のOfficial name
列から取得した名前には先頭にthe
がついているものがあり、後に邪魔なので除去しました。
3. 重複削除
3つから取得しているので国名が一部重複しており、重複削除しました。
4. 単一名削除
今回のお題は「複合語からなる国名」であり、単一語の国名は不要です。EXCEL上で=COUNTIF(A1,"* *")
をして空白を挟んでいた国名を複合語と判断し、EXCEL関数結果が0の国名を除去しました。
※ノック96本目「国名に関するベクトルの抽出」で単一名も必要となるので、EXCELは残しておくと後が楽です。
5. マニュアル微調整
一部、そのまま使えないものがあり、マニュアルで微調整しました。手間がかかります・・・
下記が一例です。
元 | 変更後 |
---|---|
Bolivia (Plurinational State of) | Plurinational State of Bolivia |
Cocos (Keeling) Islands | Cocos Keeling Islands Cocos Keeling Cocos Islands Keeling Islands |
最終的に247個の国名ができました。
回答プログラム 081.複合語からなる国名への対処.ipynb
プログラムです。処理は短くたいしたことがありません(私はスキル不足で2から3時間ほどかけて作っていますが・・・)。
ただ、247個の国名の数だけ全文検索をして置換をするので、処理時間は約12分かかります。記事「言語処理100本ノック 2015年版 (80~82)」のようにsed
コマンド使えばもっと速いのでしょうか?
import re
# ファイルから改行コードを除去し、ソートのために単語数を先頭に付加
with open('./081.countries.txt') as countires:
country_num = [[len(country.split()), country.rstrip('\n')] for country in countires]
country_num.sort(reverse=True)
with open('./080.corpus.txt') as file_in:
body = file_in.read()
for i, country in enumerate(country_num):
print(i, country[1])
regex = re.compile(country[1], re.IGNORECASE)
body = regex.sub(country[1].replace(' ', '_'), body)
with open('./081.corpus.txt', mode='w') as file_out:
file_out.write(body)
回答解説
国名一覧ファイルを読み込み、単語数をリストに付加して降順ソートしています。これは"United States of America"に対して先に単語数が少ない"United_States"を使って置換し、"United_States of America"としないためです。
# ファイルから改行コードを除去し、ソートのために単語数を先頭に付加
with open('./081.countries.txt') as countires:
country_num = [[len(country.split()), country.rstrip('\n')] for country in countires]
country_num.sort(reverse=True)
正規表現ではre.INGNORECASE
とすることで、大文字小文字を識別しないで置換対象にしています(この揺らぎが役に立つかは確認していないです)。
regex = re.compile(country[1], re.IGNORECASE)