乱麻篇の能書き
例によって詳細をトレースできる Jupyter Notebook (*1) は下の おまけ のところに置いておきました。
今回も細かい説明は省いて、ポイントとなる script のシェアにとどめます。
(*1) Google Colab (Python 3.6.9)上で動作確認済。
乱麻篇に登場する伏兵
- 連番に潜む謎の文字列: column の中身をかなり整理した後なので数字(string)しか入っていないはずのセルに、謎の文字列 Naga8r5 が。オリジナル PDF で目視確認してもそこには普通に 85 という数字しかない。しかしオリジナル PDF 上の文字列をマウスでコピペして editor に貼り付けて見るとそこには 85の皮を被ったNaga8r5 の文字列が…😱(お分かり いただけただろうか?)。
解決法: 当該セルを探し出して手書きで修正 🙄
今回の作業の流れ
- 前回(死闘篇)までの作業で、PDF の粗々の CSV 化まで漕ぎ着けたので、オリジナル PDF の目次に従って、レコード(行 = rows)の並び順をオリジナルに戻すことにします。
- 目標は、州名略記(state_code)> 州ごとの連番(sc_num)の順にソートされた表を作ることです。
- なお、DF の Sort については、Pandas: DataFrame の任意の列を任意の順序でソートする として別記事に少し詳しく書いておきました。
作業は次の順序で行います。
- Setup (Script 0)
- 作業中のデータの読み込み (Script 1)
- 州ごとの連番列 (sc_num column) の準備 (Scripts 2)
- 州略記列 (state_code colunn) の準備 (Scripts 3, 4)
- state_code > sc_num の順に並べ替え(Script 5)
- 作業データの保存(Script 6)
それでは始めます。
作業準備
- 前提1: tabula-py が Google Drive の ~/My Drive/Colab Notebooks/my-modules にインストールされていること。
- 前提2: 読み込み元の PDF が、Google Drive の
~/My Drive/pdf_project/data/2018_co_list_jp_r.pdf
に置かれていること。 - 前提3: 前回作業中の CSV が、Google Drive の
~/My Drive/pdf_project/data/df_all_intermed.csv
に置かれていること。 - もし上記の前提が満たされていないようでしたら お手数ですが、立志篇、死闘篇 を参照の上、準備をお願いします。
Script 0
'''以下はすべて Google Colab / Drive が前提となっているので、 local machine で実験する時には適当に path などを変えてください。''' # Google Drive のマウント。 from google.colab import drive drive.mount('/content/drive') # Modules の import. import os import sys # PATH を通す(Python に modules の場所を教える) # local machine で実験するときは不要。 MODULE_PATH = '/content/drive/My Drive/Colab Notebooks/my-modules' sys.path.append(MODULE_PATH) import tabula # Module の場所を教えたので、import. import pandas as pd # 念のため明示的に import しておく。 # ディレクトリ構造を定義する。 # local machine で実験するときは、 # PROJECT_ROOT_PATH = '.' などと適当に変えて使います。 PROJECT_ROOT_PATH = '/content/drive/My Drive/pdf_project' DATA_PATH = os.path.join(PROJECT_ROOT_PATH, 'data') print('準備完了 🍻')
作業中のデータの読み込み
Remarks: 2020年08月18日以前に df_all_intermed.csv を作られた方は、お手数ですが df_all_intermed.csv を更新してください(\r 問題 解決済バージョンです)。
Script 1
# Load the previously prepared intermediate working file. df_all_intermed = pd.read_csv(os.path.join(DATA_PATH, 'df_all_intermed.csv')) df_all_intermed
州ごとの連番列 (sc_num column) の準備
- CSV から読み込んだだけなので、DF のすべてのデータは object(string)です。そのままうっかり sort を掛けると、数値で並べ替えたつもりが文字列での並べ替えになってしまうので、int にしたいところです。
- ところが、この CSV の場合、とある事情で、いきなり int にしようとすると文句を言われるので、いちど float を経由して int に変換します。
- だが、ここで伏兵が襲う! — というのが 85の皮を被ったNaga8r5 問題ですが、話が長くなるので詳細はおまけの Jupyter Notebook で。
Script 2
# Data correction df_all_intermed.at[1711, 'sc_num'] = 85 # Change the dtype of sc_num -> float -> int df_all_intermed.sc_num = df_all_intermed.sc_num.astype(float) df_all_intermed.sc_num = df_all_intermed.sc_num.astype(int) # Confirm the results. df_all_intermed.info()
州略記列 (state_code colunn) の準備
- Script 3 で州名略記(state_code)の順序を list にしておき、Script 4 で DF の state_code column を category type にするとともに、カテゴリー項目 の順序を教えてあげます。
Script 3
# Define state codes and their order. state_code_order = ['DL', 'HR', 'UP', 'RJ', 'CH', 'PB', 'UK', 'HP', 'JK', 'AS', 'AR', 'MN', 'ML', 'MZ', 'SK', 'TR', 'WB', 'JH', 'OD', 'BR', 'MH', 'GJ', 'MP', 'GA', 'CG', 'DD', 'DN', 'KA', 'TN', 'AP', 'TS', 'KL', 'PY']
Script 4
# 念のため、Copy に対して作業することにします。 df_sorted = df_all_intermed.copy() # sc_num の掃除は既に済んでいる。 # state_code column の data type を categorical にし、順序も定義。 df_sorted.state_code = pd.Categorical( df_sorted.state_code, categories=state_code_order, ordered=True)
Let’s SORT!
- 準備が整ったので、state_code と sc_num の 2 columns を一気に sort します(Script 5)。
- Excel の カスタムソートとの違いは、Pandas の場合は state_code column (dtype = category) が自分でカテゴリー項目の順番を覚えている(Script 4)ので、sort のたびに一々順序を指定しなくても良いところです。
Script 5
# Sort the whole DF. df_sorted.sort_values(by=['state_code', 'sc_num'], inplace=True) # reset the index. df_sorted.reset_index(drop=True, inplace=True) df_sorted.head(16)
作業データの保存
- ここまでの結果を、df_sorted_intermed.csv の名前で Google Drive の data フォルダに保存しておきます(Script 6)。
Script 6
df_sorted.to_csv(os.path.join(DATA_PATH, 'df_sorted_intermed.csv'), index=False)
Main Takeaway
- pandas.Categorical() はとても良い奴。Sort は奴の実力のごく一部、友達になっておくと心強い 🏋️♂️
次回計画
- ‘biz_type’ の正規化。現状で unique 値が 56 個ですが、これは本当の数の倍ぐらいに増えちゃっています(Triad退治などの影響)。
- 正規化: ‘com_in’, ‘com_jp’, ‘biz_type’ は別の table に分けて foreign key で引っ張ってくるようにした方が良さそう。
Sneak Preview
- 既にお気づきのように、この DF にはもう一つ Categorical な column, biz_type があります。
- ちょっと覗いてみると、Script 7 のような状況です。
- Triad退治の後遺症などもあって、オリジナル PDF のデータがだいぶ撹乱されていますが、次回はその整序などを行いたいと思います。
Script 7
sorted(df_sorted.biz_type.unique())
おまけ
上記の script に説明を加えた Jupyter Notebook を pdf_to_csv_sort.ipynb_.zip [7 KB] (Preview)に置いておきました。Colab に upload すればそのまま使えます。