Pythonで既存のデータで学習・テストをする際にカテゴリーデータは、ダミー変数(One-Hotエンコーディング)を作りますよね。
学習データやテストデータについては、機械学習モデルを作る前に、pandasのget_dummies()を使いますよね。
ですが、本番実装時に1行のデータを受けてそれをモデルに入れたいとします。ダミー変数化される前の元のカテゴリカルデータしかないデータです。
さて、困りました。どうすればいいのでしょうか?
方法は、3つあるようです。
- pd.get_dummies() + reindex() を使う
- pd.CategoricalDtypeを使う
- sklearn.preprocessing.OneHotEncoder を使う
それぞれのメリットとデメリットは下記のとおりです。
| メリット | デメリット | |
| pd.get_dummies() + reindex() |
|
|
| pd.CategoricalDtype を使う |
|
|
| sklearn.preprocessing.OneHotEncoder を使う |
|
|
次にスクリプトの例を示します。
まずはテストに使うデータを作ります。
カテゴリーデータがないので、EntranceDirectionというデータを付与します。
import pandas as pd import numpy as np from sklearn.datasets import fetch_california_housing # カリフォルニア住宅データをロード california_housing_data = fetch_california_housing() exp_data = pd.DataFrame(california_housing_data.data, columns=california_housing_data.feature_names) tar_data = pd.DataFrame(california_housing_data.target, columns=['HousingPrices']) data = pd.concat([exp_data, tar_data], axis=1) # EntranceDirection, HouseMaker, Parkling のカテゴリデータをランダムに作成 np.random.seed(42) # 再現性のためのシード data['EntranceDirection'] = np.random.choice(['east', 'west', 'south', 'north'], size=data.shape[0]) data['HouseMaker'] = np.random.choice(['A', 'B', 'C', 'D', 'E', 'F'], size=data.shape[0]) data['Parkling'] = np.random.choice(['covered', 'uncovered', 'none'], size=data.shape[0]) display(data.head()) print(data.shape) print(data.dtypes)
1. pd.get_dummies() + reindex() を使う
●データの読み込みとモデル作成
import pandas as pd
from sklearn.linear_model import LinearRegression
import joblib
import numpy as np
from sklearn.datasets import fetch_california_housing
# カリフォルニア住宅データをロード
california_housing_data = fetch_california_housing()
exp_data = pd.DataFrame(california_housing_data.data, columns=california_housing_data.feature_names)
tar_data = pd.DataFrame(california_housing_data.target, columns=['HousingPrices'])
data = pd.concat([exp_data, tar_data], axis=1)
# EntranceDirection, HouseMaker, Parkling のカテゴリデータをランダムに作成
np.random.seed(42) # 再現性のためのシード
data['EntranceDirection'] = np.random.choice(['east', 'west', 'south', 'north'], size=data.shape[0])
data['HouseMaker'] = np.random.choice(['A', 'B', 'C', 'D', 'E', 'F'], size=data.shape[0])
data['Parkling'] = np.random.choice(['covered', 'uncovered', 'none'], size=data.shape[0])
# カテゴリカル変数をダミー変数化
data = pd.get_dummies(data, columns=['EntranceDirection', 'HouseMaker', 'Parkling'])
# モデルの作成
X = data.drop('HousingPrices', axis=1)
y = data['HousingPrices']
model = LinearRegression()
model.fit(X, y)
# モデルとカラム情報の保存
joblib.dump(model, 'model_get_dummies.pkl')
joblib.dump(X.columns, 'columns_get_dummies.pkl')
●新しいデータの処理と予測
import pandas as pd
import joblib
# モデルとカラム情報をロード
model = joblib.load('model_get_dummies.pkl')
columns = joblib.load('columns_get_dummies.pkl')
# 新しいデータを読み込み
new_data = pd.DataFrame({'MedInc': [5], 'HouseAge': [30], 'AveRooms': [6], 'AveBedrms': [1],
'Population': [1000], 'AveOccup': [3], 'Latitude': [34], 'Longitude': [-118],
'EntranceDirection': ['east'], 'HouseMaker': ['A'], 'Parkling': ['covered']})
new_data = pd.get_dummies(new_data, columns=['EntranceDirection', 'HouseMaker', 'Parkling'])
new_data = new_data.reindex(columns=columns, fill_value=0)
# 予測
predictions = model.predict(new_data)
print(predictions)
2. pd.CategoricalDtypeを使う
●データの読み込みとモデル作成
import pandas as pd
from sklearn.linear_model import LinearRegression
import joblib
import numpy as np
from sklearn.datasets import fetch_california_housing
# カリフォルニア住宅データをロード
california_housing_data = fetch_california_housing()
exp_data = pd.DataFrame(california_housing_data.data, columns=california_housing_data.feature_names)
tar_data = pd.DataFrame(california_housing_data.target, columns=['HousingPrices'])
data = pd.concat([exp_data, tar_data], axis=1)
# EntranceDirection, HouseMaker, Parkling のカテゴリデータをランダムに作成
np.random.seed(42) # 再現性のためのシード
data['EntranceDirection'] = np.random.choice(['east', 'west', 'south', 'north'], size=data.shape[0])
data['HouseMaker'] = np.random.choice(['A', 'B', 'C', 'D', 'E', 'F'], size=data.shape[0])
data['Parkling'] = np.random.choice(['covered', 'uncovered', 'none'], size=data.shape[0])
# カテゴリデータの型を定義
cat_type_entrance = pd.api.types.CategoricalDtype(categories=['east', 'west', 'south', 'north'])
cat_type_house = pd.api.types.CategoricalDtype(categories=['A', 'B', 'C', 'D', 'E', 'F'])
cat_type_parkling = pd.api.types.CategoricalDtype(categories=['covered', 'uncovered', 'none'])
# カテゴリカル変数をダミー変数化
data = data.astype({'EntranceDirection': cat_type_entrance, 'HouseMaker': cat_type_house, 'Parkling': cat_type_parkling})
data = pd.get_dummies(data, columns=['EntranceDirection', 'HouseMaker', 'Parkling'])
# モデルの作成
X = data.drop('HousingPrices', axis=1)
y = data['HousingPrices']
model = LinearRegression()
model.fit(X, y)
# モデルとカテゴリ型、カラム情報の保存
joblib.dump(model, 'model_categoricaldtype.pkl')
joblib.dump([cat_type_entrance, cat_type_house, cat_type_parkling], 'cat_types.pkl')
joblib.dump(X.columns, 'columns_categoricaldtype.pkl')
●新しいデータの処理と予測
import pandas as pd
import joblib
# モデルとカテゴリ型、カラム情報をロード
model = joblib.load('model_categoricaldtype.pkl')
cat_types = joblib.load('cat_types.pkl')
cat_type_entrance, cat_type_house, cat_type_parkling = cat_types
columns = joblib.load('columns_categoricaldtype.pkl')
# 新しいデータを読み込み
new_data = pd.DataFrame({'MedInc': [5], 'HouseAge': [30], 'AveRooms': [6], 'AveBedrms': [1],
'Population': [1000], 'AveOccup': [3], 'Latitude': [34], 'Longitude': [-118],
'EntranceDirection': ['east'], 'HouseMaker': ['A'], 'Parkling': ['covered']})
# カテゴリカル変数をダミー変数化
new_data = new_data.astype({'EntranceDirection': cat_type_entrance, 'HouseMaker': cat_type_house, 'Parkling': cat_type_parkling})
new_data = pd.get_dummies(new_data, columns=['EntranceDirection', 'HouseMaker', 'Parkling'])
new_data = new_data.reindex(columns=columns, fill_value=0)
# 予測
predictions = model.predict(new_data)
print(predictions)
3. sklearn.preprocessing.OneHotEncoder を使う
●データの読み込みとモデル作成
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import OneHotEncoder
import joblib
import numpy as np
from sklearn.datasets import fetch_california_housing
# カリフォルニア住宅データをロード
california_housing_data = fetch_california_housing()
exp_data = pd.DataFrame(california_housing_data.data, columns=california_housing_data.feature_names)
tar_data = pd.DataFrame(california_housing_data.target, columns=['HousingPrices'])
data = pd.concat([exp_data, tar_data], axis=1)
# EntranceDirection, HouseMaker, Parkling のカテゴリデータをランダムに作成
np.random.seed(42) # 再現性のためのシード
data['EntranceDirection'] = np.random.choice(['east', 'west', 'south', 'north'], size=data.shape[0])
data['HouseMaker'] = np.random.choice(['A', 'B', 'C', 'D', 'E', 'F'], size=data.shape[0])
data['Parkling'] = np.random.choice(['covered', 'uncovered', 'none'], size=data.shape[0])
# OneHotEncoderでダミー変数化
encoded = encoder.fit_transform(data[['EntranceDirection', 'HouseMaker', 'Parkling']]).toarray()
encoded_df = pd.DataFrame(encoded, columns=encoder.get_feature_names_out(['EntranceDirection', 'HouseMaker', 'Parkling']))
data_encoded = pd.concat([data.drop(['EntranceDirection', 'HouseMaker', 'Parkling'], axis=1), encoded_df], axis=1)
# 特徴名を文字列に変換
data_encoded.columns = data_encoded.columns.astype(str)
# モデルの作成
X = data_encoded.drop('HousingPrices', axis=1)
y = data['HousingPrices']
model = LinearRegression()
model.fit(X, y)
# モデルとエンコーダーの保存
joblib.dump(model, 'model_onehotencoder.pkl')
joblib.dump(encoder, 'encoder.pkl')
●新しいデータの処理と予測
import pandas as pd
import joblib
from sklearn.preprocessing import OneHotEncoder
# モデルとエンコーダーをロード
model = joblib.load('model_onehotencoder.pkl')
encoder = joblib.load('encoder.pkl')
# 新しいデータを読み込み
new_data = pd.DataFrame({'MedInc': [5], 'HouseAge': [30], 'AveRooms': [6], 'AveBedrms': [1],
'Population': [1000], 'AveOccup': [3], 'Latitude': [34], 'Longitude': [-118],
'EntranceDirection': ['east'], 'HouseMaker': ['A'], 'Parkling': ['covered']})
new_encoded = encoder.transform(new_data[['EntranceDirection', 'HouseMaker', 'Parkling']]).toarray()
new_encoded_df = pd.DataFrame(new_encoded, columns=encoder.get_feature_names_out(['EntranceDirection', 'HouseMaker', 'Parkling']))
new_data_encoded = pd.concat([new_data.drop(['EntranceDirection', 'HouseMaker', 'Parkling'], axis=1), new_encoded_df], axis=1)
# 特徴名を文字列に変換
new_data_encoded.columns = new_data_encoded.columns.astype(str)
# 予測
predictions = model.predict(new_data_encoded)
print(predictions)
これらの3つの方法をメリット・デメリットを確認しながら適切に実装していきたいと思います。
(参考)
データ取得:こちら