2018年03月30日

言語処理100本ノックでPython入門 #30 - mecabを使って形態素解析

   このエントリーをはてなブックマークに追加 Clip to Evernote
今日から言語処理100本ノック 2015「第4章 形態素解析」に入ります。

僕にとっては未知の分野、どんな問題なのか楽しみです。

■ 問題

夏目漱石の小説『吾輩は猫である』の文章(neko.txt)をMeCabを使って形態素解析し,その結果をneko.txt.mecabというファイルに保存せよ.このファイルを用いて,以下の問に対応するプログラムを実装せよ. なお,問題37, 38, 39はmatplotlibもしくはGnuplotを用いるとよい.

30. 形態素解析結果の読み込み
形態素解析結果(neko.txt.mecab)を読み込むプログラムを実装せよ.ただし,各形態素は表層形(surface),基本形(base),品詞(pos),品詞細分類1(pos1)をキーとするマッピング型に格納し,1文を形態素(マッピング型)のリストとして表現せよ.
第4章の残りの問題では,ここで作ったプログラムを活用せよ.


■ mecabのインストール

Windowsの場合は、http://taku910.github.io/mecab/ から、mecab-0.996.exe をダウンロードしてインストールするだけみたいです。Unixdだと自分でビルドするようなことが書いてありましたが、Macの場合は、brewコマンドでインストールできるみたいです。

で、以下のコマンドを投入。
brew install mecab
 
でも、失敗、どうもXCodeとhomebrewのバージョンが古いみたいなメッセージがでました。

で、まずは、XCodeをAppStoreからインストールします。
けっこうな時間がかかりました。
インストールしたあと、
sudo xcodebuild -license accept
でライセンスを承認。

その後、以下のコマンドでbrewをアップグレード
brew upgrade

brewが最新のものになったので、再度mecabと辞書をインストールします。 

brew install mecab 
brew install mecab-ipadic

これで準備完了です。

■ mecabファイルの作成

Pythonからmecabを呼び出すこともできるようですが、ここではコマンドラインから、neko.txtを読み込み、neko.txt.mecabを作成します。 neko.txtのあるフォルダに移動し、以下のコマンドを実行

mecab neko.txt -o neko.txt.mecab

neko.txt.mecabを作成すれば良いだけなので、これはすんなり行きました。 以下のようなファイルが作成されます。一部だけ抜粋して掲載
一    名詞,数,*,*,*,*,一,イチ,イチ
EOS
EOS
     記号,空白,*,*,*,*, , , 
吾輩    名詞,代名詞,一般,*,*,*,吾輩,ワガハイ,ワガハイ
は    助詞,係助詞,*,*,*,*,は,ハ,ワ
猫    名詞,一般,*,*,*,*,猫,ネコ,ネコ
で    助動詞,*,*,*,特殊・ダ,連用形,だ,デ,デ
ある    助動詞,*,*,*,五段・ラ行アル,基本形,ある,アル,アル
。    記号,句点,*,*,*,*,。,。,。
EOS
名前    名詞,一般,*,*,*,*,名前,ナマエ,ナマエ
は    助詞,係助詞,*,*,*,*,は,ハ,ワ
まだ    副詞,助詞類接続,*,*,*,*,まだ,マダ,マダ
無い    形容詞,自立,*,*,形容詞・アウオ段,基本形,無い,ナイ,ナイ
。    記号,句点,*,*,*,*,。,。,。
EOS
EOS
     記号,空白,*,*,*,*, , , 
どこ    名詞,代名詞,一般,*,*,*,どこ,ドコ,ドコ
で    助詞,格助詞,一般,*,*,*,で,デ,デ
生れ    動詞,自立,*,*,一段,連用形,生れる,ウマレ,ウマレ
た    助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
か    助詞,副助詞/並立助詞/終助詞,*,*,*,*,か,カ,カ
とんと    副詞,一般,*,*,*,*,とんと,トント,トント
見当    名詞,サ変接続,*,*,*,*,見当,ケントウ,ケントー
が    助詞,格助詞,一般,*,*,*,が,ガ,ガ
つか    動詞,自立,*,*,五段・カ行イ音便,未然形,つく,ツカ,ツカ
ぬ    助動詞,*,*,*,特殊・ヌ,基本形,ぬ,ヌ,ヌ
。    記号,句点,*,*,*,*,。,。,。
EOS
■ mecabファイルの形式

http://taku910.github.io/mecab/ を読むと、mecabの出力フォーマットは、以下の通りです。
表層形\t品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用型,活用形,原形,読み,発音
これが複数行続いています。文の終わりには、EOSという行があります。

問題にある「基本形」がどの項目を指しているのかわからないのですが、「原形」ってのがあるから、たぶんこのことだと思います。

mecabを使ってみて、株式会社平和情報センターが開発したHAPPINESSという日本語を分かち書きをするソフトを使って文章のなかからキーワードを抽出するプログラムをはるか昔に書いたことを思い出しました。 その頃のプラットホームはホストコンピュータでした。懐かしいなー。

■ Pythonのコード
import re

def analyze():
    lines = []
    sentence = []
    with open('neko.txt.mecab', 'r', encoding='utf8') as fin:
        for line in fin:
            words = re.split(r'\t|,|\n', line)
            if words[0] == 'EOS':
                if sentence:
                    lines.append(sentence)
                    sentence = []
                continue
            sentence.append({
                "surface": words[0],
                "base": words[7],
                "pos": words[1],
                "pos1": words[2],
            })
    return lines

def main():
    article = analyze()
    print(article[0])
    print()
    print(article[1])
    print()
    print(article[2])

if __name__ == '__main__':
    main()

■ 採用するデータの構造


関数analyzeが今回の目的のコードで、ここで問題が求めているデータを作成しています。


問題文のマッピング型ってのは、辞書形式のことらしいです。なので、ファイルの1行をマッピング型に格納し、1文をリストに格納することとします。

問題文は、「1文を形態素(マッピング型)のリストとして表現せよ」とありますが、ファイルには複数文のデータがあるわけだから、さらにこれをリストに格納するってことなのだと判断しました。
文章:リスト型 (文が複数格納される)
    1文:リスト型 (形態素が複数格納される)
        形態素:マッピング型 (surface,base, pos, pos1をキーとする)

■ 形態素のマッピング型を作成


読み込んだ1行の分割は、正規表現ライブラリの re.splitを使って分割します。
words = re.split(r'\t|,|\n', line)
この1行が1形態素になります。 これをsurface, base, pos, pos1をキーとするマッピング型にするということで、たぶん以下のようなオブジェクトにすればいいのだと思います。
{
    "surface": words[0],
    "base": words[7],
    "pos": words[1],
    "pos1": words[2],
}
C#だと、こういった場合は、4つのプロパティを持つクラスを定義することになると思いますが、Pythonだとこうするのが普通なのかな? JavaScriptのオブジェクトっぽいです。

このオブジェクトを1文を表すsentenceリストにappendメソッドで追加します。 

■ 文単位に分割する


入力データに'EOS'が来たら、それまでため込んでいた1文のデータ(sentence)を、linesに追加。
最初は以下のように書きました。
# if len(sentense) > 0: って書いたらpylintに怒られた
if sentence:
    lines.append(sentence)
    sentence.clear()  # 次の文を格納するために初期化
continue
でも、実行してみると、次のような結果になったので「なんで?」ってなってしまいました。
[]
[]
[]

そりゃそうですよね。
linesにアペンドしたものをクリアしちゃってるんだから。
なので、以下のように書き直します。

if sentence:
    lines.append(sentence)
    sentence = []
continue
これでOKです。
以下のようにオブジェクトの複製を作る方法もありですが、ちょっと効率が悪そうかな。
if sentence:
    lines.append(sentence.copy())
    sentence.clear()
continue


■ 結果

作成したプログラムでは、確認のために最初の3文の形態素を出力しています。

[{'surface': '一', 'base': '一', 'pos': '名詞', 'pos1': '数'}]

[{'surface': '\u3000', 'base': '\u3000', 'pos': '記号', 'pos1': '空白'}, {'surface': '吾輩', 'base': '吾輩', 'pos': '名詞', 'pos1': '代名詞'}, {'surface': 'は', 'base': 'は', 'pos': '助詞', 'pos1': '係助詞'}, {'surface': '猫', 'base': '猫', 'pos': '名詞', 'pos1': '一般'}, {'surface': 'で', 'base': 'だ', 'pos': '助動詞', 'pos1': '*'}, {'surface': 'ある', 'base': 'ある', 'pos': '助動詞', 'pos1': '*'}, {'surface': '。', 'base': '。', 'pos': '記号', 'pos1': '句点'}]

[{'surface': '名前', 'base': '名前', 'pos': '名詞', 'pos1': '一般'}, {'surface': 'は', 'base': 'は', 'pos': '助詞', 'pos1': '係助詞'}, {'surface': 'まだ', 'base': 'まだ', 'pos': '副詞', 'pos1': '助詞類接続'}, {'surface': '無い', 'base': '無い', 'pos': '形容詞', 'pos1': '自立'}, {'surface': '。', 'base': '。', 'pos': '記号', 'pos1': '句点'}]
  

Posted by gushwell at 21:40Comments(0)

2018年03月25日

言語処理100本ノックでPython入門 #29 - HttpGetでJSONデータを取得

   このエントリーをはてなブックマークに追加 Clip to Evernote
今日は、言語処理100本ノック 2015の第3章最後の問題 No.29です。

■ 問題

問題20で作成したファイルを入力として、以下の問題を解きます。
29. 国旗画像のURLを取得する
テンプレートの内容を利用し,国旗画像のURLを取得せよ.(ヒント: MediaWiki APIのimageinfoを呼び出して,ファイル参照をURLに変換すればよい)

■ Pythonのコード
import json
import re
import urllib.request
import urllib.parse

def readArticle(filename):
    with open(filename, 'r', encoding='utf8') as fin:
        return fin.read()

def getImageFileName(article):
    basicInfo = re.search(r'\{\{基礎情報(.+?)\}\}\n', article, re.DOTALL).group(1)
    pat = r'^\|国旗画像\s*=\s*(.+?)\n'
    reg = re.compile(pat, re.MULTILINE | re.DOTALL)
    m = reg.search(basicInfo)
    return m.group(1)

def getImage(finename):
    url = 'https://ja.wikipedia.org/w/api.php?' \
        + 'action=query' \
        + '&format=json' \
        + '&titles=File:' + urllib.parse.quote(finename) \
        + '&prop=imageinfo' \
        + '&iiprop=url'
    with urllib.request.urlopen(url) as res:
        data = json.loads(res.read().decode())
        return data['query']['pages']['-1']['imageinfo'][0]['url']


def main():
    # england-article.txtは、問題20(nlp20.py)で作成したファイル
    article = readArticle('england-article.txt')  
    fname = getImageFileName(article)
    print(getImage(fname))

if __name__ == '__main__':
    main()

■ 今回のトピック

ソースコードの1文を複数行に分ける

ソースコード上で、複数行にわたる文字列を連結するには、行末に \ が必要。
url = 'https://ja.wikipedia.org/w/api.php?' \
    + 'action=query' \
    + '&format=json' \
    + '&titles=File:' + urllib.parse.quote(finename) \
    + '&prop=imageinfo' \
    + '&iiprop=url'

最初は、この \ を書かなかったら、
E0001:unexpected indent

が表示されてしばらく意味わかりませんでした。

文字列連結にかかわらず、本来1行のものを複数行に分ける場合は、\ が必要ということらしいです。 そういえば、No13 で既に使ってました。すっかり忘れています。

URLエンコード
import urllib.parse

encoded = urllib.parse.quote(s)

文字列sをurlエンコードします。
quoteというメソッド名にものすごい違和感を感じるのですが、それは僕の英語力がないせいでしょうか?


Http Get
import urllib.request

with urllib.request.urlopen(url) as res:
    bytes = res.read()

指定したurlからHttp Getでデータを取得します。readメソッドで読み込んだデータはbyte配列です。


byte配列をstringにする


byte配列をstringにするには、decodeメソッドを呼び出します。

with urllib.request.urlopen(url) as res:
    data = json.loads(res.read().decode())

これで得た文字列を、json.loadsに渡し、jsonを辞書形式に変換します。


ちなみに、取得したjsonデータは以下の通りです。

{
    'query': {
        'normalized': [
            {'from': 'File:Flag of the United Kingdom.svg', 
             'to': 'ファイル:Flag of the United Kingdom.svg'
            }
        ], 
        'pages': {
            '-1': {
                'missing': '', 
                'known': '', 
                'title': 'ファイル:Flag of the United Kingdom.svg', 
                'ns': 6, 
                'imagerepository': 'shared', 
                'imageinfo': [
                    {
                        'descriptionshorturl': 'https://commons.wikimedia.org/w/index.php?curid=347935', 
                        'descriptionurl': 'https://commons.wikimedia.org/wiki/File:Flag_of_the_United_Kingdom.svg', 
                        'url': 'https://upload.wikimedia.org/wikipedia/commons/a/ae/Flag_of_the_United_Kingdom.svg'
                    }
                ]
            }
        }
    }, 
    'continue': {
        'continue': '||', 
        'iistart': '2007-09-03T09:51:34Z'
    }
}
このデータの中から、以下のようにして国旗画像のURLを取得します。階層構造の深いところにあるので面倒です。

js['query']['pages']['-1']['imageinfo'][0]['url']


■ 結果
https://upload.wikimedia.org/wikipedia/commons/a/ae/Flag_of_the_United_Kingdom.svg



これで、第3章が終わりました。
まだ3割。先が長いです。 そもそも僕の実力で最後まで解くことができるのでしょうか。 モチベーションが継続するかも問題だなー。  
Posted by gushwell at 22:46Comments(0)

2018年03月21日

言語処理100本ノックでPython入門 #28 - いろいろな正規表現

   このエントリーをはてなブックマークに追加 Clip to Evernote

今日は、言語処理100本ノック 2015の問題28です。

■ 問題

問題20で作成したファイルを入力として、以下の問題を解きます。
28. MediaWikiマークアップの除去 27の処理に加えて,テンプレートの値からMediaWikiマークアップを可能な限り除去し,国の基本情報を整形せよ.

■ Pythonのコード
import re

def readArticle(filename):
    with open(filename, 'r', encoding='utf8') as fin:
        return fin.read()

def getBasicInfo(article):
    basicInfo = re.search(r'\{\{基礎情報(.+?)\}\}\n', article, re.DOTALL).group(1)
    pat = r'^\|(.+?)\s*=\s*(.+?)(?<!<br/>)\n'
    reg = re.compile(pat, re.MULTILINE | re.DOTALL)
    wdict = {}
    for k, v in reg.findall(basicInfo):
        v = re.sub(r"''('|''')([^']+?)''+", r"\2", v)
        v = re.sub(r"\[\[(?:[^\]]+\|)?([^:]+?)\]\]", r"\1", v)
        v = re.sub(r"\[http(?:[^\]]+? )?(.+?)\]", r"\1", v)
        v = re.sub(r"#REDIRECT \[\[([^\[]+?)\]\]", r"\1", v)
        v = re.sub(r"^===*\s*(.+)\s*=", r"\1", v)
        v = re.sub(r"^(?:\*|#|;)+\s*([^\n]+?)$", r"\1", v, flags=re.MULTILINE)
        v = re.sub(r"{{(?:[^\{]+\|)?([^\|]+?)}}", r"\1", v)
        v = re.sub(r"<[^>]+>", r" ", v)
        wdict[k] = v

    return wdict


def main():
    # england-article.txtは、問題20(nlp20.py)で作成したファイル
    article = readArticle('england-article.txt')    
    dic = getBasicInfo(article)
    dic = sorted(dic.items(), key=lambda x: x[0])
    for item, v in dic:
        print(item, ':', v)

if __name__ == '__main__':
    main()


■ コードについて一言

「可能な限り除去し」ということで、わかる範囲で除去してみました。これまでの、正規表現の総復習みたいなコードですね。かなり力業的なコードです。 ひとつ一つの説明は省きますが、一つだけ、flags=re.MULTILINEを付けているものがあります。これは、記事の中にある箇条書きに対応するためです。
 v = re.sub(r"^(?:\*|#|;)+\s*([^\n]+?)$", r"\1", v, flags=re.MULTILINE)
compileしないで、 MULTILINEオプションを指定しています。


■ 結果
GDP/人 : 36,727 
GDP値 : 2兆3162億 
GDP値MER : 2兆4337億 
GDP値元 : 1兆5478億 IMF>Data and Statistics>World Economic Outlook Databases>By Countrise>United Kingdom 
GDP統計年 : 2012
GDP統計年MER : 2012
GDP統計年元 : 2012
GDP順位 : 6
GDP順位MER : 5
ISO 3166-1 : GB / GBR
ccTLD : .uk / .gb 使用は.ukに比べ圧倒的少数。 
人口値 : 63,181,775 United Nations Department of Economic and Social Affairs>Population Division>Data>Population>Total Population 
人口大きさ : 1 E7
人口密度値 : 246
人口統計年 : 2011
人口順位 : 22
位置画像 : Location_UK_EU_Europe_001.svg
元首等氏名 : エリザベス2世
元首等肩書 : 女王
公式国名 : United Kingdom of Great Britain and Northern Ireland 英語以外での正式国名: 
An Rioghachd Aonaichte na Breatainn Mhor agus Eirinn mu Thuath(スコットランド・ゲール語) 
Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon(ウェールズ語) 
Riocht Aontaithe na Breataine Moire agus Tuaisceart na hEireann(アイルランド語) 
An Rywvaneth Unys a Vreten Veur hag Iwerdhon Gledh(コーンウォール語) 
Unitit Kinrick o Great Breetain an Northren Ireland(スコットランド語) 
Claught Kangrick o Docht Bratain an Norlin Airlann、Unitet Kangdom o Great Brittain an Norlin Airlann(アルスター・スコットランド語) 
公用語 : 英語(事実上)
国旗画像 : Flag of the United Kingdom.svg
国歌 : 神よ女王陛下を守り給え
国章リンク : (国章)
国章画像 : イギリスの国章
国際電話番号 : 44
夏時間 : +1
建国形態 : 建国
日本語国名 : グレートブリテン及び北アイルランド連合王国
時間帯 : ±0
最大都市 : ロンドン
標語 : Dieu et mon droit (フランス語:神と私の権利)
水面積率 : 1.3%
注記 :  
略名 : イギリス
確立年月日1 : 927年/843年
確立年月日2 : 1707年
確立年月日3 : 1801年
確立年月日4 : 1927年
確立形態1 : イングランド王国/スコットランド王国 (両国とも1707年連合法まで)
確立形態2 : グレートブリテン王国建国 (1707年連合法)
確立形態3 : グレートブリテン及びアイルランド連合王国建国 (1800年連合法)
確立形態4 : 現在の国号「グレートブリテン及び北アイルランド連合王国」に変更
通貨 : UKポンド (&pound;)
通貨コード : GBP
面積値 : 244,820
面積大きさ : 1 E11
面積順位 : 76
首相等氏名 : デーヴィッド・キャメロン
首相等肩書 : 首相
首都 : ロンドン
  
Posted by gushwell at 21:00Comments(0)

2018年03月19日

言語処理100本ノックでPython入門 #27 - 正規表現の(?:……)

   このエントリーをはてなブックマークに追加 Clip to Evernote
今日は、言語処理100本ノック 2015の問題27です。

■ 問題

問題20で作成したファイルを入力として、以下の問題を解きます。

27. 内部リンクの除去
26の処理に加えて,テンプレートの値からMediaWikiの内部リンクマークアップを除去し,テキストに変換せよ(参考: マークアップ早見表).


内部リンクマークアップを除去するとは、
[[記事名]]  ==> 記事名
[[記事名|表示文字]  ==> 表示文字
[[記事名#節名|表示文字]]  ==> 表示文字
と変換せよということだと解釈。 なので、
[[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]
といった、ファイル: と先頭にあるものなどは、対象外としました。


■ Pythonのコード
import json
import re

def readArticle(filename):
    with open(filename, 'r', encoding='utf8') as fin:
        return fin.read()

def getBasicInfo(article):
    basicInfo = re.search(r'\{\{基礎情報(.+?)\}\}\n', article, re.DOTALL).group(1)
    pat = r'^\|(.+?)\s*=\s*(.+?)(?<!<br/>)\n'
    reg = re.compile(pat, re.MULTILINE | re.DOTALL)
    wdict = {}
    for k, v in reg.findall(basicInfo):
        v1 = re.sub(r"''('|''')([^']+?)''+", r"\2", v)
        v2 = re.sub(r"\[\[(?:[^:\]]+?\|)?([^:]+?)\]\]", r"\1", v1)
        wdict[k] = v2
    return wdict

def main():
    # england-article.txtは、問題20(nlp20.py)で作成したファイル
    article = readArticle('england-article.txt')    
    dic = getBasicInfo(article)
    dic = sorted(dic.items(), key=lambda x: x[0])
    for item, v in dic:
        print(item, ':', v)

if __name__ == '__main__':
    main()

■ コード内の正規表現の説明
v2 = re.sub(r"\[\[(?:[^:\]]+?\|)?([^:]+?)\]\]", r"\1", v1)
の簡単な説明を。

まずは、正規表現の全体像。
r"\[\[……\]\]"
これで、内部リンクと一致させます。

途中の
(?:[^:\[]+?\|)?
は、削除して良い文字列を示しています。
(?:……)
は、後からグループ参照をしないという意味。これで、/1, /2 などは対象外になります。

[^:\[]+?
は、: と [ 以外の文字が1以上連続(+?なので最短一致)する文字列です。

その後に、|が続くパターンを示しています。 最後に?付けてるので、これが、0回か1回でマッチします。

その後の
([^:]+?)
は、:以外の任意の文字が1以上連続するパターンで、これが残すべき文字列です。\1 で参照できます。

前半の (?:……)が有効に働いています。 ?: 指定をしておかないと、\1 か \2 かのどちらを残したらよいかわからないです。


■ 結果

GDP/人 : 36,727<ref name="imf-statistics-gdp" />
GDP値 : 2兆3162億<ref name="imf-statistics-gdp" />
GDP値MER : 2兆4337億<ref name="imf-statistics-gdp" />
GDP値元 : 1兆5478億<ref name="imf-statistics-gdp">[http://www.imf.org/external/pubs/ft/weo/2012/02/weodata/weorept.aspx?pr.x=70&;pr.y=13&sy=2010&ey=2012&scsm=1&ssd=1&sort=country&ds=.&br=1&c=112&s=NGDP%2CNGDPD%2CPPPGDP%2CPPPPC&grp=0&a= IMF>Data and Statistics>World Economic Outlook Databases>By Countrise>United Kingdom]</ref>
GDP統計年 : 2012
GDP統計年MER : 2012
GDP統計年元 : 2012
GDP順位 : 6
GDP順位MER : 5
ISO 3166-1 : GB / GBR
ccTLD : .uk / .gb<ref>使用は.ukに比べ圧倒的少数。</ref>
人口値 : 63,181,775<ref>[http://esa.un.org/unpd/wpp/Excel-Data/population.htm United Nations Department of Economic and Social Affairs>Population Division>Data>Population>Total Population]</ref>
人口大きさ : 1 E7
人口密度値 : 246
人口統計年 : 2011
人口順位 : 22
位置画像 : Location_UK_EU_Europe_001.svg
元首等氏名 : エリザベス2世
元首等肩書 : 女王
公式国名 : {{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>英語以外での正式国名:<br/>
*{{lang|gd|An Rioghachd Aonaichte na Breatainn Mhor agus Eirinn mu Thuath}}(スコットランド・ゲール語)<br/>
*{{lang|cy|Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon}}(ウェールズ語)<br/>
*{{lang|ga|Riocht Aontaithe na Breataine Moire agus Tuaisceart na hEireann}}(アイルランド語)<br/>
*{{lang|kw|An Rywvaneth Unys a Vreten Veur hag Iwerdhon Gledh}}(コーンウォール語)<br/>
*{{lang|sco|Unitit Kinrick o Great Breetain an Northren Ireland}}(スコットランド語)<br/>
**{{lang|sco|Claught Kangrick o Docht Bratain an Norlin Airlann}}、{{lang|sco|Unitet Kangdom o Great Brittain an Norlin Airlann}}(アルスター・スコットランド語)</ref>
公用語 : 英語(事実上)
国旗画像 : Flag of the United Kingdom.svg
国歌 : 神よ女王陛下を守り給え
国章リンク : (国章)
国章画像 : [[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]
国際電話番号 : 44
夏時間 : +1
建国形態 : 建国
日本語国名 : グレートブリテン及び北アイルランド連合王国
時間帯 : ±0
最大都市 : ロンドン
標語 : {{lang|fr|Dieu et mon droit}}<br/>(フランス語:神と私の権利)
水面積率 : 1.3%
注記 : <references />
略名 : イギリス
確立年月日1 : 927年/843年
確立年月日2 : 1707年
確立年月日3 : 1801年
確立年月日4 : 1927年
確立形態1 : イングランド王国/スコットランド王国<br />(両国とも1707年連合法まで)
確立形態2 : グレートブリテン王国建国<br />(1707年連合法)
確立形態3 : グレートブリテン及びアイルランド連合王国建国<br />(1800年連合法)
確立形態4 : 現在の国号「グレートブリテン及び北アイルランド連合王国」に変更
通貨 : UKポンド (&pound;)
通貨コード : GBP
面積値 : 244,820
面積大きさ : 1 E11
面積順位 : 76
首相等氏名 : デーヴィッド・キャメロン
首相等肩書 : 首相
首都 : ロンドン
  
Posted by gushwell at 21:00Comments(0)

2018年03月18日

言語処理100本ノックでPython入門 #26 - 正規表現の置換処理

   このエントリーをはてなブックマークに追加 Clip to Evernote
今日は、言語処理100本ノック 2015の問題26です。

■ 問題

問題20で作成したファイルを入力として、以下の問題を解きます。
26. 強調マークアップの除去 25の処理時に,テンプレートの値からMediaWikiの強調マークアップ(弱い強調,強調,強い強調のすべて)を除去してテキストに変換せよ(参考: マークアップ早見表).

■ どうやって解くか


強調マークアップは、以下のように2つ以上の''で括ります。
''弱い強調''    
'''強調'''    
'''''強い強調'''''

このマークアップ('記号)だけを削除するということですね。

re.subメソッドを使って置換処理を行えばいいですね。 ちょっと手抜きですが、以下のような正規表現にしました。
for k, v in reg.findall(basicInfo):
    wdict[k] =  re.sub(r"''('|''')([^']+?)''+", r"\2", v)

4番目の引数として、回数を指定すると、置換する回数を指定できます。省略するとすべてを置換します。 今回は、すべてを置換対象とするので、第4引数は省略しています。


■ Pythonのコード
import re

def readArticle(filename):
    with open(filename, 'r', encoding='utf8') as fin:
        return fin.read()

def getBasicInfo(article):
    basicInfo = re.search(r'\{\{基礎情報(.+?)\}\}\n', article, re.DOTALL).group(1)
    pat = r'^\|(.+?)\s*=\s*(.+?)(?<!<br/>)\n'
    reg = re.compile(pat, re.MULTILINE | re.DOTALL)
    wdict = {}
    for k, v in reg.findall(basicInfo):
        v1 = re.sub(r"''('|''')([^']+?)''+", r"\2", v)
        wdict[k] = v1

    return wdict

def main():
    article = readArticle('england-article.txt')    
    dic = getBasicInfo(article)
    dic = sorted(dic.items(), key=lambda x: x[0])
    for item, v in dic:
        print(item, ':', v)

if __name__ == '__main__':
    main()


■ 結果

結果は長くなるので、該当箇所だけ抜き出したものを以下に示します。
確立形態4 : 現在の国号「グレートブリテン及び北アイルランド連合王国」に変更
でも、基礎情報のうち該当する個所が上の一か所なんだけど...
これで合ってるのかな?

元の行は以下の通りです。

確立形態4 : 現在の国号「'''グレートブリテン及び北アイルランド連合王国'''」に変更
  
Posted by gushwell at 22:30Comments(0)

2018年03月15日

言語処理100本ノックでPython入門 #25 - re.DOTALL/re.MULTILINEオプション

   このエントリーをはてなブックマークに追加 Clip to Evernote

言語処理100本ノック 2015の問題25に挑戦です。 

■ 問題

問題20で作成したファイルを入力として、以下の問題を解きます。
25. テンプレートの抽出
記事中に含まれる「基礎情報」テンプレートのフィールド名と値を抽出し,辞書オブジェクトとして格納せよ.

■ Pythonのコード
import json
import re
def readArticle(filename): with open(filename, 'r', encoding='utf8') as fin: return fin.read()
def getBasicInfo(article): basicInfo = re.search(r'\{\{基礎情報(.+?)\}\}\n', article, re.DOTALL).group(1) pat = r'^\|(.+?)\s*=\s*(.+?)(?<!<br/>)\n' reg = re.compile(pat, re.MULTILINE | re.DOTALL) wdict = {} for k, v in reg.findall(basicInfo): wdict[k] = v return wdict
def main(): article = readArticle('england-article.txt') dic = getBasicInfo(article) dic = sorted(dic.items(), key=lambda x: x[0]) for item, v in dic: print(item, ':', v)
if __name__ == '__main__': main()
'england-article.txt'は問題20で作成したファイルです。これを入力ファイルとしています。


■ 今回のトピック等

今回はいろいろと勉強になりました。

re.DOTALLオプション

まずは、基礎情報のところだけを抜き出し、それから、フィールドと値を取り出そうと考えました。 しかし、
re.search(r'\{\{基礎情報(.+?)\}\}\n', article)
って書いても一致しません。どうしてって悩みましたが、re.DOTALLというオプションが必要なようです。
re.DOTALLは、正規表現のドット.に改行も含めるという指定です。

途中で、対応していない`}}\n`があったら、正しく取り出せないんですが、そういた記述はないようなので これで良しとします。

re.MULTILINEオプション

次に、フィールドと値を取り出します。調べると、"公式国名"の値は改行が含まれていて複数行にわたっています。 次のように書いてみました。
pat = r'^\|(.+?)\s*=\s*(.+?)(?<!<br/>)\n'
reg = re.compile(pat, re.DOTALL)

正規表現の (?<!...) は、... とマッチしない場合にマッチします。つまり、(?<!--<br/-->)は、文字列`<br />`を含まないという指定です。

でも、うまくいきません。複数行の検索においては、行頭の ^ を各行の行頭とマッチさせるには、re.DOTALLとは別にre.MULTILINE を指定する必要がありました。
reg = re.compile(pat, re.MULTILINE | re.DOTALL)

finditer/findallメソッド 

finditerメソッドを使うと、matchオブジェクトを列挙することができます。

最初に書いたコード。
wdict = {}
for m in reg.finditer(basicInfo):
    wdict[m.group(1)] = m.group(2)
return wdict 

次に書いたコード。
wdict = {}
for k, v in reg.findall(basicInfo):
    wdict[k] = v
return wdict    
findallメソッド使うと、こんな書き方もできるんですね。

findallメソッドは、戻り値はmatchオブジェクトではないので、group()などは利用できません。
その代わり、グループ指定がある場合は、そのタプルのリストが返るので、for文で上のようにタプルの要素を分解して取得することができます。

今回のコードでは、これを採用しました。
今回の例だと、()によるグルーピングは2つですが、3つの場合は、3つの要素を持つタプルになるので、
 
for g1, g2, g3 in reg.findall(s):
のように書けます。


■ 結果
GDP/人 : 36,727<ref name="imf-statistics-gdp" />
GDP値 : 2兆3162億<ref name="imf-statistics-gdp" />
GDP値MER : 2兆4337億<ref name="imf-statistics-gdp" />
GDP値元 : 1兆5478億<ref name="imf-statistics-gdp">[http://www.imf.org/external/pubs/ft/weo/2012/02/weodata/weorept.aspx?pr.x=70&;pr.y=13&sy=2010&ey=2012&scsm=1&ssd=1&sort=country&ds=.&br=1&c=112&s=NGDP%2CNGDPD%2CPPPGDP%2CPPPPC&grp=0&a= IMF>Data and Statistics>World Economic Outlook Databases>By Countrise>United Kingdom]</ref>
GDP統計年 : 2012
GDP統計年MER : 2012
GDP統計年元 : 2012
GDP順位 : 6
GDP順位MER : 5
ISO 3166-1 : GB / GBR
ccTLD : [[.uk]] / [[.gb]]<ref>使用は.ukに比べ圧倒的少数。</ref>
人口値 : 63,181,775<ref>[http://esa.un.org/unpd/wpp/Excel-Data/population.htm United Nations Department of Economic and Social Affairs>Population Division>Data>Population>Total Population]</ref>
人口大きさ : 1 E7
人口密度値 : 246
人口統計年 : 2011
人口順位 : 22
位置画像 : Location_UK_EU_Europe_001.svg
元首等氏名 : [[エリザベス2世]]
元首等肩書 : [[イギリスの君主|女王]]
公式国名 : {{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>英語以外での正式国名:<br/>
*{{lang|gd|An Rioghachd Aonaichte na Breatainn Mhor agus Eirinn mu Thuath}}([[スコットランド・ゲール語]])<br/>
*{{lang|cy|Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon}}([[ウェールズ語]])<br/>
*{{lang|ga|Riocht Aontaithe na Breataine Moire agus Tuaisceart na hEireann}}([[アイルランド語]])<br/>
*{{lang|kw|An Rywvaneth Unys a Vreten Veur hag Iwerdhon Gledh}}([[コーンウォール語]])<br/>
*{{lang|sco|Unitit Kinrick o Great Breetain an Northren Ireland}}([[スコットランド語]])<br/>
**{{lang|sco|Claught Kangrick o Docht Bratain an Norlin Airlann}}、{{lang|sco|Unitet Kangdom o Great Brittain an Norlin Airlann}}(アルスター・スコットランド語)</ref>
公用語 : [[英語]](事実上)
国旗画像 : Flag of the United Kingdom.svg
国歌 : [[女王陛下万歳|神よ女王陛下を守り給え]]
国章リンク : ([[イギリスの国章|国章]])
国章画像 : [[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]
国際電話番号 : 44
夏時間 : +1
建国形態 : 建国
日本語国名 : グレートブリテン及び北アイルランド連合王国
時間帯 : ±0
最大都市 : ロンドン
標語 : {{lang|fr|Dieu et mon droit}}<br/>([[フランス語]]:神と私の権利)
水面積率 : 1.3%
注記 : <references />
略名 : イギリス
確立年月日1 : [[927年]]/[[843年]]
確立年月日2 : [[1707年]]
確立年月日3 : [[1801年]]
確立年月日4 : [[1927年]]
確立形態1 : [[イングランド王国]]/[[スコットランド王国]]<br />(両国とも[[連合法 (1707年)|1707年連合法]]まで)
確立形態2 : [[グレートブリテン王国]]建国<br />([[連合法 (1707年)|1707年連合法]])
確立形態3 : [[グレートブリテン及びアイルランド連合王国]]建国<br />([[連合法 (1800年)|1800年連合法]])
確立形態4 : 現在の国号「'''グレートブリテン及び北アイルランド連合王国'''」に変更
通貨 : [[スターリング・ポンド|UKポンド]] (&pound;)
通貨コード : GBP
面積値 : 244,820
面積大きさ : 1 E11
面積順位 : 76
首相等氏名 : [[デーヴィッド・キャメロン]]
首相等肩書 : [[イギリスの首相|首相]]
首都 : [[ロンドン]]

  
Posted by gushwell at 20:15Comments(0)

2018年03月13日

言語処理100本ノックでPython入門 #24 - 正規表現のcompile

   このエントリーをはてなブックマークに追加 Clip to Evernote

本日は、言語処理100本ノック 2015の問題24です。

■ 問題


問題20で作成したファイルを入力として、以下の問題を解きます。
24. ファイル参照の抽出 
記事から参照されているメディアファイルをすべて抜き出せ.

■ Pythonのコード
import json
import re

def readArticle(filename): with open(filename, 'r', encoding='utf8') as fin: return fin.read()
def getMediaFfiles(article): reg = re.compile(r'(?:File|ファイル):([^\|]+)\|') for s in article.split('\n'): m = reg.search(s) if m: yield m.group(1)
def main(): article = readArticle('england-article.txt') for name in getMediaFfiles(article): print(name)
if __name__ == '__main__': main()

'england-article.txt'は問題20で作成したファイルです。これを入力ファイルとしています。


■ 正規表現


今回は、以下のような正規表現を書いたわけですが、

r'(?:File|ファイル):([^\|]+)\|'
?: は、後方参照の対象にはならないので、(?:File|ファイル) の部分は m.group(n)での参照対象外です。

なので、 m.group(1) で目的の文字列が取り出せます。

それと、Pyrtnonのドキュメント「6.2. re — 正規表現操作」によると、
簡単なアプリケーションを除くほとんどで、コンパイルされた形式が用いられるのが普通です。
とのことなので、今回は、以下のようにcompileしてみました。

reg = re.compile(r'(?:File|ファイル):([^\|]+)\|')

■ 実行結果
Royal Coat of Arms of the United Kingdom.svg
Battle of Waterloo 1815.PNG
The British Empire.png
Uk topo en.jpg
BenNevis2005.jpg
Elizabeth II greets NASA GSFC employees, May 8, 2007 edit.jpg
Palace of Westminster, London - Feb 2007.jpg
David Cameron and Barack Obama at the G20 Summit in Toronto.jpg
Soldiers Trooping the Colour, 16th June 2007.jpg
Scotland Parliament Holyrood.jpg
London.bankofengland.arp.jpg
City of London skyline from London City Hall - Oct 2008.jpg
Oil platform in the North SeaPros.jpg
Eurostar at St Pancras Jan 2008.jpg
Heathrow T5.jpg
Anglospeak.svg
CHANDOS3.jpg
The Fabs.JPG
PalaceOfWestminsterAtNight.jpg
Westminster Abbey - West Door.jpg
Edinburgh Cockburn St dsc06789.jpg
Canterbury Cathedral - Portal Nave Cross-spire.jpeg
Kew Gardens Palm House, London - July 2009.jpg
2005-06-27 - United Kingdom - England - London - Greenwich.jpg
Stonehenge2007 07 30.jpg
Yard2.jpg
Durham Kathedrale Nahaufnahme.jpg
Roman Baths in Bath Spa, England - July 2006.jpg
Fountains Abbey view02 2005-08-27.jpg
Blenheim Palace IMG 3673.JPG
Liverpool Pier Head by night.jpg
Hadrian's Wall view near Greenhead.jpg
London Tower (1).JPG
Wembley Stadium, illuminated.jpg
  
Posted by gushwell at 22:30Comments(0)

2018年03月11日

言語処理100本ノックでPython入門 #23 - タプル

   このエントリーをはてなブックマークに追加 Clip to Evernote

言語処理100本ノック 2015の問題23に挑戦です。 

■ 問題


問題20で作成したファイルを入力として、以下の問題を解きます。
23. セクション構造
記事中に含まれるセクション名とそのレベル(例えば"== セクション名 =="なら1)を表示せよ.

■ Pythonのコード
import re

def readArticle(filename): with open(filename, 'r', encoding='utf8') as fin: return fin.read()
def getSections(article): for s in article.split('\n'): m = re.match(r'(=+)\s*([^=\s]+)\s*=+', s) if m: yield m.group(2), len(m.group(1)) - 1
def main(): article = readArticle('england-article.txt') for name, level in getSections(article): print(name, level)
if __name__ == '__main__': main()

■ どう解くか

'england-article.txt'は問題20で作成したファイルです。これを入力ファイルとしています。
No22 とほぼ同じような構造で書けますね。

変更したのは、getCategoriesメソッドの名前をgetSectionsに変え、中身と戻り値を若干変更しただけです。

def getSections(article):
    for s in article.split('\n'):
        m = re.match(r'(=+)\s*([^=\s]+)\s*=+', s)
        if m:
            yield m.group(2), len(m.group(1)) - 1

このメソッドで、レベルをどう取り出そうか少し悩みましたが、`=` 連続もグループ化して、その長さを求めればOKですね。 それと、= と セクション名の間に空白がある場合もあるので、`\s*`で空白有りにも対応しています。


■ タプル


Pythonで初めてタプル使いました。yieldで列挙するときに、タプルを使っています。

yield m.group(2), len(m.group(1)) - 1

呼び出し元での参照も簡単です。
for name, level in getSections(article):
        print(name, level)

やはり、言語そのものでタプルをサポートしているのでとても便利です。


■ 結果

国名 1
歴史 1
地理 1
気候 2
政治 1
外交と軍事 1
地方行政区分 1
主要都市 2
科学技術 1
経済 1
鉱業 2
農業 2
貿易 2
通貨 2
企業 2
交通 1
道路 2
鉄道 2
海運 2
航空 2
通信 1
国民 1
言語 2
宗教 2
婚姻 2
教育 2
文化 1
食文化 2
文学 2
哲学 2
音楽 2
イギリスのポピュラー音楽 3
映画 2
  
Posted by gushwell at 22:20Comments(0)