書籍概要

前処理大全
[データ分析のためのSQL/R/Python実践テクニック]

著者
発売日
更新日

概要

データサイエンスの現場において,その業務は「前処理」と呼ばれるデータの整形に多くの時間を費やすと言われています。「前処理」を効率よくこなすことで,予測モデルの構築やデータモデリングといった本来のデータサイエンス業務に時間を割くことができるわけです。本書はデータサイエンスに取り組む上で欠かせない「前処理スキル」の効率的な処理方法を網羅的に習得できる構成となっています。ほとんどの問題についてR,Python,SQLを用いた実装方法を紹介しますので,複数のプロジェクトに関わるようなデータサイエンスの現場で重宝するでしょう。

目次

  • はじめに
  • 0-1 本書の目的
  • 0-2 対象読者
  • 0-3 本書の構成

Part1 入門前処理
第1章 前処理とは

  • 1-1 データ
  • 1-2 前処理の役割
  • 1-3 前処理の流れ
  • 1-4 3つのプログラミング言語
  • 1-5 パッケージ/ライブラリ
  • 1-6 データセット
  • 1-7 データの読み込み

Part2 データ構造を対象とした前処理
第2章 抽出

  • 2-1 データ列指定による抽出
  • 2-2 条件指定による抽出
  • 2-3 データ値に基づかないサンプリング
  • 2-4 サンプリング

第3章 集約

  • 3-1 データ数,種類数の算出
  • 3-2 合計値の算出
  • 3-3 極値,代表値の算出
  • 3-4 ばらつき具合の算出
  • 3-5 最頻値の算出
  • 3-6 順位の算出

第4章 結合

  • 4-1 マスタテーブルの結合
  • 4-2 条件に応じた結合テーブルの切り替え
  • 4-3 過去データの結合
  • 4-4 全結合

第5章 分割

  • 5-1 レコードデータにおけるモデル検証用のデータ分割
  • 5-2 時系列データにおけるモデル検証用のデータ分割

第6章 生成

  • 6-1 アンダーサンプリングによる不均衡データの調整
  • 6-2 オーバーサンプリングによる不均衡データの調整

第7章 展開

  • 7-1 横持ちへの変換
  • 7-2 スパースマトリックスへの変換

Part3 データ内容を対象とした前処理
第8章 数値型

  • 8-1 数値型への変換
  • 8-2 対数化による非線形な変化
  • 8-3 カテゴリ化による非線形な変化
  • 8-4 カテゴリ化による非線形な変化
  • 8-5 外れ値の除去
  • 8-6 主成分分析による次元圧縮
  • 8-7 数値の補完

第9章 カテゴリ型

  • 9-1 カテゴリ型への変換
  • 9-2 ダミー変数化
  • 9-3 カテゴリ値の集約
  • 9-4 カテゴリ値の組み合わせ
  • 9-5 カテゴリ型の数値化
  • 9-6 カテゴリ型の補完

第10章 日時型

  • 10-1 日時型,日付型への変換
  • 10-2 年/月/日/時刻/分/秒/曜日への変換
  • 10-3 日時差への変換
  • 10-4 日時型の増減
  • 10-5 季節への変換
  • 10-6 時間帯への変換
  • 10-7 平休日への変換

第11章 文字型

  • 11-1 形態素解析による分解
  • 11-2 単語の集合データに変換
  • 11-3 TF-IDFによる単語の重要度調整

第12章 位置情報型

  • 12-1 日本測地系から世界測地系の変換,度分秒から度への変換
  • 12-2 2点間の距離,方角の計算

Part4 実践前処理
第13章 演習問題

  • 13-1 集計分析の前処理
  • 13-2 レコメンデーションの前処理
  • 13-3 予測モデリングの前処理

サポート

正誤表

本書の以下の部分に誤りがありました。ここに訂正するとともに,ご迷惑をおかけしたことを深くお詫び申し上げます。

(2018年11月1日最終更新)

P.91 上部のSQLのコード(sql_2_awesome.sql)


-- ホテルテーブルからビジネスホテルのデータのみ抽出
WHERE hotel.is_business is True


-- ホテルテーブルからビジネスホテルのデータのみ抽出
  AND hotel.is_business is True

P.91 Pointの文章

このコードでは、JOIN句と同じクエリにWHERE句を指定しています。
このコードでは、ON句の結合条件に加えて、抽出条件を指定しています。

(以下2018年9月21日更新)

P.93

また、テーブル間で列名が異なるキーを結合キーとして設定することはできません。
テーブル間で列名が異なる場合は、left_onとright_on引数を利用してください。

P.107


result['before_price'] = \
  pd.Series(result['total_price'].groupby('customer_id').shift(periods=2))


result['before_price'] = \
  result['total_price'].groupby('customer_id').shift(periods=2)


(以下2018年7月20日更新)

P.169の5行目,6行目
P.171のRコードの解説2行目

P.64 r_awesome.r の解説3行目

自然数
ネイピア

(以下2018年6月12日更新)

P.64 r_awesome.r の解説3行目

reserve_tb.summary()のようにsummary関数を呼び出すことで、
reserve_tb %>% summary()のようにsummary関数を呼び出すことで

P.69 Point

data.frameのNAの置換によって実現しています。
coalesce関数によって実現しています。

P.73 ファイル名

r_awesome.R
r_not_awesome.R

P.80 python_awesome.pyの解説1行目

rank関数は、R同様に順位付けを行う関数です。
rank関数は、順位付けを行う関数です。

P.150 r_awesome.py

以下のコードに差し替えてください。


# ubBalance用のライブラリ
library(unbalanced)
library(tidyverse)

# percOverの設定値の計算
t_num <- production_tb %>% filter(fault_flg==T) %>% summarize(t_num=n())
f_num <- production_tb %>% filter(fault_flg==F) %>% summarize(f_num=n())
percOver <- round(f_num / t_num) * 100 - 100

# 不均衡を正す対象をfactor型に変換(logical型ではないことに注意)
# (第9章「9-1 カテゴリ型」の例題で解説)
production_tb$fault_flg <- as.factor(production_tb$fault_flg)

# ubBalance関数でオーバーサンプリングを実現、typeにubSMOTEを設定
# positiveは分類で少ない方の値を指定(指定しないことも可能だが警告が表示される)
# percOverは、元データから何%増やすかを設定
# (200なら3(200/100+1)倍、500なら6(500/100+1)倍となる。100未満の値は切り捨て)
# percUnderはアンダーサンプリングを行うときに必要だが、行わない場合は0で設定
# kはsmoteのkパラメータ
production_balance <-
  ubBalance(production_tb[,c('length', 'thickness')],
            production_tb$fault_flg,
            type='ubSMOTE', positive='TRUE',
            percOver=percOver, percUnder=0, k=5)

# 生成したfault_flgがTRUEのデータと元のfault_flgがFALSEのデータを合わせる
bind_rows(

  # production_balance$Xに生成したlengthとthicknessのdata.frameが格納
  production_balance$X %>%

    # production_balance$Yに生成したfault_flgのベクトルが格納
    mutate(fault_flg=production_balance$Y),

  # 元のfault_flgがFalseデータの取得
  production_tb %>%

    # factor型なので一致判定で取得
    filter(fault_flg == 'FALSE') %>%
    select(length, thickness, fault_flg)
)


(以下2018年5月18日更新)

P.112 python_awesome.py

以下のコードに差し替えてください。

# customer_idごとにreserve_datetimeでデータを並び替え
result = reserve_tb.groupby('customer_id') \
  .apply(lambda x: x.sort_values(by='reserve_datetime', ascending=True)) \
  .reset_index(drop=True)

# 新たな列としてprice_sumを追加
result['price_sum'] = pd.Series(
    # 必要なデータ列のみに絞り込み
    result.loc[:, ["customer_id", "total_price"]]

    # customer_idごとにtotal_priceのwindow3件にまとめ、その合計値を計算
    .groupby('customer_id')
    .rolling(center=False, window=3, min_periods=3).sum()

    # group化を解除すると同時に、total_priceの列を取り出し
    .reset_index(drop=True)
    .loc[:, 'total_price']
)

(以下2018年5月17日最終更新)

P.107 python_awesome.py 

以下のコードに差し替えてください。

# customerごとにreserve_datetimeで並び替え
# groupby関数のあとにapply関数を適用することによって、groupごとに並び替える
# sort_values関数によってデータを並び替え、axisが0の場合は行、1の場合は列を並び替え
result = reserve_tb \
  .groupby('customer_id') \
  .apply(lambda group:
         group.sort_values(by='reserve_datetime', axis=0, inplace=False))

# resultはすでに、customer_idごとにgroup化されている
# customerごとに2つ前のtotal_priceをbefore_priceとして保存
# shift関数は、periodsの引数の数だけデータ行を下にずらす関数
result['before_price'] = \
  pd.Series(result['total_price'].groupby('customer_id').shift(periods=2))

P116 python_not_awesome.py

以下のコードに差し替えてください。

# customer_idごとにreserve_datetimeでデータを並び替え
result = reserve_tb.groupby('customer_id') \
  .apply(lambda x: x.sort_values(by='reserve_datetime', ascending=True)) \
  .reset_index(drop=True)

# 新たな列としてprice_avgを追加
result['price_avg'] = pd.Series(
  result
    # customer_idごとにtotal_priceのwindow3件にまとめ、その平均値を計算
    # min_periodsを1に設定し、1件以上あった場合には計算するよう設定
    .groupby('customer_id')
    ['total_price'].rolling(center=False, window=3, min_periods=1).mean()

    # group化を解除すると同時に、customer_idの列を削除
    .reset_index(drop=True)
)

# customer_idごとにprice_avgを1行下にずらす
result['price_avg'] = \
  result.groupby('customer_id')['price_avg'].shift(periods=1)


(以下2018年5月15日更新)

P.65 Pythonによる前処理の3行目

NumPyライブラリのquantile関数
NumPyライブラリのpercentile関数

(以下2018年5月7日更新)

P.274 「12−1 日本測地系から正解測地系の変換,度分秒から度への変換」の10行目

「35度30分15秒」は30秒は0.25(15/60)分なので
「35度30分15秒」の15秒は0.25(15/60)分なので

P.171 Pythonによる前処理 コードの2行目


  reserve_tb['total_price'].apply(lambda x: np.log(x / 1000 + 1))


  reserve_tb['total_price'].apply(lambda x: np.log10(x / 1000 + 1))

P.171 Pythonによる前処理 コード解説1行目

NumPyライブラリのlog関数は対数を計算する関数です。1つ目の引数に対数化する値を指定します。2つ目の引数に対数の底を指定します。2つ目の引数を省略した場合は、10が対数の底に指定されます。
NumPyライブラリのlog関数は対数を計算する関数です。引数には対数化する値を指定します。log関数は対数の底に自然数が設定されます。log2関数を利用すると対数の底は2に設定され、同様にlog10関数では10が設定されます。

(以下2018年4月16日更新)

P88 図4.2 (INNER) JOINの右側のid_aの値

a_1 a_2 a_3
a_1 a_2 a_4

P195 sql_awesome.sqlの4行目から6行目

SELECT
  type,
  length,

  -- thicknessの欠損値を1で補完
  COALESCE(thickness, 1) AS thickness,
  fault_flg
FROM work.production_missn_tb
SELECT
  type,
  length,
  COALESCE(thickness,
           (SELECT AVG(thickness) FROM work.production_missn_tb))
    AS thickness,
  fault_flg
FROM work.production_missn_tb

商品一覧