望郷篇の能書き
インドの話をするにあたって資料を紹介しようとして始めた話が迷走して、ここまできてしましました。長〜い話にお付き合いいただきありがとうございます
例によって詳細をトレースできる Jupyter Notebook (*1) は下の おまけ のところに置いておきましたので、今回も細かい説明は省いて、ポイントとなる script のシェアにとどめます。
(*1) Google Colab (Python 3.6.9)上で動作確認済
望郷篇に登場する伏兵
最終回はこれといったトラブルもなく作業が進みました。
多機能な Pandas は Database アプリ的な使い方もできる[1]ので、今回は UPDATE [2, 3] や LEFT JOIN [4] 的な機能を使ってみました。
最終回の作業の流れ
前回(乱麻篇)までの作業で、tabula を使ってオリジナルの PDF をデータ化し、州名略記(state_code)> 州ごとの連番(sc_num)の順にソートするところまで漕ぎ着けました(df_sorted_intermed.csv として一時保存)。
最終回となる今回は、ここから進めて業種列(biz_type column)の整理と正規化等を行います。
前提
- Google Colab 上で作業する前提です。
- df_sorted_intermed.csv が Google Drive の
/content/drive/My Drive/pdf_project/data/df_sorted_intermed.csv
に置いてあること(cf. 乱麻篇)。
手順
作業は次の順序で行います。
- 作業準備(Script 0)
- 作業中のデータの読み込み(Script 1)
- 業種マスターの読み込み(Script 2)
- 修正用 DF の読み込み(Script 3)
- com_jp column の微修正(Script 4)
- マスターテーブルの準備(Script 5)
- 修正用 DF の適用(Script 6)
- 業種データの正規化(Script 7)
- CSV ファイルに保存(Script 8)
それでは始めます。
作業準備
Google Drive のマウントなどを行います(Script 0)。
Script 0
'''以下はすべて Google Colab / Drive が前提となっているので、 local machine で実験する時には適当に path などを変えてください。''' # Google Drive のマウント。 from google.colab import drive drive.mount('/content/drive') # Modules の import. import os import pandas as pd # ディレクトリ構造を定義する。 PROJECT_ROOT_PATH = '/content/drive/My Drive/pdf_project' DATA_PATH = os.path.join(PROJECT_ROOT_PATH, 'data') print('準備完了 🍻')
作業中のデータの読み込み
前回保存した作業ファイル(df_sorted_intermed.csv)を読み込みます(Script 1)。
せっかく sort してあるので、index をコピーして id column を作っておきます。
Script 1
df = pd.read_csv(os.path.join(DATA_PATH, 'df_sorted_intermed.csv')) # Create an 'id' column df.reset_index(drop=False, inplace=True) df.rename(columns={'index': 'id'}, inplace=True) df.head()
業種マスターの読み込み
オリジナル PDF を確認しながら正規化した業種データ(biz_type column)を「業種マスター」(table_biz_types)として DataFrame(以下「DF」)の形で用意しておきます(Script 2)。
Script 2
table_biz_types = pd.DataFrame( [ ['00', '(blank)'], ['01', '農業、林業、漁業 / Agriculture, forestry, fishery'], ['02', '(blank)'], ['03', '建設業 / Construction industry'], ['04', '食料品、飲料・たばこ・飼料製造業 / Manufacturing - Food, drink, tobacco'], ['05', '繊維工業 / Textile industry'], ['06', '木材・木製品、パルプ・紙・紙加工品製造業 / Manufacturing - paper and paper products'], ['07', '化学工業 / Chemical industry'], ['08', '石油製品・石炭製品製造業 / Manufacturing - coal products, petro products'], ['09', '窯業・土石製品製造業 / Pottery'], ['10', '鉄鋼業 / Steel'], ['11', '非鉄金属製造業 / Manufacturing - Non-metal products'], ['12', '金属製品製造業 / Manufacturing metal products'], ['13', 'はん用機械器具製造業 / Manufacturing - general machinery'], ['14', '生産用機械器具製造業 / Manufacturing - production machinery'], ['15', '業務用機械器具製造業 / Manufacturing - commercial machinery'], ['16', '電気機械器具製造業 / Manufacturing - electronic machinery'], ['17', '情報通信機械器具、電子部品・デバイス・電子回路製造業 / Manufacturing - IT products and electronic products'], ['18', '輸送機械器具製造業 / Manufacturing - automobile , two wheeler'], ['19', 'その他の製造業 / Manufacturing - others'], ['20', '電気・ガス・熱供給・水道業 / Electricity, gas, water supply'], ['21', '情報通信業 / Broadcasting, telecommunication etc.'], ['22', '運輸業 / Transportation'], ['23', '卸売業(商社) / Wholesale (trading company)'], ['24', '卸売業(販社) / Wholesale (sales company)'], ['25', '小売業 / Retail'], ['26', '金融業、保険業 / Finance and insurance'], ['27', '不動産業 / Real estate'], ['28', '物品賃貸業 / Product lease'], ['29', '宿泊業、飲食サービス業 / Hotel service, restaurant'], ['30', '教育、学習支援サービス業 / Education, learning support service'], ['31', '医療、福祉サービス業 / Medical, social welfare service'], ['32', '複合サービス業 / Compound service (postal, cooperative, association etc.)'], ['33', 'その他のサービス業 / Other service'] ], columns=['biz_type_code', 'biz_description'] ) table_biz_types.head()
修正用 DF の読み込み
前々回(死闘篇)で行った Triad退治 の副作用で乱れた rows (records) を修正するための DF を用意しておきます(Script 3)。
Script 3
# df_btyp_non_num を手作業で修正(correct)した DF. df_btyp_non_num_corrected = pd.DataFrame( [ [346, 'HR', 'Bawal', 7, 'Caparo MI Steel Processing, Denso Ten Minda', '伊藤忠丸紅鉄鋼', '10 鉄鋼業/ Steel'], [351, 'HR', 'Bawal', 12, 'HANKYU HANSHIN EXPRESS INDIA', '阪急阪神エクスプレス', '22 運輸業/ Transportation'], [365, 'HR', 'Bawal', 26, 'RANE NSK STEERING SYSTEMS', '日本精工', '12 金属製品製造業/ Manufacturing metal products'], [367, 'HR', 'Bawal', 28, 'Sanko Gosei Technology India', '三光合成', '07 化学工業/ Chemical industry'], [374, 'HR', 'Dharuhera', 35, 'SMI AMTEK Crankshaft', '新日鐵住金', '12 金属製品製造業/ Manufacturing metal products'], [2397, 'MH', 'Mumbai', 273, 'Lintec India', 'リンテック', '24 卸売業(販社)/ Wholesale (sales company)'], [4201, 'TN', 'Chennai', 176, 'Kohyei Polymers India', '弘栄貿易', '11 非鉄金属製造業/ Manufacturing - Non-metal products'], [4250, 'TN', 'Chennai', 225, 'Mizuho Bank', 'みずほ銀行', '26 金融業、保険業/ Finance and insurance'], [4259, 'TN', 'Chennai', 234, 'MUFG Bank', '三菱UFJ銀行', '26 金融業、保険業/ Finance and insurance'], [4261, 'TN', 'Chennai', 236, 'Murugappa Organo Water Solutions', 'オルガノ', '01 農業、林業、漁業/ Agriculture, forestry, fishery'], [4264, 'TN', 'Chennai', 239, 'Netmagic Solutions Chennai Office', 'NTTコミュニケーションズ', '21 情報通信業/ Broadcasting, telecommunication etc.'], [4282, 'TN', 'Chennai', 257, 'NISSIN ABC LOGISTICS', '日新', '22 運輸業/ Transportation'] ], columns=['id', 'state_code', 'location', 'sc_num', 'com_in', 'com_jp', 'biz_type'] ) # あとあとの都合があるので、index を key の値に一致させておきます。 df_btyp_non_num_corrected.index = df_btyp_non_num_corrected.id # 確認 df_btyp_non_num_corrected.head()
com_jp column の微修正
同じく前々回の セル内改行対策 の副作用で、com_jp column の中身も少し乱れています。
目視で確認したところ、複数の親会社(例えば、A社とB社)を持つ場合に、’A社<br>B社’ となっているパターンと、’A社及び<br>B社’ となっているパターンの 2 パターンがあったので、これらは差し当たりすべて comma 区切りに変換しておきます(Script 4)。
Script 4
# Regex で、この順序で置換。 df.com_jp.replace(r'<br>', r', ', inplace=True, regex=True) df.com_jp.replace(r'及び', r'', inplace=True, regex=True) df.head(10)
マスターテーブルの準備
以上の作業結果をひとつの DF (master_table) にまとめます。
作業結果が反映された df を copy して、新たに table_master と名付けて土台の DF にします(Script 5)
Script 5
# 新たに table_master (DataFrame) を作って気分一新。 table_master = df.copy()
修正用 DF の適用
- 上の 修正用 DF の読み込み で準備しておいた df_btyp_non_num_corrected を使って master_table を UPDATE します(Script 6)。
Script 6
# 修正済みの df_btyp_non_num_corrected で df_master を UPDATE. table_master.update(df_btyp_non_num_corrected, join='left')
業種データの正規化
次のステップで業種データの正規化を行います(Script 7)。
- biz_type_code column を作る
Database に流し込むときに、biz_type は上の 業種マスターの読み込み で作っておいた別 table (table_biz_types) に分けておいた方が便利なので、table_master 上に新たに ‘biz_type_code column’ を作って biz_type の先頭 2 桁の数字を入れておきます。 - LEFT JOIN
table_master を LEFT TABLE, table_biz_types を RIGHT TABLE として、WHERE table_master.biz_type_code = table_biz_types.biz_type_code とするような感じで 2 つの tables を JOIN します。 - biz_type column を drop
以上の操作で biz_type column は番号部分(biz_type_code)と中身の部分(biz_description)の 2 つの columns に分割されたので、重複を避けるために元の biz_type column 自身は drop しておきます。
Script 7
# 業種を番号(biz_type_code)で管理したいので、 ## 1. 新しく biz_type_code column を作って biz_type の頭の数字 2 桁を入れておきます。 table_master['biz_type_code'] = table_master.biz_type.str[0:2] ## 2. biz_type_code を KEY にして LEFT JOIN する。 table_master = pd.merge(table_master, table_biz_types, how='left', on='biz_type_code') ## 3. biz_type の中身は biz_type_code と biz_description と同じなので、drop します。 table_master.drop(columns=['biz_type'], inplace=True) # 掃除 ## key, sc_num の dtype(の見た目)を int にしておく。 table_master[['id', 'sc_num']] =\ table_master[['id', 'sc_num']].astype('int') ## biz_type_code dtype(の見た目)を string にしておく。 '''できあがった CSV を editor で開くとわかりますが、1 桁の数字は、01, ... 09 と padding されています。しかし、CSV を開くアプリによっては、勝手にゼロを削除 してしまうものもあるので注意が必要です。''' table_master[['biz_type_code']] =\ table_master[['biz_type_code']].astype('str') table_biz_types[['biz_type_code']] =\ table_biz_types[['biz_type_code']].astype('str') table_master
CSV ファイルに保存
table_master と table_biz_types を CSV ファイルに保存(*2)します(Script 8)。
(*2) ファイルは Google Drive の /content/drive/My Drive/pdf_project/data に保存されます。
- Database に納める時は、
- table_master の biz_description 以外の columns と、
- table_biz_types の 2 つの table を CREATE して、
- biz_type_code を FOREIGN KEY に指定すると良いと思います。
Script 8
table_master.to_csv(os.path.join(DATA_PATH, 'table_master.csv'), index=False) table_biz_types.to_csv(os.path.join(DATA_PATH, 'table_biz_types.csv'), index=False) print('DONE! お疲れ様でした 🍻')
おまけ
上記の script に説明を加えた Jupyter Notebook を pdf_to_csv_norm.ipynb_.zip [10 KB] (Preview)に置いておきました。Colab に upload すればそのまま使えます。
References
[1] pandas User Guide. Comparison with SQL ( Google翻訳 | Bing翻訳 )
[2] pandas User Guide. pandas.DataFrame.update ( Google翻訳 | Bing翻訳 )
[3] pandas User Guide. UPDATE ( Google翻訳 | Bing翻訳 )
[4] pandas User Guide. LEFT OUTER JOIN ( Google翻訳 | Bing翻訳 )