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つの方法をメリット・デメリットを確認しながら適切に実装していきたいと思います。
(参考)
データ取得:こちら