機械学習データ前処理入門

機械学習データ前処理:条件分岐で新しい列を作成する方法【Pandas入門】

Tags: Python, 機械学習, データ前処理, Pandas, 特徴量エンジニアリング

機械学習モデルの性能を高めるためには、適切なデータ前処理が不可欠です。収集した生データには、そのままではモデルが活用しにくい情報が含まれていることがよくあります。特に、既存のデータの値に応じて新しい情報を付与したい場面は多く、これは「条件分岐」という手法を用いて新しい列として表現されます。

この新しい列は、ビジネス上の特定ルール(例: 購入金額が〇〇円以上の顧客にフラグを立てる)や、分析上の意味合い(例: 年齢を年代別のカテゴリに分ける)を持たせることができ、機械学習モデルにとって有用な「特徴量」となることがあります。

この記事では、Pythonのデータ分析ライブラリであるPandasを使用し、データフレーム内で既存の列の条件に基づいて新しい列を作成する基本的な方法を、具体的なコード例とともにご紹介します。

なぜデータ前処理で条件分岐が必要か?

機械学習モデルは数値データを扱うことが得意ですが、それ以上に、データの「パターン」や「関係性」を学習します。生データに含まれる情報の中には、そのままではモデルがパターンを捉えにくいものがあります。

例えば、顧客データの「購入金額」という列があったとします。この数値をそのままモデルに入力することも可能ですが、「購入金額が10万円以上の顧客は特別なグループである」といったビジネス上の知見がある場合、この知見をデータに反映させたいと考えます。

このような場合、購入金額が10万円以上かどうかを判定し、その結果を「優良顧客フラグ」といった新しい列(例: 1なら優良顧客、0ならそれ以外)としてデータフレームに追加します。モデルはこの「優良顧客フラグ」という列を見ることで、購入金額が高い顧客とそれ以外の顧客の間にあるかもしれないパターンを学習しやすくなるのです。

このように、条件に基づいて新しい列を作成する前処理は、以下の目的で重要になります。

Pandasで条件分岐により新しい列を作成する基本

Pandasでは、データフレームに対して様々な方法で条件分岐を適用し、新しい列を作成することができます。ここでは、特によく使われるいくつかの方法をご紹介します。

まずは、サンプルデータを作成します。顧客の購入履歴を模したデータフレームを考えます。

import pandas as pd
import numpy as np

# サンプルデータの作成
data = {
    '顧客ID': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    '購入金額': [5000, 12000, 3000, 150000, 8000, 2000, 90000, 6000, 10000, 4000],
    '購入回数': [2, 5, 1, 15, 3, 1, 10, 2, 4, 1],
    '居住地': ['東京', '大阪', '東京', '福岡', '大阪', '東京', '福岡', '大阪', '東京', '東京'],
    '年齢': [30, 45, 22, 58, 35, 25, 62, 40, 33, 28]
}
df = pd.DataFrame(data)

print("元のデータフレーム:")
print(df)
元のデータフレーム:
   顧客ID  購入金額  購入回数 居住地  年齢
0     1    5000     2  東京  30
1     2   12000     5  大阪  45
2     3    3000     1  東京  22
3     4  150000    15  福岡  58
4     5    8000     3  大阪  35
5     6    2000     1  東京  25
6     7   90000    10  福岡  62
7     8    6000     2  大阪  40
8     9   10000     4  東京  33
9    10    4000     1  東京  28

方法1: np.where を使う(シンプルでよく使われる)

numpy.where()関数は、条件に基づいて値を選択するための非常に便利な関数です。np.where(条件式, Trueの場合の値, Falseの場合の値)という形で使用します。新しい列を作成する際に、最もシンプルに条件分岐を適用したい場合に適しています。

例として、「購入金額」が50000円以上の顧客に「高額購入者」というフラグ(1または0)を立てる新しい列高額購入者フラグを作成してみましょう。

# np.where を使って新しい列を作成
df['高額購入者フラグ'] = np.where(df['購入金額'] >= 50000, 1, 0)

print("\n'高額購入者フラグ'列を追加したデータフレーム:")
print(df)
'高額購入者フラグ'列を追加したデータフレーム:
   顧客ID  購入金額  購入回数 居住地  年齢  高額購入者フラグ
0     1    5000     2  東京  30          0
1     2   12000     5  大阪  45          0
2     3    3000     1  東京  22          0
3     4  150000    15  福岡  58          1
4     5    8000     3  大阪  35          0
5     6    2000     1  東京  25          0
6     7   90000    10  福岡  62          1
7     8    6000     2  大阪  40          0
8     9   10000     4  東京  33          0
9    10    4000     1  東京  28          0

np.whereは、指定した条件(df['購入金額'] >= 50000)がTrueである行には2つ目の引数の値(1)を、Falseである行には3つ目の引数の値(0)を適用してくれます。

方法2: loc を使う(特定の行に値を代入)

locアクセサは、条件に基づいて行を選択し、その選択された行に対して値を代入する際に強力です。特定の条件を満たす行にのみ値を設定したい場合に適しています。

例として、「居住地」が「東京」の顧客に対して、「地域」という新しい列に「関東」という値を設定してみましょう。その他の地域には、今回は特に値を設定しない(または後で別の条件で設定する)という場合に考えられます。

# '地域'列をNoneで初期化
df['地域'] = None

# loc を使って条件を満たす行に値を代入
df.loc[df['居住地'] == '東京', '地域'] = '関東'

print("\n'地域'列を追加したデータフレーム:")
print(df)
'地域'列を追加したデータフレーム:
   顧客ID  購入金額  購入回数 居住地  年齢  高額購入者フラグ    地域
0     1    5000     2  東京  30          0    関東
1     2   12000     5  大阪  45          0  None
2     3    3000     1  東京  22          0    関東
3     4  150000    15  福岡  58          1  None
4     5    8000     3  大阪  35          0  None
5     6    2000     1  東京  25          0    関東
6     7   90000    10  福岡  62          1  None
7     8    6000     2  大阪  40          0  None
8     9   10000     4  東京  33          0    関東
9    10    4000     1  東京  28          0    関東

df.loc[df['居住地'] == '東京', '地域'] = '関東' は、「居住地列の値が東京である行」を選び出し、その行の地域列に関東という値を代入するという意味になります。

複数条件での値代入を連ねて記述することもできますが、多分岐の場合は後述のapplyや関数定義の方がコードが読みやすくなることがあります。

方法3: apply と関数を使う(複雑な条件や処理)

より複雑な条件分岐や、複数の列の値を使った計算が必要な場合は、関数を定義してapplyメソッドでデータフレームの各行(または列)に適用する方法が有効です。

例として、「年齢」列から「年代」というカテゴリ列を作成してみましょう。例えば、20代、30代、40代、50代、60代以上といった区分を作成します。

# 年齢から年代を判断する関数を定義
def classify_age_group(age):
    if age < 20:
        return '10代'
    elif 20 <= age < 30:
        return '20代'
    elif 30 <= age < 40:
        return '30代'
    elif 40 <= age < 50:
        return '40代'
    elif 50 <= age < 60:
        return '50代'
    else: # age >= 60
        return '60代以上'

# apply メソッドを使って新しい列を作成 (axis=1で各行に適用)
df['年代'] = df['年齢'].apply(classify_age_group)

print("\n'年代'列を追加したデータフレーム:")
print(df)
'年代'列を追加したデータフレーム:
   顧客ID  購入金額  購入回数 居住地  年齢  高額購入者フラグ    地域     年代
0     1    5000     2  東京  30          0    関東    30代
1     2   12000     5  大阪  45          0  None    40代
2     3    3000     1  東京  22          0    関東    20代
3     4  150000    15  福岡  58          1  None    50代
4     5    8000     3  大阪  35          0  None    30代
5     6    2000     1  東京  25          0    関東    20代
6     7   90000    10  福岡  62          1  None  60代以上
8     9   10000     4  東京  33          0    関東    30代
9    10    4000     1  東京  28          0    関東    20代

applyメソッドはシリーズ(この場合はdf['年齢'])の各要素に関数を適用し、その結果を新しいシリーズとして返します。今回は「年齢」列単体に適用しましたが、apply(axis=1)とすることでデータフレームの各行を関数に渡すことも可能です。これにより、複数列の値を考慮した複雑な条件分岐や計算結果を新しい列として追加できます。

例:購入金額と購入回数の両方を考慮して顧客ランクを決定する場合(行全体を関数に渡す例)。

# 購入金額と購入回数からランクを判断する関数を定義
def classify_customer_rank(row):
    if row['購入金額'] >= 100000 and row['購入回数'] >= 10:
        return 'プラチナ'
    elif row['購入金額'] >= 50000 or row['購入回数'] >= 5:
        return 'ゴールド'
    else:
        return '一般'

# apply(axis=1) を使って新しい列を作成 (各行に関数を適用)
df['顧客ランク'] = df.apply(classify_customer_rank, axis=1)

print("\n'顧客ランク'列を追加したデータフレーム:")
print(df)
'顧客ランク'列を追加したデータフレーム:
   顧客ID  購入金額  購入回数 居住地  年齢  高額購入者フラグ    地域     年代  顧客ランク
0     1    5000     2  東京  30          0    関東    30代     一般
1     2   12000     5  大阪  45          0  None    40代   ゴールド
2     3    3000     1  東京  22          0    関東    20代     一般
3     4  150000    15  福岡  58          1  None    50代   プラチナ
4     5    8000     3  大阪  35          0  None    30代     一般
5     6    2000     1  東京  25          0    関東    20代     一般
6     7   90000    10  福岡  62          1  None  60代以上   ゴールド
8     9   10000     4  東京  33          0    関東    30代     一般
9    10    4000     1  東京  28          0    関東    20代     一般

axis=1を指定することで、apply関数はデータフレームの各行(Seriesオブジェクト)を引数としてclassify_customer_rank関数に渡します。関数内でrow['列名']のようにして各列の値にアクセスし、条件分岐や計算を行うことができます。

複数の条件を組み合わせる方法

上記の方法で、単一の条件だけでなく複数の条件を組み合わせて新しい列を作成することも可能です。条件式は論理演算子& (AND), | (OR), ~ (NOT) を使って結合します。条件式を組み合わせる際は、それぞれの条件式を必ず丸括弧()で囲む必要があります。

例として、「居住地が東京」かつ「購入金額が1万円以上」の顧客にフラグを立ててみましょう。np.whereと組み合わせます。

# 複数の条件を組み合わせて新しい列を作成
df['東京かつ1万円以上フラグ'] = np.where((df['居住地'] == '東京') & (df['購入金額'] >= 10000), 1, 0)

print("\n複数の条件で列を追加したデータフレーム:")
print(df)
複数の条件で列を追加したデータフレーム:
   顧客ID  購入金額  購入回数 居住地  年齢  高額購入者フラグ    地域     年代  顧客ランク  東京かつ1万円以上フラグ
0     1    5000     2  東京  30          0    関東    30代     一般              0
1     2   12000     5  大阪  45          0  None    40代   ゴールド              0
2     3    3000     1  東京  22          0    関東    20代     一般              0
3     4  150000    15  福岡  58          1  None    50代   プラチナ              0
4     5    8000     3  大阪  35          0  None    30代     一般              0
5     6    2000     1  東京  25          0    関東    20代     一般              0
6     7   90000    10  福岡  62          1  None  60代以上   ゴールド              0
8     9   10000     4  東京  33          0    関東    30代     一般              1
9    10    4000     1  東京  28          0    関東    20代     一般              0

条件式(df['居住地'] == '東京')(df['購入金額'] >= 10000)&でつなぎ、全体をnp.whereの第一引数として渡しています。これにより、両方の条件を満たす行にのみ1が設定されます。

まとめ

この記事では、Pandasを使用してデータフレームに新しい列を追加する際に、既存の列の値に基づいて条件分岐を行う基本的な方法をご紹介しました。

これらの手法を使い分けることで、ビジネスルールをデータに反映させたり、機械学習モデルにとってより有用な特徴量を作成したりすることが可能になります。データ前処理のステップで条件分岐を効果的に活用し、より分析に適したデータを作成してください。