MENU

【成功の秘訣】探索的データ分析(EDA)の手順と方法を解説(コピペで使えるPythonサンプルコード付き)

本記事は「【成功の秘訣】現場で使えるデータ分析手順を体系的に解説」で紹介した探索的分析(EDA)の具体的な手順と、EDAで良く用いられる分析手法のPythonサンプルコードを紹介しています。

目次

探索的データ分析(EDA)の目的

探索的分析(EDA=Exploratory data analysis)は、データの特徴、構造、異常値などを体系的に分析することで、そこから仮説を導き出し、それに基づいて分析方針を選定するための活動です。具体的には、統計分析、機械学習、データ可視化などの手法を組み合わせ、データに潜むルールや規則性を浮き彫りにします。

探索的分析は、前段階の「【成功の秘訣】データ探索(事前分析)の手順と方法を解説(Pythonソースコード付き)」で得られた知見をベースに、次の作業を行います。

  • データの統計量を確認する
  • データの傾向や分布を確認する
  • 欠損値や異常値の処理方法を検討する
  • 変数間の関係性を確認する
  • データが待つパターンを分析する
  • 仮説を立て、分析方針を選定する

探索的データ分析(EDA)の手順

データの統計量を確認する

データの平均値、標準偏差、最小値、最大値などの統計情報と、欠損値、異常値の件数を確認します。

ここでのポイントは、欠損値や異常値の件数が無視できるかどうかの検討です。

分析対象件数が十分に多く、欠損値が極めて少ない場合は、無条件に欠損値のある行を削除するという方法も選択できますが、欠損値が多い場合は何らかの補完が必要かもしれません。

異常値に関しても、0件なら問題ありませんが、1件以上存在する場合は処置方法を検討しなければなりません。

欠損値や異常値の判断にはドメイン知識が必要になるため、有識者と相談できるように結果をまとめておきます。

データの傾向や分布を確認する

データがどのように変化しているのか、それが線形なのか非線形なのか、分布が正規分布に従っているのか、偏りや裾野の状態はどうなっているのかを分析します。

ここでのポイントは、欠損値や異常値の処置方法を判断するための情報を得ることです。

欠損値が多く補完が必要な場合、データの傾向から補完方法を検討できます。例えば、線形で増加している場合は、そのように補完すれば良いかもしれません。

異常値についても、分布からあまりに外れ過ぎている場合、それが入力ミスやノイズである可能性が高くなります。

いずれにせよ、最終的な判断にはドメイン知識が必要であるため、ここではデータの傾向や分布をもとに補完方法や異常値の処置案を考え、有識者に相談できるようにまとめておきます。

正規分布になっているか否かで使える分析手法が異なってきますが、提供されるデータが理想的な正規分布であることは稀であるため、多少崩れているだけなら正規分布と断定してしまうことも多いです。

損値や異常値の処理方法を検討する

欠損値と異常値が存在する場合、ここまでの分析でまとめた情報(欠損値や異常値の件数、欠損値や異常値の処置案)を有識者と相談し、最終的な処置方法を決定します。

欠損値が無視できるほど少ない場合、欠損値のある行を削除することが一つの選択肢となります。しかし、欠損値が多い場合は、何らかの補完方法を検討しなければなりません。

異常値についても件数が少なければ除外することで対応可能ですが、無視できない場合は妥当な値に置き換える、正しい値に修正するなどの処置方法を検討します。

変数間の関係性を分析する

相関分析や回帰分析などの統計手法を用いて、変数間の関係性を分析します。

相関分析は、変数間の相関係数を計算し、二つの変数がどの程度関連しているかを数値化する手法です。一方、回帰分析は、一つの変数に対して、どの変数がどれくらい影響しているかを探る手法です。これらの手法を駆使することで、変数間の影響や相互作用を明確にすることが可能です。

データが待つパターンを分析する

どのような分析手法が適しているかの判断材料の1つとして、データが持つパターンを分析します。ここでは、データのクラスタリングや次元削減の手法がよく用いられます。

クラスタリング

クラスタリングは、データを類似した特徴を持つグループ(クラスタ)に分割する手法です。クラスタリングは教師なし学習の一種であり、データの内部構造に基づいて自然なグループに分類します。代表的なクラスタリング手法には、k-means法や階層的クラスタリングなどがあります。

k-means法k-means法は、データをあらかじめ指定された数のクラスタ(k)に分割する方法です。各クラスタの中心点をランダムに選び、各データ点を最も近い中心点に割り当てます。このプロセスを繰り返し、クラスタの中心点を更新していきます。最終的には、クラスタ内のデータ点の距離の合計が最小になるようにクラスタリングされます。
階層的クラスタリング階層的クラスタリングは、データポイントを階層的な構造でクラスタに分ける方法です。この手法には、凝集型(bottom-up)と分割型(top-down)の2つのアプローチがあります。凝集型は、まずすべてのデータポイントを個別のクラスタとして扱い、類似度が高いクラスタを統合していきます。一方、分割型は、すべてのデータポイントを1つのクラスタとして扱い、類似度が低いクラスタを分割していきます。
DBSCANDBSCAN(Density-Based Spatial Clustering of Applications with Noise)は、密度に基づいてクラスタを形成する方法です。データポイントの密度が高い領域はクラスタとして認識され、密度が低い領域はノイズとして扱われます。DBSCANは、クラスタの数を事前に指定する必要がないため、柔軟性が高いとされています。

次元削減

次元削減は、高次元のデータを低次元のデータに変換する手法です。高次元のデータでは、データの可視化や解釈が困難であったり、計算コストが高くなる傾向があります。次元削減は、データの持つ重要な情報を保持しつつ、データの次元を削減し、データの解釈や処理の効率化を図ります。代表的な次元削減手法には、主成分分析(PCA)、t-SNE、UMAPなどがあります。

主成分分析(PCA)主成分分析は、データの持つ情報を保持しつつ、データを直交する新しい軸(主成分)に変換する手法です。データの分散が最大となるような軸を見つけ出し、その軸に射影することで次元を削減します。主成分分析は線形変換を行うため、線形な構造を持つデータに適しています。
t-SNEt-SNE(t-distributed Stochastic Neighbor Embedding)は、データの局所的な関係性をできるだけ保持しつつ、データを低次元空間に埋め込む手法です。t-SNEでは、高次元空間でのデータポイント間の類似度を低次元空間での類似度に反映するようなマップを学習します。t-SNEは非線形な関係を捉えることができるため、可視化に適しています。
UMAPUMAP(Uniform Manifold Approximation and Projection)は、高次元データを低次元空間に埋め込む手法です。t-SNEに似ていますが、より高速かつ大規模なデータセットにも適用可能です。UMAPは局所的な構造を保持しつつ、グローバルな構造をより正確に捉えることができるとされています。

主成分分析の結果(主成分が持つ意味や寄与度)をドメイン知識に基づいて解釈することで、新しい指標の発見やデータ分類のためのヒントとして利用できます。しかし、これは本格的な分析において必要に応じて行われるべきものです。探索的データ分析(EDA)の段階では、主成分分析はあくまでも次元削減の手段として用いることが多いです。

仮説を立て、分析方針を選定する

データの探索や事前分析の後に、探索的データ解析(EDA)を実施することで、設定した分析方針の妥当性がより鮮明になったと思います。

新しい発見や気づきがあれば、それに基づいて有益な仮説を立て、アプローチ方法や分析方針を再検討することで、分析結果の精度向上や作業効率の向上が期待できます。

ただし、誤った仮説は逆効果になる可能性があるため、ドメイン知識を持つ有識者に仮説の正当性を確認するか、有識者と結果を共有し、ディスカッションを通じて仮説を立てることが重要です。

Python による探索的データ分析(EDA)のプログラム4例

総当たりによる散布図の描画

引数で指定されたDataFrameに対して、指定したカラムのリストから総当たりで散布図を作成するサンプルです。

import pandas as pd
import matplotlib.pyplot as plt
import itertools

# matplotlibに日本語フォントを設定
plt.rcParams['font.family'] = "MS Gothic"

def scatter_matrix(df, columns, dot_size=20):
    """
    指定された列の総当たりの散布図を1枚のグラフに描画する関数。

    Parameters:
        df (DataFrame): データフレーム
        columns (list): 描画する列のリスト
        dot_size (int): ドットのサイズ(デフォルトは20)
    """
    # グラフのサイズを設定
    num_cols = len(columns)
    fig, axes = plt.subplots(num_cols, num_cols, figsize=(8, 8))
    
    # 散布図を描画
    for i, j in itertools.product(range(num_cols), range(num_cols)):
        if i != j:
            ax = df.plot.scatter(x=columns[j], y=columns[i], ax=axes[i, j], alpha=0.5, s=dot_size)
            ax.tick_params(axis='both', which='both', bottom=False, left=False, labelbottom=False, labelleft=False)
            ax.set_xlabel(columns[j])
            ax.set_ylabel(columns[i])
        else:
            axes[i, j].axis('off')  # 対角線上のグラフは非表示にする
    
    plt.tight_layout()
    plt.show()
df = pd.read_csv("o:/sample.tsv", delimiter="\t")

# 関数を呼び出して散布図を表示
columns = df.columns[1:5]
scatter_matrix(df,columns)

総当たりによる相関係数ヒートマップの描画

引数で指定されたDataFrameに対して、指定されたカラムを総当たりで相関係数を計算し、ヒートマップとして可視化するサンプルです。カラム数が多い場合はこちらの方が見やすいです。

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import itertools

# matplotlibに日本語フォントを設定
plt.rcParams['font.family'] = "MS Gothic"

def heatmap_matrix(df, columns):
    """
    指定された列の総当たりの相関行列をヒートマップとして表示する関数。

    Parameters:
        df (DataFrame): データフレーム
        columns (list): 描画する列のリスト
    """
    # 指定された列の相関行列を計算
    correlation_matrix = df[columns].corr()
    
    # ヒートマップを作成
    plt.figure(figsize=(10, 8))
    sns.heatmap(correlation_matrix, cmap='coolwarm', annot=True, fmt='.2f', vmin=-1, vmax=1)
    plt.title('Pairwise Correlation Heatmap')
    plt.show()
# 利用例
df = pd.read_csv("o:/sample.tsv", delimiter="\t")

# 関数を呼び出して散布図を表示
columns = df.columns[1:15]
heatmap_matrix(df,columns)

指定したラグの散布図の描画

引数で指定されたDataFrameのカラムに対して、指定したラグのリストで散布図を作成します。コレログラムで高い相関が確認された場合、そのラグを可視化することで、データの散らばり具合や外れ値の有無、パターンの発見などに役立ちます。

import pandas as pd
import matplotlib.pyplot as plt
import itertools

# matplotlibに日本語フォントを設定
plt.rcParams['font.family'] = "MS Gothic"

def autocorrelation_plot(df, column, lags, dot_size=20, graphs_per_row=3):
    """
    指定された列に対して、指定されたラグの自己相関の散布図を横に並べて描画する関数。

    Parameters:
        df (DataFrame): データフレーム
        column (str): 描画する列の名前
        lags (list of int): ラグのリスト
        dot_size (int): ドットのサイズ(デフォルトは20)
        graphs_per_row (int): 1行に並べるグラフの数(デフォルトは3)
    """
    num_lags = len(lags)
    num_rows = (num_lags + graphs_per_row - 1) // graphs_per_row
    fig, axes = plt.subplots(num_rows, graphs_per_row, figsize=(graphs_per_row * 3, num_rows * 3))

    for i, lag in enumerate(lags):
        row = i // graphs_per_row
        col = i % graphs_per_row
        ax = axes[row, col]
        
        x = df[column]
        y = df[column].shift(-lag)
        ax.scatter(x, y, s=dot_size)
        ax.set_title(f"Lag {lag}")
        ax.set_xlabel(column)
        ax.set_ylabel(f"{column} (Lag {lag})")

    plt.tight_layout()
    plt.show()
df = pd.read_csv("o:/sample.tsv", delimiter="\t")

# 関数を呼び出して自己相関のグラフを横に並べて表示
column = df.columns[15]
lags = [1, 2, 3, 4, 5, 6,7,8,9,10,11,12]  # 例として、ラグ1から12までの自己相関を表示
autocorrelation_plot(df, column, lags, dot_size=10, graphs_per_row=4)

クラスタリング

0:['data6', 'data7', 'data9', 'data10', 'data12', 'data13', 'data14', 'data15', 'data16', 'data17', 'Outside_X_Index', 'data18', 'data19', 'data20', 'data21', 'data22', 'data23', 'data24', 'data25', 'data26', 'data27']
1:['data3', 'data4']
2:['data8']
3:['data5']
4:['daga0', 'data1', 'data2', 'data11']

引数で指定されたDataFrameに対して、Kで指定した個数でクラスタリングを実施し、分類結果を辞書で返します。

import pandas as pd
from sklearn.cluster import KMeans

def clustering_with_labels(df, K):
    """
    指定されたデータフレームを指定された数のクラスタにクラスタリングし、各クラスタのクラスタリングされたカラム名とクラスタ番号を含む辞書を返す関数。

    Parameters:
        df (DataFrame): クラスタリングするデータフレーム
        K (int): クラスタの数

    Returns:
        clusters (dict): 各クラスタのクラスタリングされたカラム名とクラスタ番号を含む辞書
    """
    # NaN値を含む行を削除
    df.dropna(inplace=True)

    # データフレームを転置してクラスタリングを実行
    kmeans = KMeans(n_clusters=K)
    kmeans.fit(df.T)
    
    # 各データポイントのクラスタラベルを取得
    labels = kmeans.labels_

    # クラスタごとにクラスタリングされたカラム名のリストを作成し、辞書に格納
    clusters = {}
    for cluster_id in range(K):
        cluster_data = df.columns[labels == cluster_id].tolist()
        clusters[cluster_id] = cluster_data
    
    return clusters
df = pd.read_csv("o:/sample.tsv", delimiter="\t")

# クラスタリングを実施
clusters = clustering_with_labels(df, 5)

#結果を表示
for key,val in clusters.items() :
    print(f"{key}:{val}")

クラスタリングが簡単に行える便利な自作クラスを「【製造業】データ分類と異常検知の強い味方!クラスタリングとは?」に掲載しています。併せてご覧ください。

主成分分析

引数で指定されたDataFrameに対して、主成分分析を実施します。

import pandas as pd
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

# matplotlibに日本語フォントを設定
plt.rcParams['font.family'] = "MS Gothic"

def apply_pca(df):
    """
    主成分分析を実行して、寄与度と累積寄与度を可視化する関数。

    Parameters:
        df (DataFrame): データフレーム

    Returns:
        pca (PCA): 主成分分析のモデル
    """
    # 欠損値を含む行を削除する
    df.dropna(inplace=True)

    # データの標準化
    scaled_data = (df - df.mean()) / df.std()

    # 主成分分析(n_componentsで求める主成分の数を指定)
    pca = PCA(n_components=None)
    pca.fit(scaled_data)

    # 寄与度と累積寄与度を取得
    explained_variance_ratio = pca.explained_variance_ratio_
    cumulative_explained_variance_ratio = explained_variance_ratio.cumsum()

    # 寄与度と累積寄与度を可視化
    plt.figure(figsize=(10, 6))
    plt.bar(range(1, len(explained_variance_ratio) + 1), explained_variance_ratio, label='寄与度', color='blue')
    plt.plot(range(1, len(cumulative_explained_variance_ratio) + 1), cumulative_explained_variance_ratio, marker='o', color='red', label='累積寄与度')
    plt.xlabel('主成分')
    plt.ylabel('分散の割合')
    plt.title('主成分分析の結果')
    plt.legend()
    plt.show()

    return pca
df = pd.read_csv("o:/sample.tsv", delimiter="\t")
# 主成分分析を実施
apply_pca(df)

主成分分析が簡単に行える便利な自作クラスを「【Python実践】データ分析の影の立役者!主成分分析(PCA)の使い方ガイド(コピペで使えるサンプルコード付き)」に掲載しています。併せてご覧ください。

まとめ

記事では、探索的データ解析(EDA)について詳しく解説しました。探索的データ解析(EDA)は、データの特徴、構造、異常値などを体系的に分析することで、仮説を導き出し、分析方針を選定するためのプロセスです。具体的には、以下の作業を行います。

  • データの統計量を計算する
  • データの分布を分析する
  • 変数間の関係性を分析する
  • データが待つパターンを分析する

また、Pythonのサンプルプログラムとして次のものを紹介しました。

  • 総当たりによる散布図の描画
  • 総当たりによる相関係数ヒートマップの描画
  • 指定したラグの散布図の描画
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

目次