データ分析とは何か：ビジネス課題の特定からEDAまで

以前、こちらのブログ記事を書きました。3年前です。

[データ分析の目的とプロセス - Databricks を活用したデータ分析によるビジネス価値の創出](https://www.databricks.com/jp/blog/2021/06/29/data-analytics-a-to-z-with-databricks-jp.html)

こちらでは自分なりに**データ分析とは何か**をまとめたものとなっていますが、Sparkベースで話を進めているのと、Databricksの機能も3年前のものを参照しているので、今回はpandasベースであらためて振り返ってみます。今回は機械学習モデルの構築までは踏み込まず、前半部分となるEDA(Exploratory Data Analysis:探索的データ分析)までをカバーします。

# データ分析とは何か

- データ分析はビジネス上の課題を解決するための手段の1つです。
- データ分析においては、ビジネス洞察を得るためにデータの加工、集計を行います。得られた洞察をアクションに繋げることで収益の改善、コスト削減などのビジネス価値を生み出すことが目的となります。
- データ分析というと機械学習モデルが脚光を浴びがちですが、それはデータをビジネス価値につなげる長い道のりのほんの一部です。

![Screenshot 2024-05-28 at 11.23.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/f60166b7-7eef-dbbe-40e6-c49b6ae88d13.png)

# データ分析の（終わり無き）長いプロセス

今回はステップ1から6にフォーカスします。

1. ビジネス課題の特定
1. データ分析における仮説の立案
1. データ分析アプローチの検討
1. データソースの調査、分析データの入手
1. 分析データの読み込み
1. 探索的データ分析（EDA: Exploratory Data Analysis）
1. 分析データの前処理
1. 分析アルゴリズムの検討
1. 分析パイプラインのレビュー
1. モデルの構築
1. モデルの評価
1. モデルのチューニング
1. モデルのデプロイ
1. 精度・性能のモニタリング

# ステップ 1〜4：ヒアリングやディスカッション

いきなりデータ分析を始めるプロジェクトはまず存在しません。データ分析には必ずビジネスにつながる目的があるべきです。

## ステップ 1：ビジネス課題の特定

あるマーケティング担当者の悩み：「マーケティングを効率的に進めるために、年収の高いお客様を簡単に特定できないだろうか？」

**ビジネス課題：** 富裕層を特定することによるマーケティングの効率化

![Screenshot 2024-05-28 at 11.25.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/6bc0affe-ea99-4a67-9739-54bf0bd83e16.png)

## ステップ 2：データ分析における仮説の立案
あるデータサイエンティストの思い：「デモグラフィック情報から収入を予測できれば、ユーザー情報登録時に年収を予測できるのではないか？」

**データ分析における仮説：** デモグラフィック情報から年収を予測できる

![Screenshot 2024-05-28 at 11.25.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/f5c315ed-1f3e-1e42-3bc7-df01782894ad.png)

## ステップ 3：データ分析アプローチの検討
マーケティング担当者とデータサイエンティストの議論：「具体的な年収を予測するのではなく、年収が一定額以上か未満かを識別するだけで十分ではないか」

**データ分析アプローチ：** 年収が 5 万ドルを超えるか否かを分類する二値分類問題に取り組む

![Screenshot 2024-05-28 at 11.27.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/fb69054a-d1a4-dc47-f8b6-a31cf0d3dcac.png)

## ステップ 4：データソースの調査、分析データの入手

データサイエンティストと[DWH](https://www.databricks.com/jp/glossary/data-warehouse)担当者の会話：「過去に蓄積したデモグラフィック情報と年収情報は利用できそうだ」

**分析データ:** 過去に蓄積したデモグラフィック情報、年収情報

![Screenshot 2024-05-28 at 11.27.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/2ceebc83-d984-c694-17ac-ed7c046c7836.png)

# ステップ 5：データの読み込み

前のステップで特定したデータを分析環境に読み込みます。分析データはファイル、データベースに格納されているテーブル、ストリーミングデータであったりします。

ファイルである場合、CSVファイル、JSONファイル、Parquetファイル、Deltaファイルなど様々な**フォーマット**が存在するので、それらに合わせた読み込み手順が必要となります。

- CSVファイル：カンマ区切りの表形式のデータ
- JSONファイル：入れ子構造を表現できるデータ形式
- Parquet/Deltaファイル：大規模データを高速に処理できる列指向データフォーマット、DeltaはDatabricksのデフォルトフォーマットです。

また、読み込むデータは、数値、テキスト、画像、音声など様々な形式となるので、それらに合わせたデータの加工が必要となります。

- 数値：計算処理、集計処理
- テキスト：文字列操作、形態素解析など
- 画像：白黒処理、サイズ圧縮など

# ステップ 6：探索的データ分析（EDA: Exploratory Data Analysis）

データを理解するためのEDAは非常に重要です。EDAを通じて取り扱うデータの素性を理解することで、以降のデータ分析での手戻りを減らすことができます。

こちらの記事にはEDAの観点がまとめられています。

https://toukei-lab.com/eda

- どこにどのようなデータがあるか確認する
- データの質と量を大まかに把握する
- 各種統計量を把握する
- 相関関係を確認する
- 欠損値を把握する
- 外れ値を把握する
- 集計やドリルダウンを通してデータを可視化する

以降のステップは人気のプログラミング言語[Python](https://www.python.org/)とデータ分析ライブラリ[Pandas](https://pandas.pydata.org/)を用いて実施していきます。こちらでは、Databricksの環境に格納されているサンプルデータを用いて、EDAを行います。

# Pythonとは

https://ja.wikipedia.org/wiki/Python

コンパイルが不要なインタプリタ型のプログラミング言語です。インデント(行の最初に空白を挿入して字下げすること)でコードブロックを記述するのが特徴的です。データ分析で使うプログラミング言語のデファクトと言えます。

# pandasとは

https://ja.wikipedia.org/wiki/Pandas

Pythonでデータ分析を行う際には必須のライブラリです。テーブルデータの取り扱いを容易にする**データフレーム**を操作できる様になります。

# EDA実践

サンプルデータはこちらとなります。デモグラフィック情報と年収が含まれているアメリカの国勢調査のデータとなります。

https://www.cs.toronto.edu/~delve/data/adult/desc.html

列の説明はこちらにも記載されています。

https://www.kaggle.com/datasets/uciml/adult-census-income

## データの確認

データを分析するには読み込みが必要です。まずは、生のデータを確認します。

```py
%fs head databricks-datasets/adult/adult.data
```

ここでは`%fs`マジックコマンドを用いて`/databricks-datasets/adult/adult.data`にあるファイルの中身を表示しています。

```
[Truncated to first 65536 bytes]
39, State-gov, 77516, Bachelors, 13, Never-married, Adm-clerical, Not-in-family, White, Male, 2174, 0, 40, United-States, <=50K
50, Self-emp-not-inc, 83311, Bachelors, 13, Married-civ-spouse, Exec-managerial, Husband, White, Male, 0, 0, 13, United-States, <=50K
38, Private, 215646, HS-grad, 9, Divorced, Handlers-cleaners, Not-in-family, White, Male, 0, 0, 40, United-States, <=50K
```

これから以下の様なことがわかります。

- 1行が1人の個人を表現している。
- 各列はカンマで区切られている。すなわち、CSVファイルである。
- 数字や文字列が含まれている。
- 列名(ヘッダー)が無い。

## データの読み込み

pandasを用いてCSVファイルをデータフレームに読み込みます。ヘッダーがないので`names`で各列のヘッダーを指定しています。

```py
# pandasをpdとしてインポート
import pandas as pd

data = pd.read_csv(
    # ファイルのパスを指定
    "/dbfs/databricks-datasets/adult/adult.data",
    # ヘッダーなし
    header=None,
    # ヘッダーを指定
    names=[
        "age",
        "workclass",
        "fnlwgt",
        "education",
        "education_num",
        "marital_status",
        "occupation",
        "relationship",
        "race",
        "sex",
        "capital_gain",
        "capital_loss",
        "hours_per_week",
        "native_country",
        "income",
    ],
)
# データフレームの中身を表示
data
```
![Screenshot 2024-05-28 at 12.48.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/90866f46-0a60-d741-2d1d-042aab4e260d.png)

なお、Databricksではデータフレームに[display関数](https://docs.databricks.com/ja/visualizations/index.html)を適用することで、データフレームの並び替えや可視化を行うこともできます。

```py
display(data)
```

![Screenshot 2024-05-28 at 12.15.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/7ba9a66f-0150-ba8a-3ab5-1af57bf5f7e4.png)
![Screenshot 2024-05-28 at 12.15.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/aa984450-dfc1-ee96-ebac-2aa1c7e96ab3.png)

こちらからは以下の様なことがわかります。

- `age`は年齢
- `workclass`は
- `education`は学歴
- `occupation`は職業
- `sex`は性別
- `capital_gain`は資産のゲイン
- `capital_loss`は資産のロス
- `income`が年収。

また、データフレームの各列のデータ型も確認しておきましょう。

```py
print(data.dtypes)
```
```
age                int64
workclass         object
fnlwgt             int64
education         object
education_num      int64
marital_status    object
occupation        object
relationship      object
race              object
sex               object
capital_gain       int64
capital_loss       int64
hours_per_week     int64
native_country    object
income            object
dtype: object
```

こちらからはデータ型にint64(整数)とobject(文字列)があることがわかります。

ここまでで、

- どこにどのようなデータがあるか確認する
- (一部)データの質と量を大まかに把握する

は実施した形になります。

- 各種統計量を把握する
- 相関関係を確認する
- 欠損値を把握する
- 外れ値を把握する
- 集計やドリルダウンを通してデータを可視化する

## 各種統計量の把握

`describe`を使います。

```py
data.describe()
```
![Screenshot 2024-05-28 at 17.36.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/12f92096-d058-1966-1ead-5a15451fcd8b.png)

こちらからは以下の様なことがわかります。

- `count`から件数は32561件
- `mean`からは平均値
- `std`からは標準偏差、一番ばらつきがあるのは`capital_gain`
- `min`から`max`は値の分布

上はテーブルのデータや統計情報を見てきましたが、ここからはグラフで可視化していきます。テーブルデータのみでデータの概観を掴むのには限界がありますので、グラフを活用していきます。

Python + pandasでの可視化には[matplotlib](https://matplotlib.org/)や[seaborn](https://seaborn.pydata.org/)のライブラリを活用しますが、ここではseabornを使ってきます。

## 相関関係の確認

あまりに相関の高い列がデータに含まれている場合、どちらかを除外することを検討した方がいいかもしれません。除外しない場合でも、列間の相関関係を理解することでデータの性質の理解の助けになります。

まずは[ペアプロット](https://note.nkmk.me/python-seaborn-pandas-pairplot/)を作成してみます。

```py
# seabornをsnsとしてインポート
import seaborn as sns
# ペアプロット
sns.pairplot(data)
```

数値型の列同士の関係性が散布図として表示されます。対角線のグラフは当該列のデータの分布です。
![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/73163678-12a2-3ecc-f1ec-66c7296b5699.png)

パッとみた感じでは列間の相関はそれほどなさそうです。ただ、このグラフでは数値型のデータしか表示されていないことに注意してください。文字列で表現される列は含まれていません。

次は、相関行列を計算した上でヒートマップとして可視化します。

```py
# 相関行列を取得
corrmat = data.corr()
# ヒートマップとしてプロット
sns.heatmap(corrmat, annot=True, fmt="1.3f")
```
![download (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/eab3d0e3-5f55-38c3-0ffd-1a8da8c17fad.png)

こちらからも列間の大きな相関は認められません。同じ列同士の相関係数が1.0になるのは当たり前です。

## 欠損値の把握

データが空の状態を欠損値(missing value)と呼びます。あまりに欠損値の多い列は今後の分析の妨げとなる場合がありますので、列の除外を検討した方がいいかもしれません。しかし、場合によっては欠損値に意味がある場合があります。例えば、センサーデータの読み取り値が欠損値になっている場合、センサーの故障を意味する場合があります。

ここでは、`isna`を用いて欠損値を取得し、`sum`で件数をカウントしています。

```py
data.isna().sum()
```
```
age               0
workclass         0
fnlwgt            0
education         0
education_num     0
marital_status    0
occupation        0
relationship      0
race              0
sex               0
capital_gain      0
capital_loss      0
hours_per_week    0
native_country    0
income            0
dtype: int64
```

欠損値はありませんでした。

## 外れ値の把握

今回はカバーしませんが、後半部分となる機械学習モデルの構築においては極端な値(外れ値: outlier)がモデルのパフォーマンスに影響を及ぼす場合があり、その場合には外れ値を除外することも検討する必要があります。

こちらの記事を参考にボックスプロットを作成します。

- [pandas\.DataFrameから特定の型の列を抽出・除外するselect\_dtypes \| note\.nkmk\.me](https://note.nkmk.me/python-pandas-select-dtypes/)
- [python \- Boxplot of Multiple Columns of a Pandas Dataframe on the Same Figure \(seaborn\) \- Stack Overflow](https://stackoverflow.com/questions/49554139/boxplot-of-multiple-columns-of-a-pandas-dataframe-on-the-same-figure-seaborn)

```py
import matplotlib.pyplot as plt

# 数値列のみのデータを取得
numerical_data = data.select_dtypes(include=int)

# カラムのリストを保持
columns_to_plot = numerical_data.columns

# 図とサブプロットを作成
fig, axes = plt.subplots(ncols=len(columns_to_plot))

# Seabornでボックスプロットを作成
for column, axis in zip(columns_to_plot, axes):
        sns.boxplot(data=data[column], ax=axis) 
        axis.set_title(column)

# プロットの表示
plt.tight_layout()
plt.show()
```

fnlwgtやcapital_gain、hours_per_weekなどでは外れ値が顕著となっています。

![download (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/f3fcbf6e-f8d5-5390-0683-2a0b0e5c88b7.png)

## 集計やドリルダウンを通じたデータの可視化

ここまでの取り組みは自分自身を分析対象データに慣れ親しませることも意図しています。いろいろな角度から可視化を行うことで、データの傾向、特徴などに習熟してきている訳です。データに慣れ親しむことで、今後のデータ分析がより円滑に進む様になります。

ここまでは、seabornでプログラムを記述して可視化を行ってきましたが、ここからはDatabricksの`display`関数が提供する可視化機能を活用して、ノーコードで可視化を行っていきましょう。

display関数の結果画面で **+ > 可視化** を選択します。
![Screenshot 2024-05-28 at 18.17.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/04d0a25e-a72a-0ba2-8ff4-9ee09cf7d951.png)

年齢の分布を見てみましょう。
![Screenshot 2024-05-28 at 18.18.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/0f78b3f2-fc0c-9ffe-8333-e856f4652dc7.png)
![Screenshot 2024-05-28 at 18.20.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/f51d4453-fd60-0e9f-0b93-78aab8f4a5bc.png)

今度は **+ > データプロファイル**を選択します。
![Screenshot 2024-05-28 at 18.20.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/e9f8976f-c0d3-038f-2b62-f0ec08045ed8.png)

ここまで、pandasのメソッドなどで行ってきたEDAの処理を一括で行うことができます。
![Screenshot 2024-05-28 at 18.21.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/3f8811c5-72e3-30df-1faf-a8c2aae9c22d.png)

## ちょっと待ってください

ここまでは数値データにしか着目してきませんでした。そのため、重要な`income`を分析に含めていなかったことになります。[ステップ 1：ビジネス課題の特定](#ステップ-1ビジネス課題の特定)を思い出しましょう。

`income`を分析に含めるために、文字列データを数値データに変換します。この様なテクニックには[one hot encoding](https://qiita.com/zumax/items/de5cef1ac4a281400764)などさまざまな手法がありますが、もとのデータは2値(5万ドル超えか5万ドル以下)なので、そのまま1と0に変換します。

```py
high_income = (data.income == " >50K").astype(int)
data.income = high_income
display(data)
```
![Screenshot 2024-05-28 at 18.27.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/9007e578-e8d6-409c-d4ba-253b896d3729.png)

もう一度ペアプロットやヒートマップを作成してみます。

```py
# seabornをsnsとしてインポート
import seaborn as sns
# ペアプロット
sns.pairplot(data)
```
![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/9ddb7b29-6c54-8f4e-b8ca-3eeb4d3d0a2c.png)

ペアプロットから何かを読み解くのは少し難しいです。

```py
# 相関行列を取得
corrmat = data.corr()
# ヒートマップとしてプロット
sns.heatmap(corrmat, annot=True, fmt="1.3f")
```
![download (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/f7404597-1329-7858-cd41-459d75c36d25.png)

一方こちらですと、`education_num`(教育を受けた年数)と`income`(年収)に高い相関があることがわかります。この様な結果といわゆる一般常識(専門的なデータの場合は専門知識)と照らし合わせてデータの理解を深めていくことも重要なステップです。

# まとめ

ここまでさまざまなツールを活用してデータの理解を深めてきました。データの目鼻顔立ちをはっきりさせることで、これ以降のユースケース(BIダッシュボードや機械学習モデルの構築、予測の実施など)をスムーズに進めることが可能となります。是非トライしてみてください！

### はじめてのDatabricks

[はじめてのDatabricks](https://qiita.com/taka_yayoi/items/8dc72d083edb879a5e5d)

### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)