💡

ExcelユーザーのためのPandas「locメソッド」ガイド

2024/12/31に公開

はじめに

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

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 りんご 100 50 果物 国内
1 みかん 80 100 果物 国内
2 バナナ 120 30 果物 海外
3 いちご 450 20 果物 国内
4 メロン 800 15 果物 国内
5 ぶどう 300 45 果物 国内
6 キウイ 150 60 果物 海外
7 マンゴー 600 10 果物 海外

条件付き更新の基本

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 りんご 100 50 果物 国内
1 みかん 80 100 果物 国内
2 バナナ 120 30 果物 海外
3 いちご 450 20 果物 国内
4 メロン 800 15 高級フルーツ 国内
5 ぶどう 300 45 果物 国内
6 キウイ 150 60 果物 海外
7 マンゴー 600 10 高級フルーツ 海外

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

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

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

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

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

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

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

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

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 りんご 100 50 果物 国内 100.0
1 みかん 80 100 果物 国内 72.0
2 バナナ 120 30 果物 海外 132.0
3 いちご 450 20 果物 国内 495.0
4 メロン 800 15 果物 国内 880.0
5 ぶどう 300 45 果物 国内 300.0
6 キウイ 150 60 果物 海外 150.0
7 マンゴー 600 10 果物 海外 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.000 50 果物 国内
1 みかん 80.000 100 果物 国内
2 バナナ 158.700 20 果物 海外
3 いちご 450.000 20 果物 国内
4 メロン 800.000 15 果物 国内
5 ぶどう 300.000 45 果物 国内
6 キウイ 198.375 50 果物 海外
7 マンゴー 793.500 0 果物 海外

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 りんご 100 50 果物 国内 在庫十分
1 みかん 80 100 果物 国内 在庫十分
2 バナナ 120 30 果物 海外 在庫あと少し
3 いちご 450 20 果物 国内 在庫あと少し
4 メロン 800 15 果物 国内 在庫あと少し
5 ぶどう 300 45 果物 国内 在庫十分
6 キウイ 150 60 果物 海外 在庫十分
7 マンゴー 600 10 果物 海外 高級品:残りわずか

エラー回避と注意点

データ型の一致

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

# 価格が文字列として格納されている場合の対処
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 りんご 100 50 果物 国内 NaN
1 みかん 80 100 果物 国内 NaN
2 バナナ 120 30 果物 海外 NaN
3 いちご 450 20 果物 国内 NaN
4 メロン 800 15 果物 国内 NaN
5 ぶどう 300 45 果物 国内 NaN
6 キウイ 150 60 果物 海外 NaN
7 マンゴー 600 10 果物 海外 要注文

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

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

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で実際に試してみてください。コードを実行して結果を確認しながら、少しずつ理解を深めていくことをお勧めします😊

Discussion