🐉

Kaggle 変数間の組み合わせで新しい特徴量を作成

2024/09/01に公開

はじめに

本記事では、Kaggle等のデータ分析コンペで、変数間の組み合わせを簡単に作成する関数を紹介します。本記事の関数を使うことで、モデルのパフォーマンス向上に寄与できれば幸いです。

数値データ × 数値データ

数値データ間の四則演算

from itertools import combinations
from typing import List, Union
from concurrent.futures import ProcessPoolExecutor, as_completed
import multiprocessing

def apply_operation(df: pd.DataFrame, col1: str, col2: str, op: str) -> pd.Series:
    """指定された2列に対して演算を適用する"""
    if op == 'add':
        return df[col1] + df[col2]
    elif op == 'sub':
        return df[col1] - df[col2]
    elif op == 'mul':
        return df[col1] * df[col2]
    elif op == 'div':
        return df[col1] / df[col2].replace(0, np.nan)

def process_column_pair(df: pd.DataFrame, col1: str, col2: str, operators: List[str]) -> dict:
    """列のペアに対して指定された演算を適用し、結果を辞書で返す"""
    result = {}
    for op in operators:
        if op == 'add':
            result[f'{col1}_plus_{col2}'] = apply_operation(df, col1, col2, op)
        elif op == 'sub':
            result[f'{col1}_minus_{col2}'] = apply_operation(df, col1, col2, op)
            # result[f'{col2}_minus_{col1}'] = apply_operation(df, col2, col1, op)
        elif op == 'mul':
            result[f'{col1}_times_{col2}'] = apply_operation(df, col1, col2, op)
        elif op == 'div':
            result[f'{col1}_div_{col2}'] = apply_operation(df, col1, col2, op)
            result[f'{col2}_div_{col1}'] = apply_operation(df, col2, col1, op)
    return result

def create_arithmetic_features(
    df: pd.DataFrame,
    exclude_columns: List[str] = [],
    operators: List[str] = ['add', 'sub', 'mul', 'div'],
    include_original: bool = True,
    n_jobs: int = -1
) -> pd.DataFrame:
    """
    数値データ間で四則演算を行い、新しい特徴量を作成する関数

    Args:
        df (pd.DataFrame): 元のデータフレーム
        exclude_columns (List[str]): 演算から除外する列のリスト
        operators (List[str]): 使用する演算子のリスト。'add', 'sub', 'mul', 'div'から選択
        include_original (bool): 元の列を結果に含めるかどうか。デフォルトはTrue
        n_jobs (int): 並列処理に使用するプロセス数。-1の場合は全てのCPUを使用

    Returns:
        pd.DataFrame: 新しい特徴量を追加したデータフレーム
    """
    numerical_columns = df.select_dtypes(include=['int64', 'float64']).columns.tolist()
    numerical_columns = [col for col in numerical_columns if col not in exclude_columns]

    new_features = {}

    # n_jobsの処理を改善
    if n_jobs < 1:
        n_jobs = multiprocessing.cpu_count()

    with ProcessPoolExecutor(max_workers=n_jobs) as executor:
        future_to_pair = {
            executor.submit(process_column_pair, df, col1, col2, operators): (col1, col2)
            for col1, col2 in combinations(numerical_columns, 2)
        }

        for future in as_completed(future_to_pair):
            new_features.update(future.result())

    new_df = pd.DataFrame(new_features)

    if include_original:
        new_df = pd.concat([df, new_df], axis=1)

    return new_df

# 使用例
# train_processed = create_arithmetic_features(train,
#   exclude_columns=['col_0'], operators=['add', 'mul'])

数値データ × カテゴリデータ

集約統計量

数値データとカテゴリデータでは,集約統計量を新しい特徴量として作成できます。
集約は,複数のテーブルデータがある際に有効な場合が多いです。
例えばユーザーと購入した商品が1対多になっているテーブルで,あるユーザーがこれまでに購入した金額の合計値(sum)や1商品あたりの平均値(mean)などの統計量をユーザーの特徴量として使えます。

一方で1つのテーブルだけでも同様に集約統計量の作成ができ,カテゴリデータの一種の数値化として扱えます。そのため水準数が少ないカテゴリ変数はあまり効果がない可能性があります。
以下にカテゴリ変数ごとに数値変数の集約統計量を計算する関数を記載します。

# 数値データ × カテゴリデータ -> 集約統計量
from typing import List, Dict, Any

def create_and_merge_aggregate_statistics(
    train: pd.DataFrame,
    test: pd.DataFrame,
    cat_cols: List[str],
    num_cols: List[str],
    agg_cols: List[str] = ['min', 'max', 'mean', 'median', 'std']
) -> tuple[pd.DataFrame, pd.DataFrame]:
    """
    カテゴリカル変数ごとに数値変数の集約統計量を計算し、
    訓練データとテストデータにマージします。

    Args:
        train (pd.DataFrame): 訓練データ
        test (pd.DataFrame): テストデータ
        cat_cols (List[str]): カテゴリカル変数のリスト
        num_cols (List[str]): 数値変数のリスト
        agg_cols (List[str]): 集約統計量のリスト(デフォルト: ['min', 'max', 'mean', 'median', 'std'])

    Returns:
        tuple[pd.DataFrame, pd.DataFrame]: 集約統計量がマージされた訓練データとテストデータ
    """
    for col in cat_cols:
        grp_df = train.groupby(col)[num_cols].agg(agg_cols)
        grp_df.columns = [f'{agg_col}_{num_col}_groupby_{col}' for num_col, agg_col in grp_df.columns]

        train = train.merge(grp_df, on=col, how='left')
        test = test.merge(grp_df, on=col, how='left')

    return train, test

# 使用例
# train_processed, test_processed = create_and_merge_aggregate_statistics(
#     train, test,
#     cat_cols=['category', 'subcategory'],
#     num_cols=['price', 'quantity']
# )

この関数は以下の特徴があります:

1.訓練データとテストデータの両方を入力として受け取り、両方にマージ処理を行います。
2.カテゴリカル変数のリスト(cat_cols)と数値変数のリスト(num_cols)を引数として受け取ります。
3.デフォルトの集約統計量は ['min', 'max', 'mean', 'median', 'std']ですが、agg_cols 引数で変更可能です。
4.各カテゴリカル変数に対して、指定された数値変数の集約統計量を計算します。
5.新しい列名は {集約統計量}{数値変数}{カテゴリカル変数}の形式になります。
6.計算された集約統計量を元の訓練データとテストデータにマージします。
7.マージされた訓練データとテストデータをタプルとして返します。

この関数を使用することで、カテゴリカル変数ごとの数値変数の集約統計量を簡単に計算できます。
これは特徴量エンジニアリングの一般的なテクニックであり、モデルの性能向上に寄与する可能性があります。

この関数の使用後の方針としては、最大値や最小値、中央値、平均値等の集約統計量ともとの数値データとの差を取ることで新しい特徴量の作成も考えられます。

カテゴリデータ × カテゴリデータ

from typing import List, Tuple
from itertools import combinations

def create_category_combinations(
    df: pd.DataFrame,
    cat_cols: List[str],
    r: int = 2,
    sep: str = "_"
) -> pd.DataFrame:
    """
    カテゴリカル変数の組み合わせを作成し、新しい特徴量として追加する。

    Args:
        df (pd.DataFrame): 入力データフレーム
        cat_cols (List[str]): 組み合わせを作成するカテゴリカル変数のリスト
        r (int): 組み合わせる変数の数(デフォルト: 2)
        sep (str): 新しい特徴量名を作成する際の区切り文字(デフォルト: "_")

    Returns:
        pd.DataFrame: 新しい組み合わせ特徴量が追加されたデータフレーム
    """
    new_features = []

    for cols in combinations(cat_cols, r):
        new_col_name = f"{'_'.join(cols)}_combined"
        df[new_col_name] = df[list(cols)].astype(str).agg(sep.join, axis=1)
        new_features.append(new_col_name)

    return df[new_features]

def create_multiple_category_combinations(
    train: pd.DataFrame,
    test: pd.DataFrame,
    cat_cols: List[str],
    r_values: List[int] = [2],
    sep: str = "_"
) -> Tuple[pd.DataFrame, pd.DataFrame]:
    """
    訓練データとテストデータに対して、複数のrの値でカテゴリカル変数の組み合わせを作成します。

    Args:
        train (pd.DataFrame): 訓練データ
        test (pd.DataFrame): テストデータ
        cat_cols (List[str]): 組み合わせを作成するカテゴリカル変数のリスト
        r_values (List[int]): 使用するrの値のリスト(デフォルト: [2, 3])
        sep (str): 新しい特徴量名を作成する際の区切り文字(デフォルト: "_")

    Returns:
        Tuple[pd.DataFrame, pd.DataFrame]: 新しい組み合わせ特徴量が追加された訓練データとテストデータ
    """
    for r in r_values:
        train_new_features = create_category_combinations(train, cat_cols, r, sep)
        test_new_features = create_category_combinations(test, cat_cols, r, sep)

        train = pd.concat([train, train_new_features], axis=1)
        test = pd.concat([test, test_new_features], axis=1)

    return train, test

# 使用例
# train_processed, test_processed = create_multiple_category_combinations(
#     train,
#     test,
#     cat_cols=['category', 'subcategory', 'brand'],
#     r_values=[2]
# )

この実装には以下の特徴があります:

1.create_category_combinations 関数は単一のデータフレームに対して、指定されたrの値でカテゴリカル変数の組み合わせを作成します。
2.create_multiple_category_combinations 関数は訓練データとテストデータの両方に対して、複数のrの値でカテゴリカル変数の組み合わせを作成します。
3.Pythonの標準ライブラリである itertools.combinations を使用して、効率的に組み合わせを生成しています。
4.新しい特徴量名は、組み合わせた変数名を連結し、最後に "combined" を付加しています。
型ヒントを使用して、関数の引数と戻り値の型を明示しています。
デフォルトでは r=2 の組み合わせを作成しますが、r_values 引数を変更することで柔軟に調整できます。
5.区切り文字 sep はデフォルトで "_" ですが、必要に応じて変更できます。

Discussion