言語処理100本ノック 2020 第3章 前半
第3章 正規表現の前半(20-24まで)解説書きます。
ついにJSONが出てきた。そして一気に書くの大変なので解けたら都度更新します。
目次
この章で使うファイル
Wikipediaの記事をJSONにしてgzで固めたもの。
途中、抽出してほしい箇所を指定されるが、WikipediaのHelp:早見表を見て確認のこと。
title text 0 エジプト {{otheruses|主に現代のエジプト・アラブ共和国|古代|古代エジプト}}\n{{基礎... 1 オーストリア {{基礎情報 国\n|略名 = オーストリア\n|日本語国名 = オーストリア共和国\n|公... 2 インドネシア {{基礎情報 国\n| 略名 =インドネシア\n| 日本語国名 =インドネシア共和国\n| ... 3 イラク {{複数の問題\n| 参照方法 = 2011年8月\n| 独自研究 = 2012年10月\n... 4 イラン {{半保護}}\n{{未検証|date=2010年3月}}\n{{基礎情報 国\n | 略名... .. ... ... 243 スヴァールバル諸島およびヤンマイエン島 '''スヴァールバル諸島およびヤンマイエン島''' (Svalbard and Jan Ma... 244 シンガポール {{参照方法|date=2012年2月}}\n{{基礎情報 国\n|略名 = シンガポール\... 245 マレーシア {{基礎情報 国\n|略名 =マレーシア\n|日本語国名 =マレーシア\n|公式国名 ={{... 246 BES諸島 #転送 [[ボネール、シント・ユースタティウスおよびサバ]] 247 キュラソー島 #転送 [[キュラソー (オランダ王国)]]
20. JSONデータの読み込み
pandasならread_csv、read_jsonで圧縮されたままdfに取り込めることを知った。
超便利じゃないですか。
ポイント
- pandas.read_json()で、gz等で圧縮されたままのjsonファイルを読み込める
- compression='infer'でどの圧縮ファイル(.gz, .bz2, .zip, .xz)か自動判定してくれる
- orient='records', lines=Trueで1レコードがJSONになっている形式(JSONL)を扱える
- pandas.DataFrameでの値参照はdf.valuesを使う
- pandas.DataFrameにあるレコードを条件で検索するにはdf.query('条件')を使う
- 条件を書く時、文字列を含む場合は、文字列を
""
で囲む
- 条件を書く時、文字列を含む場合は、文字列を
解答
# coding:utf-8 import pandas as pd path = './jawiki-country.json.gz' df = pd.read_json(path, compression='infer', orient='records', lines=True) # print(df) ans = df.query('title == "イギリス"')['text'].values[0] print(ans)
参考記事
21. カテゴリ名を含む行を抽出
正規表現の出番かなーと思い、reモジュールを入れる。
pandas.query()
を使わず指定した条件の行を抽出できるので、そのやり方で書いてみた。
この問題で指定されている条件にて文字を抽出するのに使った正規表現は、^\[\[Category:.+\]\]$
正規表現慣れてなくて結構時間かかってしまった。
ポイント
- 文字列について先頭に限らず正規表現にマッチするかどうかを確認する時はre.search('パターン', 文字列)を使う
- 正規表現にマッチして返ってきたマッチオブジェクトについて、文字列を取りたい時group()メソッドで取れる
解答
問題20で抽出した文字列について、改行コードで区切っておく。
1行ずつ読み込んで、正規表現に引っかかったら確保していく。
# coding:utf-8 import pandas as pd import re def uk_load(): path = './jawiki-country.json.gz' wiki = pd.read_json(path, compression='infer', orient='records', lines=True) uk = wiki[wiki['title'] == 'イギリス']['text'].values[0] return(uk) def main(): uk_txt = uk_load().split('\n') ans = [] # print(uk_txt) for item in uk_txt: result = re.search(r'^\[\[Category:.+\]\]$', item) if result: ans.append(result.group(0)) for r in ans: print(r) if __name__ == "__main__": main()
出力はこんな感じ。
$ python CH3-21.py [[Category:イギリス|*]] [[Category:イギリス連邦加盟国]] [[Category:英連邦王国|*]] [[Category:G8加盟国]] [[Category:欧州連合加盟国|元]] [[Category:海洋国家]] [[Category:現存する君主国]] [[Category:島国]] [[Category:1801年に成立した国家・領域]]
参考記事
22. カテゴリ名の抽出
問題21の続き。余計な括弧とか抜きで出力する。
問題21の行抽出の時、lambda式とfilterできれいに書けることに気づいたので、
今回はそっちで書いてみることにした。
エンジニアは面倒くさがらないといけない。鉄則。
ポイント
- filterとlambda式を使って対象を絞り込むことができる
- 正規表現を使った文字列置換をしたい時は、re.sub('置換元パターン', '置換後の文字', 対象の文字列)を使う
解答
# coding:utf-8 import pandas as pd import re def uk_load(): path = './jawiki-country.json.gz' wiki = pd.read_json(path, compression='infer', orient='records', lines=True) uk = wiki[wiki['title'] == 'イギリス']['text'].values[0] return(uk) def main(): uk_txt = uk_load().split('\n') uk_category = list(filter(lambda x: "[[Category:" in x, uk_txt)) # print(uk_category) for item in uk_category: item = item.replace('[[Category:', '').replace(']]', '') item = re.sub(r'\|.$', '', item) print(item) if __name__ == "__main__": main()
出力はこんな感じ
$ python CH3-22.py イギリス イギリス連邦加盟国 英連邦王国 G8加盟国 欧州連合加盟国 海洋国家 現存する君主国 島国 1801年に成立した国家・領域
参考記事
23. セクション構造
Wikipediaのヘルプ見ると、==
でセクション2になっているが、
問題文に従ってセクション1として実装した。
解答
# coding:utf-8 import pandas as pd def uk_load(): path = './jawiki-country.json.gz' wiki = pd.read_json(path, compression='infer', orient='records', lines=True) uk = wiki[wiki['title'] == 'イギリス']['text'].values[0] return(uk) def main(): uk_txt = uk_load().split('\n') section = list(filter(lambda x: '==' in x, uk_txt)) # print(section) for item in section: if item[:5] == '=====': print(item.replace('=====', ''), 4) elif item[:4] == '====': print(item.replace('====', ''), 3) elif item[:3] == '===': print(item.replace('===', ''), 2) elif item[:2] == '==': print(item.replace('==', ''), 1) if __name__ == "__main__": main()
出力は以下の通り
国名 1 歴史 1 地理 1 主要都市 2 気候 2 政治 1 元首 2 法 2 内政 2 地方行政区分 2 外交・軍事 2 経済 1 鉱業 2 農業 2 貿易 2 不動産 2 エネルギー政策 2 通貨 2 企業 2 通信 3 交通 1 道路 2 鉄道 2 海運 2 航空 2 科学技術 1 国民 1 言語 2 宗教 2 婚姻 2 移住 2 教育 2 医療 2 文化 1 食文化 2 文学 2 哲学 2 音楽 2 ポピュラー音楽 3 映画 2 コメディ 2 国花 2 世界遺産 2 祝祭日 2 スポーツ 2 サッカー 3 クリケット 3 競馬 3 モータースポーツ 3 野球 3 カーリング 3 自転車競技 3 脚注 1 関連項目 1 外部リンク 1
24. ファイル参照の抽出
filterとlambda式で絞ったら余計なものがくっついてきたので、
改行コードで区切らず1つの文字列扱いにして、おとなしくreモジュールで抽出することに。
そしてもう少しWikipediaのマークアップ仕様を見ていたら、
[[ファイル:ファイル名|オプション]]
でも、[[File:ファイル名|オプション]]
でも
添付ファイルを貼り付けることができると分かった。
ポイント
- re.findall('正規表現パターン', 対象の文字列)は正規表現パターンにマッチした部分文字列をリスト(要素は文字列)で返す
( )
で正規表現のグループ化(キャプチャあり)を行い、文字列をまとめて扱える- (?: )の場合、グループ化のみを行うという明示になる
解答スクリプト
ファイル名とオプションをキャプチャしているので、
1つのファイルにつき2要素ずつのタプルがリストに格納されて返ってくる。
ほしいのはファイル名だけなので、指定してあげること。
# coding:utf-8 import pandas as pd import re def uk_load(): path = './jawiki-country.json.gz' wiki = pd.read_json(path, compression='infer', orient='records', lines=True) uk = wiki[wiki['title'] == 'イギリス']['text'].values[0] return(uk) def main(): uk_txt = uk_load() file = re.findall(r'\[\[(?:ファイル|File):(.+?)\|(.+?)\]\]', uk_txt) for item in file: print(item[0]) if __name__ == "__main__": main()
出力は以下の通り。
$ python CH3-24.py Royal Coat of Arms of the United Kingdom.svg Descriptio Prime Tabulae Europae.jpg Lenepveu, Jeanne d'Arc au siège d'Orléans.jpg London.bankofengland.arp.jpg Battle of Waterloo 1815.PNG Uk topo en.jpg BenNevis2005.jpg Population density UK 2011 census.png 2019 Greenwich Peninsula & Canary Wharf.jpg Birmingham Skyline from Edgbaston Cricket Ground crop.jpg Leeds CBD at night.jpg Glasgow and the Clyde from the air (geograph 4665720).jpg Palace of Westminster, London - Feb 2007.jpg Scotland Parliament Holyrood.jpg Donald Trump and Theresa May (33998675310) (cropped).jpg Soldiers Trooping the Colour, 16th June 2007.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 Terminal 5C Iwelumo-1.jpg Airbus A380-841 G-XLEB British Airways (10424102995).jpg UKpop.svg Anglospeak.svg Royal Aberdeen Children's Hospital.jpg CHANDOS3.jpg The Fabs.JPG Wembley Stadium, illuminated.jpg