PandasでExcelのif文みたいなことをする方法

目次

はじめに

「特定の条件に当てはまる行だけ更新したい…」
「複雑な条件でのデータ更新がうまくいかない…」
「データの一括置換はできるけど、一部だけを更新するのが難しい…」

Pythonでデータ分析を始めると、必ずぶつかる壁の一つが条件付きデータの更新です。
特にExcelから移行してきた方は、オートフィルやif文のような直感的な操作ができず、戸惑うことも多いのではないでしょうか。

今回は、pandasのlocメソッドによる条件付きデータ更新について、基礎から実践的なテクニックまで詳しく解説していきます。

データの準備

まずは、実践的なサンプルデータを用意しましょう。

import pandas as pd
import numpy as np

# 商品管理データの作成
data = {
    '商品名': ['りんご', 'みかん', 'バナナ', 'いちご', 'メロン', 'ぶどう', 'キウイ', 'マンゴー'],
    '価格': [100, 80, 120, 450, 800, 300, 150, 600],
    '在庫': [50, 100, 30, 20, 15, 45, 60, 10],
    'カテゴリ': ['果物', '果物', '果物', '果物', '果物', '果物', '果物', '果物'],
    '産地': ['国内', '国内', '海外', '国内', '国内', '国内', '海外', '海外']
}
df = pd.DataFrame(data)
df
商品名価格在庫カテゴリ産地
0りんご10050果物国内
1みかん80100果物国内
2バナナ12030果物海外
3いちご45020果物国内
4メロン80015果物国内
5ぶどう30045果物国内
6キウイ15060果物海外
7マンゴー60010果物海外

条件付き更新の基本

locメソッドの基本構文

まずは、pandasのlocメソッドの基本的な構文について理解しておきましょう。

# 基本構文
df.loc[行の条件, 列の指定] = 更新する値

# 具体例:
df.loc[df['価格'] > 1000, '商品区分'] = '高額商品'  # 単一列の更新
df.loc[df['在庫'] < 5, ['価格', '状態']] = [0, '品切れ']  # 複数列の更新

ここで重要なのは、locメソッドの[]の中には2つの要素があるということです。

  • 1つ目の要素:行の選択条件(どの行を更新するか)
  • 2つ目の要素:列の指定(どの列を更新するか)

applyメソッドとlocの組み合わせ

より複雑な更新処理が必要な場合、locとapplyメソッドを組み合わせることができます。

# 基本構文
df.loc[行の条件, '更新する列'] = df[条件に合う行].apply(更新用の関数, axis=1)

# 具体例:
df.loc[df['在庫'] < 10, '価格'] = df[df['在庫'] < 10].apply(
    lambda row: row['価格'] * 1.2 if row['産地'] == '海外' else row['価格'],
    axis=1
)

この場合の処理の流れは以下のようになります。

  1. df['在庫'] < 10で対象となる行を選択
  2. 選択された行に対してapply関数を適用
  3. 関数の戻り値で該当する列を更新

シンプルな条件での更新

最も基本的な使い方から見ていきましょう。以下は価格が500円以上の商品を「高級フルーツ」カテゴリに変更する例です。

# 価格による条件付き更新
df.loc[df['価格'] >= 500, 'カテゴリ'] = '高級フルーツ'
df
商品名価格在庫カテゴリ産地
0りんご10050果物国内
1みかん80100果物国内
2バナナ12030果物海外
3いちご45020果物国内
4メロン80015高級フルーツ国内
5ぶどう30045果物国内
6キウイ15060果物海外
7マンゴー60010高級フルーツ海外

この操作は以下のような流れで処理されます。

  1. df['価格'] >= 500 で条件に合う行を選択(TrueまたはFalseの配列が生成されます)
  2. 選択された行の ‘カテゴリ’ 列を更新
  3. その他の行は変更されません

複数条件を組み合わせた更新

実務では、複数の条件を組み合わせて更新することが多くあります。
以下は、輸入品で在庫が少ない商品の価格を20%上げる例です。

# 複数条件による価格改定
df.loc[(df['産地'] == '海外') & (df['在庫'] < 30), '価格'] *= 1.2

# 結果を確認(変更された行を抽出)
df.loc[(df['産地'] == '海外') & (df['在庫'] < 30)]

df
商品名価格在庫カテゴリ産地
7マンゴー72010果物海外

実践的な条件付き更新テクニック

1. 既存の値を利用した更新

在庫数に応じて価格を段階的に調整する例を見てみましょう。

# 在庫状況に応じた価格調整
def adjust_price_by_stock(row):
    if row['在庫'] <= 10:
        return row['価格'] * 1.3  # 品薄:30%値上げ
    elif row['在庫'] <= 30:
        return row['価格'] * 1.1  # やや品薄:10%値上げ
    elif row['在庫'] >= 100:
        return row['価格'] * 0.9  # 過剰在庫:10%値下げ
    else:
        return row['価格']  # 通常在庫:変更なし

# 価格の更新を実行
df.loc[:, '調整後価格'] = df.apply(adjust_price_by_stock, axis=1)
df
商品名価格在庫カテゴリ産地調整後価格
0りんご10050果物国内100.0
1みかん80100果物国内72.0
2バナナ12030果物海外132.0
3いちご45020果物国内495.0
4メロン80015果物国内880.0
5ぶどう30045果物国内300.0
6キウイ15060果物海外150.0
7マンゴー60010果物海外780.0

2. 複数列の同時更新

同じ条件で複数の列を更新する場合もよくあります。

# 海外産の商品の価格と在庫情報を同時に更新
df.loc[df['産地'] == '海外', ['価格', '在庫']] = df.loc[df['産地'] == '海外'].apply(lambda row: pd.Series({
        '価格': row['価格'] * 1.15,  # 15%値上げ
        '在庫': row['在庫'] - 5  # 在庫数を5減らす
    }),
    axis=1
)

df
商品名価格在庫カテゴリ産地
0りんご100.00050果物国内
1みかん80.000100果物国内
2バナナ158.70020果物海外
3いちご450.00020果物国内
4メロン800.00015果物国内
5ぶどう300.00045果物国内
6キウイ198.37550果物海外
7マンゴー793.5000果物海外

3. 条件に応じた文字列の更新

商品の状態を動的に更新する例です。

def get_stock_status(stock, price):
    if stock == 0:
        return '品切れ'
    elif stock <= 10:
        if price >= 500:
            return '高級品:残りわずか'
        else:
            return '残りわずか'
    elif stock <= 30:
        return '在庫あと少し'
    else:
        return '在庫十分'

# 在庫状況ラベルの更新
df.loc[:, '在庫状況'] = df.apply(lambda row: get_stock_status(row['在庫'], row['価格']), axis=1)
df
商品名価格在庫カテゴリ産地在庫状況
0りんご10050果物国内在庫十分
1みかん80100果物国内在庫十分
2バナナ12030果物海外在庫あと少し
3いちご45020果物国内在庫あと少し
4メロン80015果物国内在庫あと少し
5ぶどう30045果物国内在庫十分
6キウイ15060果物海外在庫十分
7マンゴー60010果物海外高級品:残りわずか

エラー回避と注意点

データ型の一致

条件付き更新でよく起こるエラーの一つが、データ型の不一致です。
以下は正しい対処法を示しています。

# 価格が文字列として格納されている場合の対処
df.loc[:, '価格'] = pd.to_numeric(df['価格'], errors='coerce')

# 欠損値(NaN)がある場合の対処
df.loc[df['価格'].notnull() & (df['価格'] > 300), 'カテゴリ'] = '高級フルーツ'

条件式の構築

複雑な条件式は、可読性を考慮して段階的に構築することをお勧めします。

# 条件式を分割して構築
price_condition = df['価格'] >= 300
stock_condition = df['在庫'] <= 20
origin_condition = df['産地'] == '海外'

# 条件を組み合わせて更新
target_condition = price_condition & stock_condition & origin_condition
df.loc[target_condition, 'ステータス'] = '要注文'

df
商品名価格在庫カテゴリ産地ステータス
0りんご10050果物国内NaN
1みかん80100果物国内NaN
2バナナ12030果物海外NaN
3いちご45020果物国内NaN
4メロン80015果物国内NaN
5ぶどう30045果物国内NaN
6キウイ15060果物海外NaN
7マンゴー60010果物海外要注文

応用:実務で使えるユーティリティ関数

実務でよく使う更新処理をまとめた関数の例を示します。

def update_prices_with_rules(df, rules):
    """価格更新ルールに基づいて商品価格を一括更新する関数

    Parameters
    ----------
    df : pandas.DataFrame
        更新対象のデータフレーム
    rules : list of dict
        更新ルールのリスト。各ルールは以下のキーを持つ辞書:
        - condition: 更新条件を指定する関数
        - action: 更新内容を指定する関数

    Returns
    -------
    pandas.DataFrame
        更新後のデータフレーム
    """
    df_copy = df.copy()

    for rule in rules:
        condition = rule['condition'](df_copy)
        df_copy.loc[condition, '価格'] = rule['action'](df_copy[condition])

    return df_copy

# 使用例
rules = [
    {
        'condition': lambda df: (df['在庫'] < 20) & (df['産地'] == '海外'),
        'action': lambda df: df['価格'] * 1.2
    },
    {
        'condition': lambda df: df['在庫'] > 100,
        'action': lambda df: df['価格'] * 0.9
    }
]

updated_df = update_prices_with_rules(df, rules)

まとめ

pandasの条件付き更新は、最初は少し取っつきにくく感じるかもしれません。しかし、基本的な使い方を理解し、実践的なテクニックを身につけることで、Excelよりもずっと柔軟で効率的なデータ処理が可能になります。

特に、複数の条件を組み合わせた更新や、既存の値に基づく動的な更新など、Excelでは複雑になりがちな処理も、pandasでは簡潔に記述できます。

ぜひJupyter Notebookで実際に試してみてください。コードを実行して結果を確認しながら、少しずつ理解を深めていくことをお勧めします。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

都内の金融機関で経営企画をしています。
2年でメガバンクを辞めてしまいましたが、むしろ人生が豊かになりました。
データアナリスト的なことをしていたのでPythonとTableauがちょっとだけ使えます。
文系大卒→メガバンク(営業)→広告系ベンチャー(経営企画、FP&A、データアナリスト)→都内金融機関(経営企画)

コメント

コメントする

目次