機械学習データ前処理:複数の列にまとめて同じ処理を適用する方法【Pandas入門】
データ前処理において、特定の列に対して同じ種類の処理を行うことは頻繁に発生します。例えば、複数の数値列の欠損値をそれぞれの中央値で埋めたり、複数の文字列列から不要な空白を取り除いたりする場合などです。
このような作業を一つずつ手作業で行うのは、列数が多いほど時間もかかり、ミスも発生しやすくなります。しかし、Pandasライブラリを使用すれば、複数の列に対して効率的に同じ処理をまとめて適用することができます。これにより、コードが簡潔になり、メンテナンス性も向上します。
本記事では、Pandasを使ってデータフレームの複数の列を選択し、そこに特定の処理や関数を適用する方法について、具体的なコード例を交えながら解説します。
なぜ複数の列にまとめて処理する必要があるのか?
データ分析や機械学習モデルの構築において、多くのデータセットは複数の特徴量(列)を持っています。これらの特徴量に対して、以下のような同じ種類の前処理が必要になる場面があります。
- 欠損値の処理: 特定の種類の列(例: 全ての数値列)に対して、同じ方法(例: 平均値、中央値、最頻値で補完)で欠損値を処理したい。
- データ型の変換: 複数の列のデータ型を統一したい(例: 文字列として読み込まれた数字列を数値型に変換)。
- 値の標準化・正規化: 複数の数値列に対して同じスケーリング手法を適用したい。
- 文字列のクレンジング: 複数のテキスト列から特定の文字やパターンを取り除きたい、大文字・小文字を統一したい。
- 単位の変換: 複数の測定値列の単位を一括で変換したい(例: キログラムをポンドに)。
これらの処理を列ごとに記述していくと、コードが長くなり、可読性も低下します。Pandasの機能を活用することで、これらの作業をより効率的かつ体系的に行うことが可能になります。
Pandasで複数の列を選択する方法
まず、複数の列に処理を適用する前に、その対象となる列をデータフレームから選択する必要があります。Pandasでは、複数の列を選択するためにいくつかの方法が提供されています。
最も基本的な方法は、列名のリストを指定する方法です。
import pandas as pd
import numpy as np
# サンプルデータフレームを作成
data = {
'ID': [1, 2, 3, 4, 5],
'売上(万円)': [100, 150, np.nan, 120, 180],
'費用(万円)': [50, 70, 60, np.nan, 90],
'店舗名': ['A店', 'B店', 'C店', 'D店', 'E店'],
'評価点': [4.5, 3.8, np.nan, 4.0, 4.2],
'備考': ['通常', 'セール期間', '', '新規顧客', '通常']
}
df = pd.DataFrame(data)
print("元データフレーム:")
print(df)
# 処理対象としたい複数の列をリストで指定
target_columns_numeric = ['売上(万円)', '費用(万円)', '評価点']
# 指定した列のみを表示
print("\n選択した列:")
print(df[target_columns_numeric])
このコードでは、['売上(万円)', '費用(万円)', '評価点'] というリストを使って、データフレーム df からこれらの3つの列を選択しています。この選択した部分に対して、後述する処理を適用していきます。
特定の条件を満たす列を選択することも可能です。例えば、数値型の列だけを選択したい場合は、select_dtypes メソッドを使用します。
# 数値型の列のみを選択
numeric_columns = df.select_dtypes(include=np.number).columns.tolist()
print("\n数値型の列:")
print(numeric_columns)
# 文字列型の列のみを選択
object_columns = df.select_dtypes(include='object').columns.tolist()
print("\n文字列型の列:")
print(object_columns)
select_dtypes を使うことで、データ型に基づいて柔軟に列を選択できます。include には 'object', 'number', 'float', 'int' などを指定できます。
複数の列に同じ処理を適用する具体的な方法
複数の列を選択したら、いよいよそこに処理を適用します。ここではいくつかの典型的な処理方法と、それに対応するPandasの機能を紹介します。
方法1: 選択した列に対して直接代入する
最もシンプルなケースは、選択した複数の列に対して、ある計算結果や変換結果をまとめて代入する方法です。
例えば、選択した数値列の欠損値を、それぞれの列の平均値で補完する場合を考えます。
# 処理対象としたい数値列をリストで指定
target_numeric_cols = ['売上(万円)', '費用(万円)', '評価点']
# 選択した列に対して、fillnaメソッドとapply(lambda x: x.mean())を組み合わせて平均値で補完
# 注意: apply(lambda x: x.mean()) は、選択したDataFrameの各列に対して実行される
df[target_numeric_cols] = df[target_numeric_cols].fillna(df[target_numeric_cols].mean())
print("\n欠損値を各列の平均値で補完後:")
print(df)
この例では、df[target_numeric_cols] で複数の列を選択し、その結果に対して fillna() メソッドを適用しています。fillna() の引数に df[target_numeric_cols].mean() を渡すことで、選択した各列の平均値(これは Series として返されます)を使って、それぞれの列の欠損値が埋められます。
別の例として、選択した文字列列の先頭と末尾の空白を一括で除去する場合を考えます。
# サンプルデータフレーム(文字列に空白を含む)
data_str = {
'商品名': ['りんご ', ' バナナ', 'オレンジ'],
'カテゴリ': [' 果物', '果物 ', ' 野菜 '],
'価格': [100, 200, 150]
}
df_str = pd.DataFrame(data_str)
print("\n元データフレーム (文字列に空白):")
print(df_str)
# 処理対象としたい文字列列をリストで指定
target_string_cols = ['商品名', 'カテゴリ']
# 選択した列に対して、str.strip()メソッドを適用
# .strアクセサは文字列操作メソッドを提供します
df_str[target_string_cols] = df_str[target_string_cols].astype(str).apply(lambda x: x.str.strip())
print("\n文字列の空白除去後:")
print(df_str)
ここでは、df_str[target_string_cols] で列を選択し、apply(lambda x: x.str.strip()) を使っています。.str アクセサは文字列操作のために使われ、strip() は文字列の前後から空白を取り除くメソッドです。apply は、選択したDataFrame(この場合は df_str[target_string_cols])の各列(Series)に対して指定した関数(ここではラムダ関数 lambda x: x.str.strip())を適用します。astype(str) は、念のため対象列を文字列型に変換してから処理を行うためのものです。
このように、選択した複数の列に対して、Pandasのメソッドや関数を直接適用し、その結果を元の場所に代入するというパターンは非常に一般的です。
方法2: applymap メソッドを使用する (DataFrame全体または選択したDataFrameに要素wiseで適用)
applymap() メソッドは、データフレームの各要素(個々のセル)に関数を適用したい場合に便利です。これは特に、データフレーム全体、あるいは選択したデータフレームの全ての要素に対して、同じ簡単な変換を行いたい場合に有効です。
例えば、選択した数値列の全ての値を小数点以下第1位で丸めたい場合を考えます。
# 元のdfデータフレームを使用
print("\n元データフレーム:")
print(df)
# 処理対象としたい数値列をリストで指定
target_numeric_cols_round = ['売上(万円)', '費用(万円)', '評価点']
# 選択した列に対してapplymapで小数点以下第1位に丸める関数を適用
# NaNは数値ではないためエラーになる可能性があるため、fillnaで事前に補完しておく
df_rounded = df[target_numeric_cols_round].fillna(df[target_numeric_cols_round].mean()).applymap(lambda x: round(x, 1))
# 丸めた結果を元のデータフレームに戻す場合は代入
# df[target_numeric_cols_round] = df[target_numeric_cols_round].fillna(df[target_numeric_cols_round].mean()).applymap(lambda x: round(x, 1))
print("\n選択した数値列を小数点以下第1位に丸めた結果:")
print(df_rounded)
# 元のdfには代入していないので、df自体は変化なし
# print("\n丸めた結果を元のdfに代入後:")
# print(df)
applymap(lambda x: round(x, 1)) は、選択されたデータフレーム df[target_numeric_cols_round] の中の全ての要素に対して、round(x, 1) という関数を適用します。x は各セルの値に対応します。欠損値(NaN)に対して数値計算を行うとエラーになる場合があるため、ここでは丸める前に欠損値を補完しています。
applymap はデータフレーム全体に対しても使用できますが、特定の列に対してのみ適用したい場合は、このように事前に列を選択してから使用します。
方法3: apply メソッドとカスタム関数を使用する (列wiseまたは行wiseで適用)
apply() メソッドは、データフレームの列(デフォルト)または行に対して、少し複雑な処理を行うカスタム関数を適用したい場合に非常に強力です。applymap が要素ごとに適用されるのに対し、apply は Series オブジェクト(列または行)全体に関数が適用されます。
例えば、選択した文字列列に対して、特定の文字を置換する処理と、全て大文字に変換する処理を組み合わせたい場合を考えます。
# 元のdf_strデータフレームを使用
print("\n元データフレーム (文字列に空白):")
print(df_str)
# 処理対象としたい文字列列をリストで指定
target_string_cols_complex = ['商品名', 'カテゴリ']
# カスタム関数を定義
def clean_and_uppercase(series):
# Series(列)を受け取り、各要素に対して処理を行う
# .strアクセサを使って文字列操作メソッドをチェーンできる
return series.str.replace(' ', '').str.upper()
# 選択した列に対してapplyメソッドでカスタム関数を適用 (axis=0 は列方向)
df_str[target_string_cols_complex] = df_str[target_string_cols_complex].apply(clean_and_uppercase, axis=0)
print("\nカスタム関数(空白除去+大文字化)適用後:")
print(df_str)
ここでは、clean_and_uppercase というカスタム関数を定義しました。この関数は Pandas Series(ここでは選択された列の Series)を受け取り、.str.replace(' ', '') で空白を除去し、.str.upper() で全て大文字に変換した結果の Series を返します。df_str[target_string_cols_complex].apply(clean_and_uppercase, axis=0) は、選択されたデータフレームの各列に対して、この clean_and_uppercase 関数を適用しています。axis=0 は列方向に適用することを意味します(デフォルトのため省略可能ですが、明示すると分かりやすいです)。
apply メソッドは、applymap よりも柔軟で複雑な処理を適用できますが、パフォーマンスは applymap やベクトル化されたPandasメソッド(例: df['col'].str.method() や df['col'].fillna())に比べて劣る場合があります。可能な限りベクトル化されたメソッドを使用することが推奨されます。しかし、複数のステップを含むカスタム処理や、各列の統計量に基づいて処理を変えたい場合などには apply が有効です。
まとめ
データ前処理において、複数の列に同じ種類の処理を効率的に適用することは、コードの可読性を高め、ミスのリスクを減らし、作業時間を短縮するために非常に重要です。
本記事では、Pandasを使って以下の方法で複数の列を処理する方法を解説しました。
- 列名のリストや
select_dtypesで処理対象の列を選択する。 - 選択した列に対して、ベクトル化されたPandasメソッド(例:
fillna(),.str.strip()など)を直接適用し、結果を代入する。 applymap()メソッドを使用して、選択したデータフレームの各要素に関数を適用する。apply()メソッドとカスタム関数を使用して、選択したデータフレームの各列(または行)にカスタム処理を適用する。
これらのテクニックを習得することで、データ前処理の効率を大幅に向上させることができます。ご自身のデータに対してこれらの方法を適用し、より効率的なデータ前処理を実践してみてください。