サイトアイコン アマチュア無線局JS2IIU

【Streamlit】特徴量エンジニアリングの結果を可視化

こんにちは、JS2IIUです。
今回は特徴量エンジニアリングがテーマです。実用的な特徴量を作成してモデルの性能改善に挑戦する事例を取り上げます。今回もよろしくお願いします。

1. はじめに

特徴量エンジニアリング(Feature Engineering)とは、機械学習モデルの性能を高めるために、元のデータから有用な特徴量(変数)を作成・変換・選択するプロセスのことです。これは、モデル構築における最も重要かつ影響力の大きいステップの一つとされています。

🔍 特徴量エンジニアリングが使われる場面

特徴量エンジニアリングは、以下のような場面で活用されます:

🔧 主なテクニックと方法

以下は、代表的な特徴量エンジニアリングのテクニックです:

1. 特徴量の作成

2. カテゴリ変数の処理

3. スケーリング・正規化

4. 欠損値の処理

5. 時系列特徴の抽出

6. 交差特徴(Interaction Features)

7. 集約統計量の導入(GroupBy + 集計)

8. 次元削減(PCA、t-SNEなど)

2. 今回使うデータについて

UCIの「Heart Disease」データセットを使用します。このデータには、年齢、性別、血圧、コレステロール値など、心疾患のリスクに関係しそうな情報が含まれています。
分類問題として「心疾患があるか(target=1)/ないか(target=0)」を予測します。

UCI Machine Learning Repository

3. 使用するライブラリのインストール

Bash
pip install pandas scikit-learn matplotlib seaborn streamlit

4. アプリの全体像

Streamlitアプリでは以下の機能を実装します:

  1. データの読み込みと確認
  2. 特徴量エンジニアリングの実施(BMI、年齢カテゴリなど)
  3. モデル精度の比較(特徴量あり/なし)
  4. 特徴量重要度の可視化

5. Streamlitアプリの実装(フルコード)

以下がアプリ全体のコードです。要所で丁寧に解説していきます。

📁 ファイル名:heart_feature_engineering_app.py

Python
import streamlit as st
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score


st.title("💓 心臓病予測と特徴量エンジニアリング")
st.markdown("新しく作成した特徴量がモデル精度に与える影響を可視化します。")

@st.cache_data
def load_data():
    # download th dataset from UCI
    df = pd.read_csv('processed.cleveland.data')
    columns = ['age', 'sex', 'cp', 'trestbps', 'chol', 'fbs', 'restecg',
               'thalach', 'exang', 'oldpeak', 'slope', 'ca', 'thal', 'target']   
    df.columns = columns
    return df

df = load_data()
st.subheader("🔍 データプレビュー")
st.dataframe(df.head())

df['bmi'] = df['thalach'] / (df['age'] ** 2)  # 例として仮のBMI計算
df['age_group'] = pd.cut(df['age'], bins=[0, 40, 55, 100], labels=['young', 'middle', 'senior'])
df['high_risk'] = ((df['trestbps'] > 140) & (df['chol'] > 240)).astype(int)

features_all = ['age', 'sex', 'cp', 'trestbps', 'chol', 'thalach', 'slope', 'bmi', 'age_group', 'high_risk']
df_model = df[features_all + ['target']].copy()
df_model = pd.get_dummies(df_model, columns=['cp', 'slope', 'age_group'], drop_first=True)

X_with = df_model.drop('target', axis=1)
X_without = X_with.drop(columns=['bmi', 'age_group_middle', 'age_group_senior', 'high_risk'], errors='ignore')
y = df_model['target']

X_train1, X_test1, y_train, y_test = train_test_split(X_without, y, test_size=0.3, random_state=0)
X_train2, X_test2, _, _ = train_test_split(X_with, y, test_size=0.3, random_state=0)

model1 = RandomForestClassifier(random_state=0)
model1.fit(X_train1, y_train)
pred1 = model1.predict(X_test1)
acc1 = accuracy_score(y_test, pred1)

model2 = RandomForestClassifier(random_state=0)
model2.fit(X_train2, y_train)
pred2 = model2.predict(X_test2)
acc2 = accuracy_score(y_test, pred2)

st.subheader("📈 モデルの精度比較")
st.metric("特徴量エンジニアリングなし", f"{acc1:.3f}")
st.metric("特徴量エンジニアリングあり", f"{acc2:.3f}")

importance_df = pd.DataFrame({
    'feature': X_with.columns,
    'importance': model2.feature_importances_
}).sort_values(by='importance', ascending=True)

st.subheader("🧠 特徴量の重要度(エンジニアリングありモデル)")
fig, ax = plt.subplots(figsize=(6, 8))
ax.barh(importance_df['feature'], importance_df['importance'], color='skyblue')
ax.set_xlabel("Importance")
ax.set_title("Feature Importances")
st.pyplot(fig)

部分ごとの解説

まずはデータセットを手に入れます。今回はUCIのデータセットを本家からダウンロードしてきました。

Heart Disease – UCI Machine Learning Repository

右上のDOWNLOADからダウンロードしてください。

以下のコードではデータを読み込んで、DataFrameに格納しています。カラム名はダウンロードしたファイルのうち、heart-disease.namesというファイルに記載がありました。

Python
import streamlit as st
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score


st.title("💓 心臓病予測と特徴量エンジニアリング")
st.markdown("新しく作成した特徴量がモデル精度に与える影響を可視化します。")

@st.cache_data
def load_data():
    # download th dataset from UCI
    df = pd.read_csv('processed.cleveland.data')
    columns = ['age', 'sex', 'cp', 'trestbps', 'chol', 'fbs', 'restecg',
               'thalach', 'exang', 'oldpeak', 'slope', 'ca', 'thal', 'target']   
    df.columns = columns
    return df

df = load_data()
st.subheader("🔍 データプレビュー")
st.dataframe(df.head())

Step 1:特徴量エンジニアリングの実施

以下のような新しい特徴量を作成します:

特徴量名内容
bmi身長と体重からBMI(ここでは簡易的に weight / height^2 として仮定)
age_group年齢をカテゴリに分割(young, middle, senior)
high_risk高血圧かつ高コレステロールの条件を満たすか
Python
df['bmi'] = df['thalach'] / (df['age'] ** 2)  # 例として仮のBMI計算
df['age_group'] = pd.cut(df['age'], bins=[0, 40, 55, 100], labels=['young', 'middle', 'senior'])
df['high_risk'] = ((df['trestbps'] > 140) & (df['chol'] > 240)).astype(int)

✅ 1行目

Python
df['bmi'] = df['thalach'] / (df['age'] ** 2)

✅ 2行目

Python
df['age_group'] = pd.cut(df['age'], bins=[0, 40, 55, 100], labels=['young', 'middle', 'senior'])

✅ 3行目

Python
df['high_risk'] = ((df['trestbps'] > 140) & (df['chol'] > 240)).astype(int)

Step 2:前処理とダミー変換

Python
features_all = ['age', 'sex', 'cp', 'trestbps', 'chol', 'thalach', 'slope', 'bmi', 'age_group', 'high_risk']
df_model = df[features_all + ['target']].copy()
df_model = pd.get_dummies(df_model, columns=['cp', 'slope', 'age_group'], drop_first=True)

✅ 1行目

Python
features_all = ['age', 'sex', 'cp', 'trestbps', 'chol', 'thalach', 'slope', 'bmi', 'age_group', 'high_risk']

✅ 2行目

Python
df_model = df[features_all + ['target']].copy()

✅ 3行目

Python
df_model = pd.get_dummies(df_model, columns=['cp', 'slope', 'age_group'], drop_first=True)

📦 最終的な結果

df_model は、数値変数 + ダミー変数 + 目的変数(target)を含む、モデル学習に直接使える形式のデータになります。

Step 3:モデル構築と比較

特徴量あり・なしでモデルを訓練し、精度を比較します。

Python
X_with = df_model.drop('target', axis=1)
X_without = X_with.drop(columns=['bmi', 'age_group_middle', 'age_group_senior', 'high_risk'], errors='ignore')
y = df_model['target']

X_train1, X_test1, y_train, y_test = train_test_split(X_without, y, test_size=0.3, random_state=0)
X_train2, X_test2, _, _ = train_test_split(X_with, y, test_size=0.3, random_state=0)

model1 = RandomForestClassifier(random_state=0)
model1.fit(X_train1, y_train)
pred1 = model1.predict(X_test1)
acc1 = accuracy_score(y_test, pred1)

model2 = RandomForestClassifier(random_state=0)
model2.fit(X_train2, y_train)
pred2 = model2.predict(X_test2)
acc2 = accuracy_score(y_test, pred2)

st.subheader("📈 モデルの精度比較")
st.metric("特徴量エンジニアリングなし", f"{acc1:.3f}")
st.metric("特徴量エンジニアリングあり", f"{acc2:.3f}")

✅ 特徴量と目的変数の分割

Python
X_with = df_model.drop('target', axis=1)
X_without = X_with.drop(columns=['bmi', 'age_group_middle', 'age_group_senior', 'high_risk'], errors='ignore')
y = df_model['target']

✅ データの分割(学習用とテスト用)

Python
X_train1, X_test1, y_train, y_test = train_test_split(X_without, y, test_size=0.3, random_state=0)
X_train2, X_test2, _, _ = train_test_split(X_with, y, test_size=0.3, random_state=0)

✅ ランダムフォレストモデルの学習・予測・評価

Python
model1 = RandomForestClassifier(random_state=0)
model1.fit(X_train1, y_train)
pred1 = model1.predict(X_test1)
acc1 = accuracy_score(y_test, pred1)
Python
model2 = RandomForestClassifier(random_state=0)
model2.fit(X_train2, y_train)
pred2 = model2.predict(X_test2)
acc2 = accuracy_score(y_test, pred2)

✅ Streamlitで精度を表示

Python
st.subheader("📈 モデルの精度比較")
st.metric("特徴量エンジニアリングなし", f"{acc1:.3f}")
st.metric("特徴量エンジニアリングあり", f"{acc2:.3f}")

📌 要点まとめ

モデル特徴量精度を測る目的
model1元データのみベースライン精度
model2新たな特徴量を追加精度向上の有無を確認

Step 4:特徴量重要度の可視化

Python
AA importance_df = pd.DataFrame({
    'feature': X_with.columns,
    'importance': model2.feature_importances_
}).sort_values(by='importance', ascending=True)

st.subheader("🧠 特徴量の重要度(エンジニアリングありモデル)")
fig, ax = plt.subplots(figsize=(6, 8))
ax.barh(importance_df['feature'], importance_df['importance'], color='skyblue')
ax.set_xlabel("Importance")
ax.set_title("Feature Importances")
st.pyplot(fig)

6. アプリの実行方法

保存したファイルを次のコマンドで実行:

Bash
streamlit run heart_feature_engineering_app.py

7. 特徴量エンジニアリング振り返り

特徴量エンジニアリングとは、機械学習モデルが学習しやすいように、
既存のデータから有益な情報を抽出・加工して新しい特徴量を作る作業です。

たとえば:

モデルによっては精度向上に直結することも多く、データ分析では欠かせないテクニックです。

8. まとめ

9. 参考リンク

最後に書籍のPRです。
24年11月に第3版が発行された「scikit-learn、Keras、TensorFlowによる実践機械学習 第3版」、Aurélien Géron 著。下田、牧、長尾訳。機械学習のトピックスについて手を動かしながら網羅的に学べる書籍です。ぜひ手に取ってみてください。

最後まで読んでいただきありがとうございます。

モバイルバージョンを終了