はじめに
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))