common.title

Docs
Quantum Circuit
TYTAN CLOUD

QUANTUM GAMING


Desktop RAG

Overview
Terms of service

Privacy policy

Contact
Research

Sign in
Sign up
common.title

Kaggleのイントロを見る。機械学習中級編。。。欠損値とカテゴリ変数

Yuichiro Minato

2023/10/14 07:59

ほぼコピペですが見ていきます。。。

https://www.kaggle.com/learn/intermediate-machine-learning

こちらから見ます。

https://www.kaggle.com/code/alexisbcook/missing-values

導入

データに欠損値が存在する理由はさまざまです。例えば、

- 2つのベッドルームを持つ家は、3つ目のベッドルームのサイズの値を含んでいないでしょう。

- 調査の回答者は、彼の収入を共有したくないかもしれません。

ほとんどの機械学習のライブラリ(scikit-learnを含む)は、欠損値を含むデータを使用してモデルを構築しようとするとエラーを返します。したがって、以下の戦略のいずれかを選択する必要があります。

三つのアプローチ

  1. シンプルなオプション: 欠損値を持つ列を削除する

最もシンプルな選択肢は、欠損値を持つ列を削除することです。

このアプローチでは、削除された列のほとんどの値が欠損していない限り、モデルは多くの(潜在的に有用な!)情報へのアクセスを失います。極端な例として、1つの重要な列で1つのエントリが欠けている10,000行のデータセットを考えてみてください。このアプローチでは、その列全体が削除されるでしょう!

  1. より良いオプション: 補間

補間は、欠損値を何らかの数値で埋めます。例えば、各列に沿って平均値で埋めることができます。

補間された値は、ほとんどの場合で正確には当たらないかもしれませんが、列全体を削除するよりも正確なモデルが得られることが多いです。

  1. 補間の拡張

補間は標準的なアプローチであり、通常はうまく機能します。しかし、補間された値は、データセットで収集されなかった実際の値よりも体系的に高くなったり低くなったりするかもしれません。または、欠損値を持つ行が他の方法でユニークであるかもしれません。その場合、元々欠損していた値がどれであるかを考慮することで、あなたのモデルはより良い予測をするでしょう。

このアプローチでは、前と同様に欠損値を補間します。そして、さらに、元のデータセットで欠損エントリを持つ各列に、補間されたエントリの位置を示す新しい列を追加します。

場合によっては、この方法で結果が意味深く改善されることがあります。他の場合、全く助けにならないこともあります。

例では、メルボルンの住宅データセットを使用します。私たちのモデルは、部屋の数や土地の面積などの情報を使用して家の価格を予測します。

データの読み込みステップに焦点を当てることはありません。代わりに、すでにX_train、X_valid、y_train、およびy_validにトレーニングデータと検証データがある点を想像してください。

import pandas as pd
from sklearn.model_selection import train_test_split

Load the data

data = pd.read_csv('../input/melbourne-housing-snapshot/melb_data.csv')

Select target

y = data.Price

To keep things simple, we'll use only numerical predictors

melb_predictors = data.drop(['Price'], axis=1)
X = melb_predictors.select_dtypes(exclude=['object'])

Divide data into training and validation subsets

X_train, X_valid, y_train, y_valid = train_test_split(X, y, train_size=0.8, test_size=0.2, random_state=0)

欠損値を扱うための異なるアプローチを比較するために、score_dataset()という関数を定義します。この関数はランダムフォレストモデルからの平均絶対誤差(MAE)を報告します。

from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error

Function for comparing different approaches

def score_dataset(X_train, X_valid, y_train, y_valid):
model = RandomForestRegressor(n_estimators=10, random_state=0)
model.fit(X_train, y_train)
preds = model.predict(X_valid)
return mean_absolute_error(y_valid, preds)

アプローチ1(欠損値のある列を削除)からのスコア

訓練セットと検証セットの両方で作業をしているため、両方のデータフレームで同じ列を削除するように注意しています。

# Get names of columns with missing values
cols_with_missing = [col for col in X_train.columns
if X_train[col].isnull().any()]

Drop columns in training and validation data

reduced_X_train = X_train.drop(cols_with_missing, axis=1)
reduced_X_valid = X_valid.drop(cols_with_missing, axis=1)

print("MAE from Approach 1 (Drop columns with missing values):")
print(score_dataset(reduced_X_train, reduced_X_valid, y_train, y_valid))
MAE from Approach 1 (Drop columns with missing values):
183550.22137772635

アプローチ2(代入法)からのスコア

次に、SimpleImputerを使用して、各列の平均値で欠損値を置き換えます。

シンプルではありますが、平均値を入れることは一般的に非常に良いパフォーマンスを発揮します(ただし、データセットによって異なります)。統計家は代入値を決定するためのより複雑な方法(例えば、回帰代入など)を試みてきましたが、その複雑な戦略は結果を高度な機械学習モデルに組み込んだ後、追加の利益をもたらすことはほとんどありません。

from sklearn.impute import SimpleImputer

Imputation

my_imputer = SimpleImputer()
imputed_X_train = pd.DataFrame(my_imputer.fit_transform(X_train))
imputed_X_valid = pd.DataFrame(my_imputer.transform(X_valid))

Imputation removed column names; put them back

imputed_X_train.columns = X_train.columns
imputed_X_valid.columns = X_valid.columns

print("MAE from Approach 2 (Imputation):")
print(score_dataset(imputed_X_train, imputed_X_valid, y_train, y_valid))
MAE from Approach 2 (Imputation):
178166.46269899711

アプローチ2とアプローチ1のスコアを比較すると、アプローチ2のMAEがアプローチ1よりも低いことがわかります。従って、このデータセットにおいては、アプローチ2がより良い結果を示しました。

アプローチ3(代入法の拡張)からのスコア

次に、欠損値を代入する際に、どの値が代入されたのかを追跡しながら代入を行います。

# Make copy to avoid changing original data (when imputing)
X_train_plus = X_train.copy()
X_valid_plus = X_valid.copy()

Make new columns indicating what will be imputed

for col in cols_with_missing:
X_train_plus[col + '_was_missing'] = X_train_plus[col].isnull()
X_valid_plus[col + '_was_missing'] = X_valid_plus[col].isnull()

Imputation

my_imputer = SimpleImputer()
imputed_X_train_plus = pd.DataFrame(my_imputer.fit_transform(X_train_plus))
imputed_X_valid_plus = pd.DataFrame(my_imputer.transform(X_valid_plus))

Imputation removed column names; put them back

imputed_X_train_plus.columns = X_train_plus.columns
imputed_X_valid_plus.columns = X_valid_plus.columns

print("MAE from Approach 3 (An Extension to Imputation):")
print(score_dataset(imputed_X_train_plus, imputed_X_valid_plus, y_train, y_valid))
MAE from Approach 3 (An Extension to Imputation):
178927.503183954

それを見ると、アプローチ3はアプローチ2よりもわずかに悪い結果を示しています。

では、なぜ代入法が列を削除するよりも良い結果を示したのでしょうか?

トレーニングデータには10864行と12列があり、そのうち3列に欠損データが含まれています。各列で、エントリーの半数未満が欠損しています。そのため、列を削除すると多くの有用な情報が失われるため、代入法がより良い結果を示すのは理にかなっています。

# Shape of training data (num_rows, num_columns)
print(X_train.shape)

Number of missing values in each column of training data

missing_val_count_by_column = (X_train.isnull().sum())
print(missing_val_count_by_column[missing_val_count_by_column > 0])
(10864, 12)
Car 49
BuildingArea 5156
YearBuilt 4307
dtype: int64

結論

よくあることですが、欠損値を代入する方法(アプローチ2とアプローチ3)は、欠損値を含む列を単純に削除する方法(アプローチ1)に比べて、より良い結果をもたらしました。

次にカテゴリ変数です。

https://www.kaggle.com/code/alexisbcook/categorical-variables

導入

カテゴリ変数は、限られた数の値のみを取る変数です。

例えば、あなたがどれくらいの頻度で朝食を食べるかを尋ねる調査を考えてみてください。そして、"決して", "まれに", "ほとんどの日", "毎日" の4つのオプションを提供します。この場合、回答は固定されたカテゴリに分類されるので、データはカテゴリ的です。

もし人々がどの車のブランドを所有しているかについての調査に答えたら、回答は "ホンダ", "トヨタ", "フォード" などのカテゴリに分類されるでしょう。この場合も、データはカテゴリ的です。

これらの変数を最初に前処理せずにPythonのほとんどの機械学習モデルにプラグインしようとするとエラーが発生します。このチュートリアルでは、カテゴリデータを準備するために使用できる3つのアプローチを比較します。

三つのアプローチ

  1. カテゴリ変数を削除する

カテゴリ変数を取り扱う最も簡単な方法は、データセットからそれらを単純に削除することです。このアプローチは、その列に有用な情報が含まれていない場合にのみうまく機能します。

  1. 順序エンコーディング

順序エンコーディングは、各ユニークな値に異なる整数を割り当てる方法です。

このアプローチはカテゴリの順序付けを前提としています: "決して" (0) < "まれに" (1) < "ほとんどの日" (2) < "毎日" (3)。

この例ではこの仮定は意味があります、なぜならカテゴリには議論の余地のないランキングがあるからです。すべてのカテゴリ変数が値に明確な順序を持っているわけではありませんが、そういったものを順序変数と呼びます。ツリーベースのモデル(決定木やランダムフォレストなど)については、順序変数とともに順序エンコーディングがうまく機能することが期待されます。

  1. ワンホットエンコーディング

ワンホットエンコーディングは、元のデータでの各可能な値の存在(または不在)を示す新しい列を作成します。これを理解するために、例を通して説明します。

元のデータセットにおいて、「Color」は「Red」、「Yellow」、「Green」という3つのカテゴリを持つカテゴリ変数です。対応するワンホットエンコーディングは、可能な値ごとに1つの列と、元のデータセットの各行に対応する1つの行を持ちます。元の値が「Red」であった場合、"Red"の列に1を入れます。元の値が「Yellow」であった場合、"Yellow"の列に1を入れます、というようにします。

順序エンコーディングとは異なり、ワンホットエンコーディングはカテゴリ間に順序を前提としません。したがって、カテゴリデータに明確な順序がない場合(例えば、「Red」は「Yellow」よりも高くも低くもありません)にこのアプローチが特にうまく機能することが期待されます。我々は、固有のランキングを持たないカテゴリ変数を名義変数と呼びます。

ワンホットエンコーディングは、カテゴリ変数が多数の値を取る場合には一般的にうまく機能しません(つまり、15種類以上の異なる値を取る変数には通常使用されません)。

前のチュートリアルと同様に、メルボルンの住宅データセットを使用します。

データの読み込みステップに焦点を当てることはありません。代わりに、すでにX_train、X_valid、y_train、y_validにトレーニングデータと検証データを持っている状態を想像してください。

import pandas as pd
from sklearn.model_selection import train_test_split

Read the data

data = pd.read_csv('../input/melbourne-housing-snapshot/melb_data.csv')

Separate target from predictors

y = data.Price
X = data.drop(['Price'], axis=1)

Divide data into training and validation subsets

X_train_full, X_valid_full, y_train, y_valid = train_test_split(X, y, train_size=0.8, test_size=0.2,
random_state=0)

Drop columns with missing values (simplest approach)

cols_with_missing = [col for col in X_train_full.columns if X_train_full[col].isnull().any()]
X_train_full.drop(cols_with_missing, axis=1, inplace=True)
X_valid_full.drop(cols_with_missing, axis=1, inplace=True)

"Cardinality" means the number of unique values in a column

Select categorical columns with relatively low cardinality (convenient but arbitrary)

low_cardinality_cols = [cname for cname in X_train_full.columns if X_train_full[cname].nunique() < 10 and
X_train_full[cname].dtype == "object"]

Select numerical columns

numerical_cols = [cname for cname in X_train_full.columns if X_train_full[cname].dtype in ['int64', 'float64']]

Keep selected columns only

my_cols = low_cardinality_cols + numerical_cols
X_train = X_train_full[my_cols].copy()
X_valid = X_valid_full[my_cols].copy()

次に、トレーニングデータ内のすべてのカテゴリ変数のリストを取得します。

これは、各列のデータタイプ(またはdtype)を確認することで行います。object dtypeは、列にテキストがあることを示しています(理論的には他にもなることができますが、私たちの目的には関係ありません)。このデータセットにおいて、テキストを持つ列はカテゴリ変数を示しています。

# Get list of categorical variables
s = (X_train.dtypes == 'object')
object_cols = list(s[s].index)

print("Categorical variables:")
print(object_cols)
Categorical variables:
['Type', 'Method', 'Regionname']

各アプローチの品質を測定するための関数を定義する

私たちは、カテゴリ変数を扱うための3つの異なるアプローチを比較するためにscore_dataset()関数を定義します。この関数は、ランダムフォレストモデルからの平均絶対誤差(MAE)を報告します。一般的に、MAEはできるだけ低い方が良いです!

from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error

Function for comparing different approaches

def score_dataset(X_train, X_valid, y_train, y_valid):
model = RandomForestRegressor(n_estimators=100, random_state=0)
model.fit(X_train, y_train)
preds = model.predict(X_valid)
return mean_absolute_error(y_valid, preds)

アプローチ1のスコア(カテゴリ変数を削除)

select_dtypes()メソッドを使用してobject列を削除します。

drop_X_train = X_train.select_dtypes(exclude=['object'])
drop_X_valid = X_valid.select_dtypes(exclude=['object'])

print("MAE from Approach 1 (Drop categorical variables):")
print(score_dataset(drop_X_train, drop_X_valid, y_train, y_valid))
MAE from Approach 1 (Drop categorical variables):
175703.48185157913

アプローチ2のスコア(序数エンコーディング)

Scikit-learnにはOrdinalEncoderクラスがあり、これを使用して序数エンコーディングを取得することができます。カテゴリ変数をループし、各列に序数エンコーダを個別に適用します。

from sklearn.preprocessing import OrdinalEncoder

Make copy to avoid changing original data

label_X_train = X_train.copy()
label_X_valid = X_valid.copy()

Apply ordinal encoder to each column with categorical data

ordinal_encoder = OrdinalEncoder()
label_X_train[object_cols] = ordinal_encoder.fit_transform(X_train[object_cols])
label_X_valid[object_cols] = ordinal_encoder.transform(X_valid[object_cols])

print("MAE from Approach 2 (Ordinal Encoding):")
print(score_dataset(label_X_train, label_X_valid, y_train, y_valid))
MAE from Approach 2 (Ordinal Encoding):
165936.40548390493

上のコードセルでは、各列に対して、ユニークな値をランダムに異なる整数に割り当てます。これはカスタムラベルを提供するよりもシンプルな一般的なアプローチですが、すべての序数変数に対してより適切なラベルを提供することで、さらなる性能の向上が期待できます。

アプローチ3のスコア(ワンホットエンコーディング)

Scikit-learnのOneHotEncoderクラスを使用してワンホットエンコーディングを取得します。その動作をカスタマイズするためのいくつかのパラメーターが使用できます。

- validationデータがトレーニングデータに存在しないクラスを含む場合のエラーを避けるために、handle_unknown='ignore'を設定します。

- sparse=Falseを設定することで、エンコードされた列が疎な行列ではなくnumpy配列として返されることを確実にします。

エンコーダーを使用するには、ワンホットエンコーディングされるべきカテゴリ列のみを供給します。例えば、トレーニングデータをエンコードするためには、X_train[object_cols]を供給します。(以下のコードセルのobject_colsは、カテゴリデータを持つ列の名前のリストで、したがってX_train[object_cols]はトレーニングセットのすべてのカテゴリデータを含んでいます。)

from sklearn.preprocessing import OneHotEncoder

Apply one-hot encoder to each column with categorical data

OH_encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)
OH_cols_train = pd.DataFrame(OH_encoder.fit_transform(X_train[object_cols]))
OH_cols_valid = pd.DataFrame(OH_encoder.transform(X_valid[object_cols]))

One-hot encoding removed index; put it back

OH_cols_train.index = X_train.index
OH_cols_valid.index = X_valid.index

Remove categorical columns (will replace with one-hot encoding)

num_X_train = X_train.drop(object_cols, axis=1)
num_X_valid = X_valid.drop(object_cols, axis=1)

Add one-hot encoded columns to numerical features

OH_X_train = pd.concat([num_X_train, OH_cols_train], axis=1)
OH_X_valid = pd.concat([num_X_valid, OH_cols_valid], axis=1)

Ensure all columns have string type

OH_X_train.columns = OH_X_train.columns.astype(str)
OH_X_valid.columns = OH_X_valid.columns.astype(str)

print("MAE from Approach 3 (One-Hot Encoding):")
print(score_dataset(OH_X_train, OH_X_valid, y_train, y_valid))
MAE from Approach 3 (One-Hot Encoding):
166089.4893009678

どのアプローチが最も良いのか?

このケースでは、カテゴリカルな列を削除する(アプローチ1)のは最も性能が悪く、最もMAEスコアが高かったです。他の2つのアプローチについては、返されるMAEスコアが非常に近い値であるため、どちらかが他方よりも有意に優れているとは考えられません。

一般的に、ワンホットエンコーディング(アプローチ3)は通常最も良い性能を発揮し、カテゴリカルな列を削除する(アプローチ1)は通常最も性能が悪いとされていますが、ケースごとに異なります。

結論

世界はカテゴリカルデータで溢れています。この一般的なデータタイプをどのように使用するかを知っていれば、より効果的なデータサイエンティストになることができます!

© 2025, blueqat Inc. All rights reserved