kaggleやSIGNATEのテーブルデータコンペでとりあえずやること

はじめに

kaggleやSIGNATEのテーブルデータのコンペに参加したときにとりあえずやってみることをまとめました。

とりあえずやってみることなので、コンペによってはやらなくていいものも含まれている可能性があります。また、この他にもやったことがいいことはあるかと思いますので、都度更新したいと思います。

今回、サンプルとしているデータはkaggleのタイタニックコンペと住宅価格予測コンペのデータになります。可視化している画像については特徴量やデータの数によって、ちょうどいいサイズが異なるかと思うので都度調整してください。

準備

まずは必要なライブラリとデータのインポートを行います。

ライブラリのインポートです。

import pandas as pd
import lightgbm as lgb
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split

import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

学習データとテストデータを読み込みます。

train = pd.read_csv('../input/titanic/train.csv')
test = pd.read_csv('../input/titanic/test.csv')

データ数と特徴量の数を確認

学習データとテストデータの数、特徴量の数を確認します。

print('train data: {}'.format(train.shape[0]))
print('test data: {}'.format(test.shape[0]))
print('features: {}'.format(test.shape[1]))
train data: 891
test data: 418
features: 11

データをみてみる

データの中身やカラムなどなんとなくのデータのイメージをつかむために、とりあえずデータの上位10個を見てみます。

train.head(10)
test.head(10)

カラムと型の確認

カラム名とそれぞれの型を確認します。

train.dtypes
PassengerId      int64
Survived         int64
Pclass           int64
Name            object
Sex             object
Age            float64
SibSp            int64
Parch            int64
Ticket          object
Fare           float64
Cabin           object
Embarked        object
dtype: object

より詳しい情報をみるときは以下のコードも使えます。

train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
 #   Column       Non-Null Count  Dtype
---  ------       --------------  -----
 0   PassengerId  891 non-null    int64
 1   Survived     891 non-null    int64
 2   Pclass       891 non-null    int64
 3   Name         891 non-null    object
 4   Sex          891 non-null    object
 5   Age          714 non-null    float64
 6   SibSp        891 non-null    int64
 7   Parch        891 non-null    int64
 8   Ticket       891 non-null    object
 9   Fare         891 non-null    float64
 10  Cabin        204 non-null    object
 11  Embarked     889 non-null    object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB

欠損値の確認

学習データとテストデータに欠損値が含まれているか確認します。

欠損値がある場合は、どう対処するか考える必要があります。簡単な対応は後ほど説明します。

train.isnull().sum()
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64
test.isnull().sum()
PassengerId      0
Pclass           0
Name             0
Sex              0
Age             86
SibSp            0
Parch            0
Ticket           0
Fare             1
Cabin          327
Embarked         0
dtype: int64

ターゲットの分布

予測するターゲットの分布を確認します。

分類問題ならそれぞれのクラスの数をカウントします。

sns.countplot(x='Survived', data=train)

回帰問題ならヒストグラムで分布を可視化します。

sns.displot(train['SalePrice'], height=10)

カテゴリデータの分布

カテゴリデータの分布を確認します。

偏った分布のカテゴリデータがないか、学習データやテストデータにしかないクラスがないか確認します。

cat_cols = ['Pclass', 'Sex', 'Embarked']
fig, axes = plt.subplots(len(cat_cols), 2, figsize=(15, 20))
for i, c in enumerate(cat_cols):
    sns.countplot(x=c, data=train, ax=axes[i,0]).set(title=c+' train')
    sns.countplot(x=c, data=test, ax=axes[i,1]).set(title=c+' test')

ペアプロットとヒストグラム

ペアプロットを確認します。

各特徴量の組み合わせでの散布図とヒストグラムが表示されます。

特徴量が多いと実行に時間がかかるので、多すぎる場合は必要な特徴量を選択して実行するのがいいです。

sns.pairplot(train, hue='Survived', diag_kind='hist', palette={0: 'red', 1: 'blue'})

特徴量の選択はvarsで指定します。

sns.pairplot(train, hue='Survived', diag_kind='hist', palette={0: 'red', 1: 'blue'}, vars=['Age', 'Fare'], size=5)

分類問題でクラスごとに色分けしたいヒストグラムをみたい場合は以下のようにします。

sns.displot(train, x="Age", hue="Survived", multiple="stack")

ヒートマップ

ヒートマップで特徴量同士の相関を確認します。

plt.subplots(figsize=(10,10))
sns.heatmap(train.corr(), annot=True)

欠損値の扱い

欠損値の扱いを決めます。

欠損値をどう扱うかは色々試す必要があるかと思いますが、最初は簡単な方法で処理します。

そのまま扱う場合は特に何もしません。

取り除く

  • 欠損値があるデータ(行)を削除
train.dropna()
  • 欠損値のある特徴量(列)を削除
train.dropna(axis=1)
  • 特定の列に欠損値があるデータを削除
train.dropna(subset=['Age'])

埋める

  • 欠損値を全て同じ値で置換
train.fillna(0)
  • 列によって置換する値を変える
train.fillna({'Age': 20, 'Cabin': 0})

カテゴリデータの変換

カテゴリデータの変換もいくつか方法があるかと思いますが、最初は簡単な方法で変換します。

one-hot encoding

cat_oh_colsに変換するカテゴリデータのカラム名を持つようにしています。

all_cat = pd.concat([train[cat_oh_cols], test[cat_oh_cols]])
all_cat = pd.get_dummies(all_cat, columns=cat_oh_cols)

train = pd.concat([train, all_cat.iloc[:train.shape[0], :].reset_index(drop=True)], axis=1)
test = pd.concat([test, all_cat.iloc[train.shape[0]:, :].reset_index(drop=True)], axis=1)

label encoding

cat_le_colsに変換するカテゴリデータのカラム名を持つようにしています。

for c in cat_le_cols:
    le = LabelEncoder()
    le.fit(train[c])
    train[c] = le.transform(train[c])
    test[c] = le.transform(test[c])

LightGBMで学習

一通りデータの確認ができたらLightGBMでモデルを学習させてみます。

問題設定によってはパラメータの変更が必要になります。

ここでは二値分類を想定しています。

target = ['Survived']
features = list(set(train.columns) - set(target))

X = train[features].values
y = train[target].values.reshape(-1)
X_test = test[features].values

X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.3, random_state=0)

lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_valid, y_valid)

params = {
    'objective': 'binary',
}
num_round = 100

model = lgb.train(
    params,
    lgb_train,
    valid_sets=lgb_eval,
    num_boost_round=num_round,
    verbose_eval=10
)

特徴量の重要度

LightGBMでモデルを学習させると各特徴量の重要度を求めることができるので可視化してみます。

importance = pd.DataFrame()
importance['feature'] = features
importance['importance'] = model.feature_importance(importance_type='gain')

sns.barplot(x='importance', y='feature', data=importance.sort_values(by='importance', ascending=False))

参考

タイトルとURLをコピーしました