前回の記事では、Pythonでwebスクレイピング(PDFファイル抽出)してみました。
【Python】英文PDFファイルをスクレイピングして結合・一括翻訳する方法

きょうは、
- Pythonの tabula というライブラリを使い、PDF内の『表』を抽出整形し、
- describeメソッドで要約統計量を取得みようと思います。また、
- 統計情報をもとに『Jupyter Notebook』で matplotlib や seaborn を使い、美しいグラフを描画する方法を解説します。
今回は、例として中小企業診断士第一次試験の統計情報(10年分:h22~h31)をつかってみたいと思います。
PDFの『統計表』を抽出
必要なライブラリをインポート
まずはライブラリの import 。
1 2 3 4 5 6 7 |
#必要なライブラリをインポート import pandas as pd #データ解析用ライブラリ import tabula #PDF内の表を抜き出すライブラリ import matplotlib.pyplot as plt #グラフ描画ライブラリ from pylab import rcParams # グラフのレイアウトを調整するモジュール import seaborn as sns #データの可視化を行うライブラリ sns.set() #デフォルトスタイルにseabornのを適用 |
PDFデータ(中小企業診断士統計資料)のURLをリスト化
つぎにweb上にあるPDFデータの URL をリスト化していきます。
中小企業診断士第一次試験の統計情報(10年分:h22~h31)を使います。
オリジナルPDFファイル(令和元年度)はこちら↓このデータを10年分抽出します。映り込んじゃったみどりのぞうさん(Evernote)は気にしないでください。
いちばん下の表:「科目受験者数・科目合格者数」を抽出します。
URL をリスト化するコードはこちら↓
1 2 3 4 5 6 7 8 |
#取得するpdfデータのURLをリスト化 urls_list1 = ['https://www.j-smeca.jp/attach/test/H{0}/h{0}_1ji_toukei.pdf'.format(i) for i in range(22,25)] urls_list2 = ['https://www.j-smeca.jp/attach/test/h{0}/h{0}_1ji_toukei.pdf'.format(i) for i in range(25,27)] urls_list3 = ['https://www.j-smeca.jp/attach/test/h27/h27_1ji_toukei.pdf.pdf'] urls_list4 = ['https://www.j-smeca.jp/attach/test/h{0}/h{0}_1ji_toukei.pdf'.format(i) for i in range(28,32)] #上記URLリストをひとつのリストにまとめる url_lists = urls_list1 + urls_list2 + urls_list3 + urls_list4 |
tabulaで表を抽出(for文でdf[]に追加)
tabula でPDF内の表を pandas.DataFrame (データフレーム)として抽出します。for文で各年度の統計情報をアペンド。
1 2 3 4 5 |
#tabulaで目的の表を取得し、for文でdf[]に追加 df = [] #dfをいれるための空のリストを作成 for url in url_lists: data = tabula.read_pdf(url , pages='all') df.append(data[5]) |
オリジナルデータを確認します。以下はh22のデータ。グラフ作成や統計情報を確認したいので、ちょっと整えていきます(前処理です)。
1 |
df[0] #オリジナルデータの確認 |
データ整形(前処理)の関数定義
オリジナルデータを整形していきます。まずは関数定義。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#元の表データの前処理をする関数定義 def preprocessing1(df_original): df = df_original.copy() df.drop('6.科目受験者数・', axis=1, inplace=True) #1列目の”6.科目受験者数・”を削除(inplace=Trueで元データに反映させる) df = df.rename(columns={'Unnamed: 0': '科目名'}) #”Unnamed: 0”のカラム名を”科目名”に変更 df.iat[3, 1] = df.iat[4, 1] #3行1列目の”13251”を4行1列目の”NaN”にコピー df.iat[3, 2] = df.iat[4, 2] #3行2列目の”2416”を4行2列目の”NaN”にコピー df.drop(df.index[[4, 5]], inplace=True) #4,5行を削除 df['科目合格者数'] = df['科目合格者数'].str.replace(',', '').astype(int) #”科目合格者数”のカンマ(,)を削除して文字列から数値へ変換 df['科目受験者数'] = df['科目受験者数'].str.replace(',', '').astype(int) #”科目受験者数”のカンマ(,)を削除して文字列から数値へ変換 df['科目合格率'] = df['科目合格者数'] / df['科目受験者数'] * 100 #新たに”科目合格率”を追加 df.drop(df.columns[[1, 2]], axis=1, inplace=True) #”科目受験者数”と”科目合格者数”を削除 return df.round(2) #dfを返す(小数第2位まで表示) |
‘科目合格者数’と’科目合格率’の列の数字については、あらたに’科目合格率’を追加する必要があったため、データタイプを int (整数)に変換。
オリジナルデータのデータタイプは、下のようにすべて object でしたので、、、
1 |
df[0].dtypes #dfのデータタイプを確認 |
for文で前処理実行(10年分) + df2[ ]に追加
for文で各年度のデータ整形 + 空のリスト(: df2 = [] )にアペンド↓
1 2 3 4 5 |
に追加していく for data in df:"]df2 = [] #空のリストを作成 #for文で前処理データをdf2[]に追加していく for data in df: data = preprocessing1(data) df2.append(data) |
平成22年度のデータをベースにfor文で各年度の科目合格率を追加
平成22年度のデータをベースにfor文で各年度の科目合格率を追加していきます。
1 2 3 |
#for文で平成22年度のdfをベースに各年度の科目合格率を追加する for i in range(9): df2[0]['h'+str(23+i)] = df2[i+1]['科目合格率'] |
行列を転置して扱いやすくする
行列を .T メソッドで転置(入れ替え)して後々扱いやすい pandas.DataFrameにします↓
1 |
df2[0] = df2[0] .T #行列を転置(入れ替え)して後々扱いやすいdfにしておく |
科目名を英語名に変更
科目名を. rename で英語名に変更します。グラフ作成は英語の方が扱いやすいです。日本語で表示できないこともないのですが、設定が面倒なので、、、
ついでにh22データの 科目合格率 も h22 へ名前変更。
1 2 3 4 5 6 7 8 9 |
#科目名を英語に変更(グラフ作成は英語の方が扱いやすいです。。。) df2[0] = df2[0].rename(columns={0: 'Economic_policy', 1: 'Accounting', 2: 'Business_theory', 3: 'Operating', 6: 'Legal_affairs', 7: 'Information_system', 8: 'Corporate_policy'}, index={'科目合格率': 'h22'}) #"科目合格率"を"h22"に変更 |
↓このようになります。
先頭の 科目名 の行はもう必要なし。 .drop で削除します。 axis=0 で方向指定(0:横、1:縦)
1 |
df2[0] = df2[0].drop('科目名', axis=0) #日本語の科目名を削除 |
このようになります↓
一次試験全体の合格率を追加
さいごに各年度一次試験全体の合格率 Pepr を追加します。
1 2 3 4 5 6 7 8 9 10 11 |
#各年度の一次試験合格率の列を追加 df2[0]['Pepr'] = [15.9, 16.4, 23.5, 21.7, 23.2, 26.0, 17.7, 21.7, 23.5, 30.2] |
このようになります↓これで前処理完了。
matplotlibでグラフ描画
グラフの設定と描画(matplotlib)
前処理が完了したので、まず matplotlib でグラフを描画してみます。
1 2 3 4 5 6 |
rcParams['figure.figsize'] = 14,7 #グラフのサイズを指定 df2[0].plot(marker="o") #dfのグラフ描画とマーカーの形状を指定 plt.title('Primary_exam_pass_rate') #グラフのタイトルを指定 plt.xlabel('Year') #横軸のラベルを指定 plt.ylabel('Pass_rate') #縦軸のラベルを指定 plt.show() #グラフを描画 |
こちらがそのグラフ↓ Pepr (一次試験全体の合格率)以外はバラつきがすごいですね。
ちょっとこれだけじゃよくわからないので、要約統計量を確認したいと思います。
describeメソッドで要約統計量の確認
まずは、前処理。データタイプの変更です。 objectのままでは統計処理ができないので float に変更します。
変更前のデータタイプがこちら↓
1 |
df2[0].dtypes #データタイプの確認 |
.astype(float) でデータタイプを float 型(浮動小数点)に変更。
1 |
df2[0] = df2[0].astype(float) #データタイプを"float"(浮動小数点)に変更 |
もう一度データタイプを確認します。
1 |
df2[0].dtypes #データタイプの確認 |
これで、データタイプの変更完了。
続いて describe() メソッドで要約統計量を確認します。 .round(2) で小数第2位まで表示。
1 |
df2[0].describe().round(2) #descraibeで統計情報を表示(少数第2位まで) |
要約統計量から読み取れる特徴は、
- 経営法務の mean, std, 50%および maxは7科目中もっとも小さい。安定して難度高い。
- 経営情報システムの stdおよび maxは7科目中もっとも大きい。が、 50%と 75%の差が1未満。バラつきが大きい。
- minは7科目すべて1桁台(7%以下)。
- 1次試験全体の合格率 Pepr はバラつきが小さい。 mean:21.98%, std:4.43%。7科目のバラつき(標準偏差)は伝播してない。むしろポートフォリオが組まれバラつきが小さくなる。
と、まだまだ色々読み取れそうですが、、、どうも科目間の相関関係がありそう。。。
ということで次回は、各科目間の相関関係を Python を使って見ていきたいと思います。
Pythonならたった1行のコードでこんな感じで相関関係が視覚化できます↓
お楽しみに!
参考サイトと講座
今回、以下のサイトと講座を参考にコーディング、統計分析しました。
– DATA SCIENCE LIFE –
アメリカ在住データサイエンティストのかめ(@usdatascientist)さんのブログ。
Pythonのコーディングについて、めちゃくちゃわかりやすく解説してあります。超ハイクオリティな講座でほんとにぜんぶ無料なの?ってなるくらいすごいブログです。
Python初心者の方にはかなりお勧めです。
note.nkmk.me
nkmk(@nkmk_me)さんのブログです。
こちらもPythonプログラミングについて、かなり詳しく書いてあります。
現役シリコンバレーエンジニア(酒井 潤さん)が教えるPython 3 入門(Udemy)
現役シリコンバレーエンジニアの酒井 潤(@sakaijun)さんがUdemyで教えるPython講座もおすすめです。なんといっても解説がとても分かりやすい。
現役シリコンバレーエンジニアが教えるPython 3 入門 + 応用 +アメリカのシリコンバレー流コードスタイル
この講座ではPython3の基礎はもちろんのこと、応用編では
- データーベースアクセス(SQLite, MySQL, MongoDB, SQLAlchemy etc)、
- WEB(Flask, socket, RPC etc)、
- インフラ自動化(Fabric, Ansible)、
- データ解析(numpy, pandas, matplotlib, scikit-learn),
などなど、盛りだくさんの内容となっております。
現在、Udemyはセール中で、かなりの講座が大幅割引(90%ぐらい?)されています。本日(2020/6/4 24:00)までみたいですね。
興味のある方はぜひのぞいてみてください。