pandas任意順序での並び替え完全ガイド – カスタムソートからカテゴリカルソートまで

 

pandasでデータを扱う際、単純な昇順・降順だけでなく、任意の順序(カスタムオーダー)で並び替えたい場面は頻繁にあります。例えば、「小・中・大」の順序や、曜日順、優先度順など、特定のビジネスロジックに基づいた並び替えです。この記事では、pandasで任意の順序による並び替えを実現する様々な手法を実例とともに詳しく解説します。

1. 基本的な任意順序並び替え(map関数を使用)

サイズ順での並び替え

import pandas as pd

df = pd.DataFrame({
    'product': ['シャツA', 'シャツB', 'シャツC', 'シャツD'],
    'size': ['大', '小', '中', '大'],
    'price': [3000, 2000, 2500, 3200]
})

# サイズの順序を定義
size_order = {'小': 1, '中': 2, '大': 3}

# mapを使って並び替え
df_sorted = df.sort_values('size', key=lambda x: x.map(size_order))
print(df_sorted)

曜日順での並び替え

import pandas as pd

df = pd.DataFrame({
    'day': ['金', '月', '水', '火', '日', '土', '木'],
    'sales': [100, 80, 90, 85, 120, 110, 75]
})

# 曜日の順序を定義
day_order = {'月': 1, '火': 2, '水': 3, '木': 4, '金': 5, '土': 6, '日': 7}

df_sorted = df.sort_values('day', key=lambda x: x.map(day_order))
print(df_sorted)

2. Categoricalデータ型を使った並び替え

基本的なCategoricalソート

import pandas as pd

df = pd.DataFrame({
    'priority': ['中', '高', '低', '高', '中', '低'],
    'task': ['タスクA', 'タスクB', 'タスクC', 'タスクD', 'タスクE', 'タスクF']
})

# Categoricalに変換して順序を指定
priority_order = ['高', '中', '低']
df['priority'] = pd.Categorical(df['priority'], categories=priority_order, ordered=True)

df_sorted = df.sort_values('priority')
print(df_sorted)

複数の任意順序による並び替え

import pandas as pd

df = pd.DataFrame({
    'department': ['営業', '開発', '営業', '人事', '開発', '人事'],
    'level': ['主任', '部長', '課長', '主任', '主任', '部長'],
    'salary': [400, 800, 600, 350, 450, 750]
})

# 部署と役職の順序を定義
dept_order = ['人事', '営業', '開発']
level_order = ['主任', '課長', '部長']

df['department'] = pd.Categorical(df['department'], categories=dept_order, ordered=True)
df['level'] = pd.Categorical(df['level'], categories=level_order, ordered=True)

df_sorted = df.sort_values(['department', 'level'])
print(df_sorted)

3. リスト順序を使った並び替え

indexを使った方法

import pandas as pd

df = pd.DataFrame({
    'month': ['12月', '3月', '7月', '1月', '9月'],
    'revenue': [500, 300, 400, 250, 350]
})

# 月の順序リストを定義
month_order = ['1月', '3月', '7月', '9月', '12月']

# reindex を使用
df['month_sort'] = df['month'].apply(lambda x: month_order.index(x))
df_sorted = df.sort_values('month_sort').drop('month_sort', axis=1)
print(df_sorted)

argsortを使った効率的な方法

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'grade': ['B', 'A+', 'C', 'A', 'B+'],
    'student': ['田中', '佐藤', '鈴木', '高橋', '伊藤'],
    'score': [75, 95, 60, 88, 82]
})

grade_order = ['C', 'B', 'B+', 'A', 'A+']
sorter_index = dict(zip(grade_order, range(len(grade_order))))

df['grade_rank'] = df['grade'].map(sorter_index)
df_sorted = df.sort_values('grade_rank').drop('grade_rank', axis=1)
print(df_sorted)

4. 複雑な条件による任意順序並び替え

条件付きソート

import pandas as pd

df = pd.DataFrame({
    'status': ['完了', '進行中', '未着手', '保留', '完了', '進行中'],
    'importance': ['高', '中', '高', '低', '中', '高'],
    'task_name': ['タスク1', 'タスク2', 'タスク3', 'タスク4', 'タスク5', 'タスク6']
})

# ステータスと重要度の優先順位を定義
status_priority = {'未着手': 1, '進行中': 2, '保留': 3, '完了': 4}
importance_priority = {'高': 1, '中': 2, '低': 3}

df['status_rank'] = df['status'].map(status_priority)
df['importance_rank'] = df['importance'].map(importance_priority)

# まず重要度、次にステータスでソート
df_sorted = df.sort_values(['importance_rank', 'status_rank'])
df_final = df_sorted.drop(['status_rank', 'importance_rank'], axis=1)
print(df_final)

5. 日付・時間の任意順序並び替え

四半期順での並び替え

import pandas as pd

df = pd.DataFrame({
    'quarter': ['Q3', 'Q1', 'Q4', 'Q2', 'Q1', 'Q3'],
    'year': [2023, 2023, 2022, 2023, 2024, 2023],
    'revenue': [400, 300, 350, 320, 380, 420]
})

quarter_order = ['Q1', 'Q2', 'Q3', 'Q4']
df['quarter'] = pd.Categorical(df['quarter'], categories=quarter_order, ordered=True)

# 年、四半期の順でソート
df_sorted = df.sort_values(['year', 'quarter'])
print(df_sorted)

カスタム日付順序

import pandas as pd

df = pd.DataFrame({
    'event': ['開始', '中間', '終了', '準備', '開始', '中間'],
    'date': ['2024-01-15', '2024-01-20', '2024-01-25', '2024-01-10', '2024-02-15', '2024-02-20'],
    'participants': [50, 45, 30, 20, 55, 48]
})

# イベントの段階順序
event_order = ['準備', '開始', '中間', '終了']
df['event'] = pd.Categorical(df['event'], categories=event_order, ordered=True)

# 日付を datetime に変換
df['date'] = pd.to_datetime(df['date'])

df_sorted = df.sort_values(['date', 'event'])
print(df_sorted)

6. 文字列の任意順序並び替え

地域・エリア順での並び替え

import pandas as pd

df = pd.DataFrame({
    'region': ['関西', '関東', '九州', '北海道', '東北', '関東'],
    'city': ['大阪', '東京', '福岡', '札幌', '仙台', '横浜'],
    'population': [875, 1396, 157, 195, 109, 374]
})

# 地域の順序(北から南へ)
region_order = ['北海道', '東北', '関東', '関西', '九州']
df['region'] = pd.Categorical(df['region'], categories=region_order, ordered=True)

df_sorted = df.sort_values(['region', 'population'], ascending=[True, False])
print(df_sorted)

7. 数値の任意順序並び替え

特定の数値順序

import pandas as pd

df = pd.DataFrame({
    'room_number': [101, 205, 102, 301, 203, 105],
    'floor': [1, 2, 1, 3, 2, 1],
    'occupancy': ['空室', '満室', '清掃中', '満室', '空室', '満室']
})

# 部屋番号を特定の順序で並び替え(階数優先、その後部屋番号順)
def room_sort_key(room_num):
    floor = room_num // 100
    room = room_num % 100
    return (floor, room)

df['sort_key'] = df['room_number'].apply(room_sort_key)
df_sorted = df.sort_values('sort_key').drop('sort_key', axis=1)
print(df_sorted)

8. 多層的な任意順序並び替え

複数条件での複雑なソート

import pandas as pd

df = pd.DataFrame({
    'department': ['営業', '開発', '営業', '人事', '開発'],
    'position': ['課長', '主任', '部長', '課長', '部長'],
    'experience': [5, 3, 10, 7, 12],
    'performance': ['A', 'B', 'S', 'A', 'S']
})

# 複数の順序を定義
dept_order = ['人事', '営業', '開発']
pos_order = ['主任', '課長', '部長']
perf_order = ['S', 'A', 'B', 'C']

# Categorical に変換
df['department'] = pd.Categorical(df['department'], categories=dept_order, ordered=True)
df['position'] = pd.Categorical(df['position'], categories=pos_order, ordered=True)
df['performance'] = pd.Categorical(df['performance'], categories=perf_order, ordered=True)

# 複数列でソート(部署 → ポジション → パフォーマンス → 経験年数降順)
df_sorted = df.sort_values(['department', 'position', 'performance', 'experience'], 
                          ascending=[True, True, True, False])
print(df_sorted)

9. データフレーム全体の任意順序並び替え

行の完全カスタムソート

import pandas as pd

df = pd.DataFrame({
    'id': ['A001', 'B002', 'A003', 'C001', 'B001'],
    'category': ['TypeA', 'TypeB', 'TypeA', 'TypeC', 'TypeB'],
    'value': [100, 200, 150, 300, 250]
})

# IDの特定順序
custom_id_order = ['C001', 'A001', 'A003', 'B001', 'B002']

# reindex を使用して行を並び替え
df_custom_order = df.set_index('id').reindex(custom_id_order).reset_index()
print(df_custom_order)

10. 時系列データの任意順序並び替え

ビジネス日付順での並び替え

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'business_day': ['月末', '月初', '中旬', '月末', '月初'],
    'month': ['1月', '2月', '1月', '2月', '3月'],
    'amount': [1000, 1200, 800, 1100, 1300]
})

# 月の順序
month_order = ['1月', '2月', '3月', '4月', '5月', '6月', 
               '7月', '8月', '9月', '10月', '11月', '12月']

# ビジネス日の順序
business_day_order = ['月初', '中旬', '月末']

df['month'] = pd.Categorical(df['month'], categories=month_order, ordered=True)
df['business_day'] = pd.Categorical(df['business_day'], categories=business_day_order, ordered=True)

df_sorted = df.sort_values(['month', 'business_day'])
print(df_sorted)

11. パフォーマンス最適化

大規模データでの効率的な任意順序ソート

import pandas as pd
import numpy as np

# 大規模データのサンプル作成
np.random.seed(42)
df = pd.DataFrame({
    'category': np.random.choice(['A', 'B', 'C', 'D', 'E'], 10000),
    'subcategory': np.random.choice(['X', 'Y', 'Z'], 10000),
    'value': np.random.randint(1, 1000, 10000)
})

# 効率的な方法:辞書を使ったマッピング
category_map = {'E': 0, 'C': 1, 'A': 2, 'D': 3, 'B': 4}
subcategory_map = {'Z': 0, 'X': 1, 'Y': 2}

# 一度だけマッピングを実行
df['cat_rank'] = df['category'].map(category_map)
df['subcat_rank'] = df['subcategory'].map(subcategory_map)

df_sorted = df.sort_values(['cat_rank', 'subcat_rank', 'value'])
df_final = df_sorted.drop(['cat_rank', 'subcat_rank'], axis=1)

print(f"ソート完了: {len(df_final)}行")
print(df_final.head())

12. 実用的な応用例

売上レポートの任意順序ソート

import pandas as pd

df = pd.DataFrame({
    'product_category': ['家電', '衣料品', '食品', '家電', '書籍', '衣料品'],
    'priority_level': ['高', '中', '低', '中', '高', '低'],
    'region': ['東日本', '西日本', '東日本', '西日本', '東日本', '西日本'],
    'sales': [500000, 300000, 150000, 400000, 80000, 200000]
})

# ビジネス重要度に基づく順序
category_priority = ['家電', '衣料品', '書籍', '食品']
priority_level_order = ['高', '中', '低']
region_order = ['東日本', '西日本']

# Categorical変換
df['product_category'] = pd.Categorical(df['product_category'], 
                                       categories=category_priority, ordered=True)
df['priority_level'] = pd.Categorical(df['priority_level'], 
                                     categories=priority_level_order, ordered=True)
df['region'] = pd.Categorical(df['region'], categories=region_order, ordered=True)

# マルチレベルソート
df_report = df.sort_values(['product_category', 'priority_level', 'region', 'sales'], 
                          ascending=[True, True, True, False])

print("売上レポート(カスタム順序):")
print(df_report)

タスク管理システムの優先度ソート

import pandas as pd
from datetime import datetime, timedelta

# サンプルタスクデータ
df = pd.DataFrame({
    'task_id': ['T001', 'T002', 'T003', 'T004', 'T005'],
    'status': ['進行中', '完了', '未着手', '保留', '進行中'],
    'priority': ['高', '中', '緊急', '低', '高'],
    'deadline': ['2024-01-15', '2024-01-10', '2024-01-12', '2024-01-20', '2024-01-14'],
    'assignee': ['田中', '佐藤', '鈴木', '田中', '佐藤']
})

# タスク管理の優先順序
status_priority = {'緊急': 0, '未着手': 1, '進行中': 2, '保留': 3, '完了': 4}
priority_level = {'緊急': 0, '高': 1, '中': 2, '低': 3}

# 日付変換
df['deadline'] = pd.to_datetime(df['deadline'])

# 優先度計算
df['status_rank'] = df['status'].map(status_priority)
df['priority_rank'] = df['priority'].map(priority_level)

# 複合ソート(優先度 → ステータス → 期限)
df_tasks = df.sort_values(['priority_rank', 'status_rank', 'deadline'])
df_final = df_tasks.drop(['status_rank', 'priority_rank'], axis=1)

print("タスク優先度順:")
print(df_final)

13. エラーハンドリングと注意点

安全な任意順序ソート

import pandas as pd

def safe_custom_sort(df, column, custom_order, handle_missing='last'):
    """
    安全な任意順序ソート(存在しない値への対処)
    """
    df_copy = df.copy()
    
    # カスタムオーダーに存在しない値を処理
    unique_values = df_copy[column].unique()
    missing_values = [v for v in unique_values if v not in custom_order]
    
    if missing_values:
        print(f"警告: 以下の値がカスタムオーダーに含まれていません: {missing_values}")
        
        if handle_missing == 'last':
            extended_order = custom_order + missing_values
        elif handle_missing == 'first':
            extended_order = missing_values + custom_order
        else:  # handle_missing == 'ignore'
            extended_order = custom_order
    else:
        extended_order = custom_order
    
    # 順序マップ作成
    order_map = {value: i for i, value in enumerate(extended_order)}
    
    # ソート実行
    df_copy['sort_key'] = df_copy[column].map(order_map)
    df_sorted = df_copy.sort_values('sort_key').drop('sort_key', axis=1)
    
    return df_sorted

# 使用例
df = pd.DataFrame({
    'grade': ['A', 'B', 'C', 'A+', 'B-'],  # 'A+'と'B-'が予期しない値
    'student': ['田中', '佐藤', '鈴木', '高橋', '伊藤']
})

grade_order = ['A', 'B', 'C']
df_sorted = safe_custom_sort(df, 'grade', grade_order, handle_missing='first')
print(df_sorted)

まとめ

pandasでの任意順序による並び替えは、データ分析において非常に重要な機能です。主な手法として以下があります:

  1. map()とsort_values()の組み合わせ – シンプルで直感的
  2. Categoricalデータ型の活用 – pandas標準の効率的な方法
  3. 複数条件でのマルチレベルソート – 複雑なビジネスロジックに対応
  4. パフォーマンスを考慮した実装 – 大規模データでの最適化

用途に応じて適切な手法を選択し、エラーハンドリングも含めて堅牢な並び替え処理を実装することで、より価値のあるデータ分析が可能になります。特にビジネスデータを扱う際は、任意順序でのソートが必須スキルとなるため、これらの手法をマスターしておくことが重要です。

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座