はじめに
Python Advent Calendar 2018 25日目です。
本記事は、「統計学」と「実践編」の2部構成です。
「統計学」は、基本的な標準偏差の概要について記載しています。
「実践編」は、Raspberry PiやDockerを使用して、データ分析環境の構築からお題に沿って簡単なデータ分析についてまとめました。
本記事の環境は以下になります。
Raspberry Piでスクレイピングを行い、取得したデータをCSVファイルに保存します。
取得したCSVファイルをmacのDockerコンテナのJupyter Notebookで参照します。
統計学
データ分析を行うにあたり、統計学は必須のスキルです。
統計学とは得られたデータを基に、その特徴を導き出し活用するための技術であり学問になります。
統計学の歴史は以外に古く、世界中の歴史の中でそのへんりんが見られます。
- 人口調査
- 土地調査
以下、Wikipediaより。
統計学は、経験的に得られたバラツキのあるデータから、応用数学の手法を用いて数値上の性質や規則性あるいは不規則性を見いだす。統計的手法は、実験計画、データの要約や解釈を行う上での根拠を提供する学問であり、幅広い分野で応用されている。
現在では、医学(疫学、EBM)、薬学、経済学、社会学、心理学、言語学など、自然科学・社会科学・人文科学の実証分析を伴う分野について、必須の学問となっている。また、統計学は哲学の一分科である科学哲学においても重要な一つのトピックになっている。
統計学とは
統計学は大まかに言うと、記述統計と推測統計で構成されています。
記述統計は、得られたデータから特徴を導き出す技術です。
また、データを導き出すための具体的なツールとして、度数分布表やヒストグラムが使われます。
推測統計は、統計学の手法と確率理論を合わせた、未来に対する推測を行う方法論のことです。身近な例としては、選挙速報や株価予想に使われています。
本質として、データとは事実を証明し、活用して初めて価値が生まれ、特徴を捉えたり、未来に対する推測を行うことが可能になります。つまり、データは取得しているだけは意味はありません。
すなわち、統計は観測されたデータを活用し、過去に起きたことから規則性を導き出すこと。確率は不確かな未来を予測することになります。
縮約
統計で使われるのは、縮約と呼ばれる、データを何らかの基準で整理整頓して意味のある情報を抽出する作業です。
縮約は大まかに以下の2つの方法があります。
- グラフ化してその特徴を捉える
- 1つの数字でその特徴を捉える(代表する数字「統計量」を導き出す)
ヒストグラム
ヒストグラムとは、度数分布の状態を可視化したグラフのことです。要するに、棒グラフになります。ヒストグラムを作るためには、はじめに、度数分布表を作成します。
標準偏差
統計学の基本は標準偏差になります。
標準偏差を理解することで、統計学の世界についてもっと理解が深まります。
まず、標準偏差を理解するためには、学校で勉強した平均値について改めて学習していきます。
平均値は、分析対象とするデータの広がりに対して、その中から代表数する数値を算出しているだけにすぎません。また、平均値というと「全てのデータを足して個数で割る」だけですが、実際は色々な種類の平均値が存在します。よって、目的に応じて、平均値の出し方は変わってきます。
- 算術平均(全てのデータを足して個数で割る)
\frac{(X+Y)}{2}
- 相乗平均・幾何平均(同じ数を2個掛けて、積xyを求める)
√xy
- 二乗平均(要素を2乗して足して個数で割り、ルートにする)
√\frac{(x^2+y^2)}{2}
- 調和平均
\frac{2}{\frac{1}{x}+\frac{1}{y}}
また、標準偏差に欠かせないのが偏差と分散です。
-
偏差
各データに対する平均値との差 -
分散
単純に言うと、データのばらつき具合を評価するための縮約方法 -
標準偏差
分散のルートをとって、二乗平均した統計量のこと
標準偏差を求めることで、単純な平均でなく、本質的にデータの広がりを評価することができます。もっと言うと、データが大きかったり小さかったりしても、どちらも正の数として評価して、打ち消し合わないように平均されているため、平均値を基にデータの広がり具合からより詳細なデータ分析ができます。
実践編
本記事のお題は、転職ドラフトの第15回ドラフト参加ユーザーランキングより、「最高額(※)」のデータを取得して、市場のデータ分析を行います。
転職ドラフトは、ログインしなくても一部の情報は誰でも見ることができますが、ログインした場合のみ参照可能な情報を取得したいため、スクレイピングしてログインを行い情報を取得します。そのため、Seleniumを使用します。
(※)複数指名をもらった場合で、一番高い提示年収
データ分析環境の構築
本記事の冒頭で記載している図のデータ分析環境を使用します。
なお、Raspberry Piはなくても、mac1台でもオールインワンで構築できます。
本格的にこれから、自前でデータ分析環境を持つ場合に、RaspberryやGCPでデータ分析環境を構築するのがオススメです。メリットして、RaspberryやGCPのインスタンスを使用して、定期的なスクレイピングを行うことができます。(例えば、ノートPC1台では、電源落としたり、持ち歩いたりする場合、定期的なスクレイピングができないため)
環境:
・スクレイピング環境(Raspberry Pi + Selenium + Chromiumドライバ)
詳細は以前書いたRaspberry Piによるスクレイピング 環境構築(Selenium + Chromiumドライバを参照
・データ分析環境(Docker + Jupyter Notebook)
詳細は以前書いたDockerでJupyter Notebookを使うを参照
参考として、GCPのインスタンス作成は以前書いた、これから始めるGCP(GCE) 安全に無料枠を使い倒せを参照
スクレイピング〜データ取得
Raspberry Piに以下のプログラムを配置して実行して、データを取得します。
コンソールからプログラムを実行できるように、Headless(※)で動かします。
画面なしブラウザーでは、「PhantomJS」がありますが、Chromeでもオプションを指定することで画面なしの操作が可能になります。
(※)画面なしのCUI
- draft.py
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# スクレイピングに必要なモジュールをインポート
import sys
sys.path.append('/home/pi/.local/lib/python3.5/site-packages/')
import time
from selenium import webdriver
# Headless Chromeを使うためのオプション
options = webdriver.chrome.options.Options()
options.add_argument('--headless')
# ドライバー設定
browser = webdriver.Chrome(executable_path="/usr/lib/chromium-browser/chromedriver", chrome_options=options)
# ログイン情報
USER = “user”
PASS = “pass”
# ログイン画面を表示
url_login = "https://job-draft.jp/sign_in"
browser.get(url_login)
time.sleep(3)
# print("ログインページにアクセスしました") //デバッグ用
# フォームにメールアドレスとパスワードを入力
e = browser.find_element_by_id("user_email")
e.clear()
e.find_element_by_id("user_email").send_keys(USER)
e = browser.find_element_by_id("user_password")
e.clear()
e.find_element_by_id("user_password").send_keys(PASS)
time.sleep(3)
# フォームを送信
e.find_element_by_xpath("//*[@id=\"new_user\"]/div[4]").click()
# print("ログインしました") //デバッグ用
# 最高額を取得する関数
page = ""
def amount():
url = "https://job-draft.jp/festivals/15/users?page="
url = url+str(page)
browser.get(url)
num = 2
while num < 12:
max_amount = browser.find_elements_by_css_selector("#page-wrapper > div.wrapper-content > div > div > div.col-xs-12.col-sm-12.col-md-8.col-lg-8 > div.ibox > div > div > div:nth-child("+(str(num))+") > div > div.col-xs-9 > div.row > div:nth-child(2) > span.f-w-bold.u-font-ml")
for i in max_amount:
n = i.text
n = n.replace('万円', '')
print(n)
num += 1
# 最後のページまでループ
if not page:
amount()
page = 2
time.sleep(3)
while page < 53:
amount()
page += 1
time.sleep(3)
# ドライバを終了し、関連するすべてのウィンドウを閉じる
browser.quit()
(※)ログイン情報のUSERとPASSは、例
- データ取得(csvに保存)
pi@raspberrypi01:~/python $ python3 draft.py > amount.csv
プロフラム内でcsv呼び出すのはめんどくさいので、リダイレクト(※)して出力させます。
(※)リダイレクトさせる場合に、コマンド終了時にプロンプトが返ってこないので、気をつけること
ナレッジ
-
例外処理
ログイン失敗やページ遷移に失敗した場合などに、Seleniumのクラスで使えそうな例外処理は見つからなかったので、その点考慮する場合は別の対応が必要。 -
Chromiumドライバの残プロセス
Seleniumのプログラムが正常終了しなかった場合、Chromiumドライバの残プロセスが残る場合がある。その場合は、psコマンドで確認してkillする。 -
クエリパラメータ
スクレイピングするときのWebサイトによるが、スクレイピング対象のURL設計により、?page=1で始まらなかったりする。なので、スクレイピングするときは、WebサイトのURL設計をよく確認すること。本記事の場合、page=1で当サイトにアクセスするとリダイレクトされて、別ページに遷移するため、その点考慮してスクレイピングした。
Jupyter Notebookでデータ分析
データ分析は、取得したCSVファイルをmacのDockerで構築したJupyter Notebookより可視化して、分析します。
標準偏差
標準偏差はNumpyの関数で簡単に出力できます。
# csvファイルの読み込み(指名なしも含み計上)
amount = np.loadtxt("./amount_all.csv")
# 要素数を表示
print(amount.size)
# NumpPyの関数
print("平均値:", np.mean(amount))
print("標準偏差:", np.std(amount))
print("中央値:", np.median(amount))
print("最小値:", np.min(amount))
print("最大値:", np.max(amount))
print("----------------------------------")
# csvファイルの読み込み(指名なしを除く)
amount = np.loadtxt("./amount.csv")
# 要素数を表示
print(amount.size)
# NumpPyの関数
print("平均値:", np.mean(amount))
print("標準偏差:", np.std(amount))
print("中央値:", np.median(amount))
print("最小値:", np.min(amount))
print("最大値:", np.max(amount))
第15回ドラフトは、513人参加。その内、379人が指名され、134人が指名なしです。
そのため、「指名なし」を含むか含まないかで大分平均値が変わってくるので、それぞれ出してます。
恐らく、公式の平均提示年収(第15回ドラフト)は、640万円なので、指名なしで算出しているのではないかと想定。
上記より、データを分析してみると、「指名なし」を含むと標準偏差のバラつきが広がることが見てとれます。逆に、「指名なし」を除くと標準偏差のバラつきが狭くなります。
従って、第15回ドラフトの入札結果は指名なしで分析した場合、平均値は688万円ですが、実際の市場価値はその前後におおよそ160万円ほど散らばっています。要約すると、600万円クラスのエンジニアを筆頭に、おおよそ528万円〜848万円で市場が動いているのが特徴です。
matplotlibで可視化
matplotlibで可視化します。
まずはライブラリやCSVファイルを読み込みます。
- コード
# ライブラリの読み込み
import numpy as np
import matplotlib.pyplot as plt
# csvファイルの読み込み
csv_file = open("./amount.csv", "r", newline="" )
X = []
for line in csv_file:
# print(line, end=" ")
X.append(line)
X = X[::-1]
plt.figure(figsize=(10,10))
plt.plot(X)
plt.ylabel('annual income')
plt.show()
グラフで可視化すると、600万円台が多いのが分かります。
これをヒストグラムで見ると、もっと分かりやすいです。
X = X[::-1]
plt.figure(figsize=(20,10))
plt.hist(X, rwidth=10, bins=40)
plt.xlabel('annual income')
plt.show()
ヒストグラムにより、単なる平均値だけでなく、データの散らばり具合の統計量を知ることができます。
さいごに
これからデータ分析を始めるにあたり、事始めとしてAdvent Calendarでやってみました。
かけあしで書いたので、「統計学」の奥深い世界はまだまだですが、データ分析の面白さは体験できました。
AIの民主化が進み、可視化した世界はどんどん広がっていきます。
エンジニアとして、データ分析のスキルをもっと磨いていきたいと思います。