前の記事:計量文献学入門_00:計量文献学とは
次の記事:(準備中)
はじめに
しばらく前に計量文献学について触れる機会があったため、諸々の備忘録として記事を残します。狭い学問分野ですが、どなたかの助力になれば幸いです。
この記事は『計量文献学入門_xx:*』に関連して執筆しています。計量文献学についてなんとなく解説しながら、実際に著者推定を行っていきます。
この記事の目標
著者推定の概要と、データの前処理についてふんわりやってみることがこの記事の目標です。
著者推定
著者推定の概要
先の記事『計量文献学入門_00:計量文献学とは』に述べたように、文献に係る問題には「誰が執筆したのか」という話題があります。分析手法の仔細は、その個々の問題によって異なりますが、大枠の流れは次のようになります。
- 対象の著者のテキストデータを収集
- データを分析可能な形に整形
- 文献の特徴を定量化して抽出
- 特徴量を用いて分析
分析は対象著者が執筆した文献と、執筆の真偽が疑われている文献を比較して行われます。その手法はクラスタリングや主成分分析、対応分析など様々です。
設定した特徴量が著者の特徴を十分にとらえているかを確かめるため、複数の著者の文献をもとに、その分類精度を検証する研究もあります。この場合はサポートベクターマシンやランダムフォレストといった機械学習法がよく用いられます。
本記事群で行う著者推定
この記事群で試す著者推定タスクは次の流れで行います。
- 青空文庫から複数の著者の作品を取得
- データの前処理
- 代表的な特徴量を抽出
- 機械学習を用いて著者の推定(分類)
データ取得
電子図書館青空文庫からデータを取得しましょう。このサイトには著作権の消滅した作品が公開されています。
対象とする著者は次の三人としました。併記した数字は青空文庫における作者IDです。
- 夏目漱石 (000035)
- 芥川龍之介(000879)
- 豊島与志雄(000906)
青空文庫からデータを一括で取得する方法ですが、今回は青空文庫が用意しているGithubから持ってくることにします。各作品のzipファイルをひとつひとつ展開する手間を省けます。
ファイル構造は./caed/作者ID/files/作品Aフォルダ/作品A.txt
となっています。すなわち今回は
- ./caed/000035/files/* /*.txt
- ./caed/000879/files/* /*.txt
- ./caed/000906/files/* /*.txt
のデータさえ手に入れてしまえばデータの取得は完了です。
Gitのデータをすべてダウンロードしてもよいのですが、サイズが200MB以上あるため、対象ディレクトリのみの取得がおすすめです。なお、個別にデータをダウンロードする手順は以下の記事に譲ります(Githubのログインが必要)。
そして、ダウンロードしたデータは以下のようにまとめておきます。
.
┗ aozora_text/
├ 000035/files/ …
├ 000879/files/ …
└ 000906/files/ …
前処理
青空文庫から取得したテキストデータは、分析には不要な文章が含まれています。例えば夏目漱石『こころ』のtxtデータの文頭には次の注意書きがあります。
-------------------------------------------------------
【テキスト中に現れる記号について】
《》:ルビ
(例)私《わたくし》は
|:ルビの付く文字列の始まりを特定する記号
(例)先生一人|麦藁帽《むぎわらぼう》を
[#]:入力者注 主に外字の説明や、傍点の位置の指定
(数字は、JIS X 0213の面区点番号、または底本のページと行数)
(例)※[#「てへん+劣」、第3水準1-84-77]
-------------------------------------------------------
分析に際して、この文章はノイズになります。またこの注意書きに書かれているような、ルビや入力者注の記号も不要です。そのため取得したデータすべてについて、これらの記号や文章を除かなければなりません。
今回は以下の記事を参考にして前処理するプログラムを作成しました。入出力や細部に変更を加えたのみです。実行する際はdataディレクトリを事前に作成してから走らせてください。
Text2Data.pyimport pandas as pd from pathlib import Path # 035:太宰治,879:芥川龍之介,906:豊島与志雄 AuthorID = '000035' # 著者ID AuthorName = '太宰治' # 作家名 InDir = Path(f'./aozora_text/{AuthorID}/files') # 読み込みファイル元 OutDir = Path(f'./data/{AuthorID}') # ファイル出力先 # ===== テキストを整形する関数 ===== def text_cleanse_df(df): # 本文の先頭を探す('---…'区切りの直後から本文が始まる前提) head_tx = list(df[df['text'].str.contains( '-------------------------------------------------------')].index) # 本文の末尾を探す('底本:'の直前に本文が終わる前提) atx = list(df[df['text'].str.contains('底本:')].index) if head_tx == []: # もし'---…'区切りが無い場合は、作家名の直後に本文が始まる前提 head_tx = list(df[df['text'].str.contains(AuthorID)].index) head_tx_num = head_tx[0]+1 else: # 2個目の'---…'区切り直後から本文が始まる head_tx_num = head_tx[1]+1 df_e = df[head_tx_num:atx[0]] # 青空文庫の書式削除 df_e = df_e.replace({'text': {'《.*?》': ''}}, regex=True) df_e = df_e.replace({'text': {'[.*?]': ''}}, regex=True) df_e = df_e.replace({'text': {'|': ''}}, regex=True) # 字下げ(行頭の全角スペース)を削除 df_e = df_e.replace({'text': {' ': ''}}, regex=True) # 節区切りを削除 df_e = df_e.replace({'text': {'^.$': ''}}, regex=True) df_e = df_e.replace({'text': {'^―――.*$': ''}}, regex=True) df_e = df_e.replace({'text': {'^***.*$': ''}}, regex=True) df_e = df_e.replace({'text': {'^×××.*$': ''}}, regex=True) # 記号、および記号削除によって残ったカッコを削除 df_e = df_e.replace({'text': {'―': ''}}, regex=True) df_e = df_e.replace({'text': {'…': ''}}, regex=True) df_e = df_e.replace({'text': {'※': ''}}, regex=True) df_e = df_e.replace({'text': {'「」': ''}}, regex=True) # 一文字以下で構成されている行を削除 df_e['length'] = df_e['text'].map(lambda x: len(x)) df_e = df_e[df_e['length'] > 1] # インデックスがずれるので振りなおす df_e = df_e.reset_index().drop(['index'], axis=1) # 空白行を削除する(念のため) df_e = df_e[~(df_e['text'] == '')] # インデックスがずれるので振り直し、文字の長さの列を削除する df_e = df_e.reset_index().drop(['index', 'length'], axis=1) return df_e # ===== ファイルの入出力を行う関数 ===== def save_cleanse_text(FilePath): try: TargetFile = list(FilePath.glob('*.txt'))[0] # --- ファイルの読み込み --- # Pandas DataFrameとして読み込む(cp932で読み込まないと異体字が読めない) df_tmp = pd.read_csv(TargetFile, encoding='cp932', names=['text']) # --- テキスト整形 --- df_tmp_e = text_cleanse_df(df_tmp) Title = df_tmp['text'][0] # --- utf-8ファイルの書き込み --- FileName = Path(AuthorName[0] + '_' + Title + '.txt') df_tmp_e.to_csv(Path(OutDir / FileName), sep='\t', encoding='utf-8', index=False, header=False) print(f'DONE: {TargetFile}') except: print(f'ERROR: {TargetFile}') def main(): # 作品ディレクトリのリストを作成 DirList = list(InDir.glob('*')) # 保存ディレクトリを作成 OutDir.mkdir(exist_ok=True, parents=True) # データを一つずつ整形して保存 for item in DirList: save_cleanse_text(item) if __name__ == '__main__': main()
無事、著者ごとに作品群を整形し、データを得ることができました。テキストファイルを見ると、不要な記号や注意書きなどが削除されていることが確認できます。
おわりに
本記事では著者推定の大まかな流れと、データの取得・前処理について解説しました。次回は著者それぞれの特徴を、複数の観点から定量化していきます。どうぞよしなに。