0Pricing
Pandas & NumPy Academy · レッスン

列と行へのapply()

axis=0とaxis=1を指定したDataFrame.apply()を使って、各列または各行にカスタム関数を実行します。

「列と行へのapply()」はCoddyKit上の無料Pandas & NumPy Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPandas & NumPy Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Pandas & NumPy Academyコースには全4レッスンが含まれています。

apply() を使う場面

DataFrame.apply() は、各列(axis=0)または各行(axis=1)に対してカスタム Python 関数を実行します。これは、組み込みのベクトル化演算よりも遅いため、最後の手段として使うツールです。ただし、NumPy の算術演算、ブールインデックス、組み込みの集計関数では表現できない計算には不可欠です。複雑な条件分岐、独自の文字列解析、または一度に 1 行や 1 列を処理する複数段階の計算が必要な場合に使用してください。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'price': [10.5, 25.0, 8.3, 100.0],
    'quantity': [3, 1, 5, 2],
    'tax_rate': [0.05, 0.10, 0.05, 0.15]
})
print(df)

列に沿った apply()(axis=0)

axis=0(デフォルト)では、apply() は各列を Series として関数に渡します。関数は一度に 1 列を受け取り、集計の場合はスカラー、変換の場合は Series を返します。すべての数値列に対して、カスタムの正規化やクリーニング処理を 1 回の呼び出しで一律に適用する場合に便利です。

# Custom range normalisation (0 to 1) for each column
def min_max_scale(col):
    return (col - col.min()) / (col.max() - col.min())

df_scaled = df[['price', 'quantity']].apply(min_max_scale, axis=0)
print(df_scaled)

行に沿った apply()(axis=1)

axis=1 では、apply() は各行を Series として関数に渡します。行のインデックスは列名なので、名前を使って値にアクセスできます。各行が固有の税率を持つ場合の税引き後の売上合計の計算など、複数の列に依存する行単位の計算に適しています。

def revenue_after_tax(row):
    subtotal = row['price'] * row['quantity']
    return subtotal * (1 + row['tax_rate'])

df['revenue_after_tax'] = df.apply(revenue_after_tax, axis=1)
print(df)

apply() で複数の値を返す

apply(axis=1) に渡す関数は、名前付きの要素を持つ pd.Series を返すことができ、Pandas はそれを複数の新しい列に展開します。2 つの新しい列を作るために apply() を 2 回呼び出すよりも簡潔です。あるいは、関数から dict を返すこともでき、その場合も Pandas が列に展開します。

def compute_totals(row):
    subtotal = row['price'] * row['quantity']
    tax = subtotal * row['tax_rate']
    return pd.Series({'subtotal': subtotal, 'tax': tax, 'total': subtotal + tax})

result = df.apply(compute_totals, axis=1)
print(result)

列のカスタム集計に apply() を使う

apply(func, axis=0) を使うと、各列のカスタム統計量を計算して、要約用の Series を返せます。たとえば、すべての数値列について、変動係数(標準偏差を平均で割った値)を 1 回の呼び出しで計算できます。結果は列名をインデックスとする Series になり、列ごとの簡単な診断に役立ちます。

def coeff_of_variation(col):
    return col.std() / col.mean() if col.mean() != 0 else np.nan

cv = df[['price', 'quantity']].apply(coeff_of_variation, axis=0)
print('Coefficient of variation per column:')
print(cv)

apply() とベクトル化された代替手段の比較

apply() を使う前に、必ずベクトル化された代替手段が存在するか確認してください。税引き後の売上計算では、df['price'] * df['quantity'] * (1 + df['tax_rate']) は apply(axis=1) 版と同じ処理を行いますが、NumPy の C レベルのループを使うため 10~100 倍高速です。ベクトル化したロジックが読みにくく複雑になる場合に限って、apply() を使用してください。

import time

start = time.time()
df['vectorised'] = df['price'] * df['quantity'] * (1 + df['tax_rate'])
print('Vectorised:', time.time() - start, 's')

start = time.time()
df['apply_result'] = df.apply(revenue_after_tax, axis=1)
print('apply():', time.time() - start, 's')

lambda を使った apply()

短い 1 行の変換では、名前付き関数を定義する代わりに lambda を直接 apply() に渡します。lambda は単純な処理を簡潔に記述できますが、複雑なロジックでは、コメント付きの名前付き関数のほうが理解しやすくテストもしやすいため、避けるべきです。lambda は名前を指定して簡単に単体テストやプロファイリングを行えないため、使用は控えめにしてください。

# Clip revenue between 0 and 200, then round to nearest 10
df['revenue_clean'] = df['revenue_after_tax'].apply(lambda x: round(min(max(x, 0), 200) / 10) * 10)
print(df[['revenue_after_tax', 'revenue_clean']])

apply() に追加の引数を渡す

args タプル、または apply(func, args=(val,), axis=1) のキーワード引数を使って、関数に追加の引数を渡します。これにより関数内でグローバル変数を使わずに済み、異なるパラメータ値で関数を再利用できます。Python 3.8 以降では、functools.partial を使って関数の一部の引数を適用したバージョンを作成することもできます。

def discount_price(row, discount_pct, threshold):
    if row['quantity'] >= threshold:
        return row['price'] * (1 - discount_pct)
    return row['price']

df['discounted_price'] = df.apply(
    discount_price, axis=1,
    args=(0.1, 3)  # 10% discount for quantity >= 3
)
print(df[['price', 'quantity', 'discounted_price']])

型変換に apply() を使う

列に整数、浮動小数点数、文字列などの異なる型が混在している場合、astype() はエラーを発生させます。apply(pd.to_numeric, errors='coerce') を使うと、行ごとに変換を試み、変換できない値を NaN として返せます。これは、本来は数値であるべき列に、データ入力ミスによる数値以外の値がまれに含まれている場合に安全なパターンです。

messy = pd.Series(['10', '20.5', 'N/A', '100', '--'])
clean = messy.apply(pd.to_numeric, errors='coerce')
print(clean)

パフォーマンス:apply() と np.vectorize の比較

スカラーに適用する関数を要素ごとに適用する必要がある場合、np.vectorize(func)(array) は通常、Series.apply(func) より高速です。NumPy の vectorize は、Python の関数呼び出しに伴うオーバーヘッドではなく、C 経由でディスパッチするためです。ただし、np.vectorize は真のブロードキャストより遅いため、ブロードキャスト式ではロジックを表現できない場合にのみ使用してください。

def classify_size(price):
    if price < 15:
        return 'small'
    elif price < 50:
        return 'medium'
    return 'large'

# np.vectorize approach
classify_v = np.vectorize(classify_size)
df['size_class'] = classify_v(df['price'].values)
print(df[['price', 'size_class']])

apply() が適切な選択となる場面

次のような場合は Apply が適切なツールです。(1) 複数の列の値を同時に参照して分岐する必要があるロジック、(2) 行ごとに外部 API やデータベースを呼び出す関数(避けられない逐次処理)、(3) セルに格納された JSON の塊のような複雑な文字列を解析する関数、(4) リストのように長さが可変のオブジェクトを返す関数。それ以外の場合は、速度と可読性のためにベクトル化された演算を優先してください。

import json

# Parse a JSON metadata column row by row
df['metadata'] = ['{"color": "red"}', '{"color": "blue"}', '{}', '{"color": "green"}']
df['color'] = df['metadata'].apply(lambda s: json.loads(s).get('color', 'unknown'))
print(df[['metadata', 'color']])

クイックチェック

このレッスンで学んだデータ分析の概念について、理解度を確認します。

レッスンのまとめ

このレッスンでは、apply(axis=0) を使って列単位のカスタム統計量を計算する方法、apply(axis=1) を使って複数の列を入力とする行単位の計算を行う方法、そしてパフォーマンスのためにベクトル化された代替手段を優先すべき場面を学びました。次は、複雑なグループ単位の集計で apply() と GroupBy を組み合わせる方法を見ていきます。

よくある質問

「列と行へのapply()」レッスンは無料ですか?

はい。「列と行へのapply()」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Pandas & NumPy Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Pandas & NumPy Academyコースには全4レッスンが含まれています。

「列と行へのapply()」で何を学びますか?

axis=0とaxis=1を指定したDataFrame.apply()を使って、各列または各行にカスタム関数を実行します。 ブラウザで直接実行するハンズオンコードでPandas & NumPy Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Pandas & NumPy Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPandas & NumPy Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「列と行へのapply()」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPandas & NumPy Academyレッスンでコードを書いて実行できますか?

はい。すべてのPandas & NumPy Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 列と行へのapply()
  2. GroupByでのapply()
  3. 要素単位の処理に使うmap()とapplymap()
  4. pipe()によるメソッドチェーン
← Pandas & NumPy Academyに戻る