6. การรวบรวมและจัดกลุ่มข้อมูล (Aggregation & Grouping)

การวิเคราะห์ข้อมูลในโลกจริงมักต้องการการสรุปผลและจัดกลุ่มข้อมูลเพื่อหาข้อมูลเชิงลึก (Insights) ซึ่งเปรียบเสมือนการใช้ Pivot Table ใน Excel หรือคำสั่ง GROUP BY ใน SQL แต่ด้วยความยืดหยุ่นและประสิทธิภาพที่สูงกว่า Pandas มีเครื่องมือที่ทรงพลังสำหรับการทำงานนี้

%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#fbf1c7','primaryTextColor':'#3c3836','primaryBorderColor':'#458588','lineColor':'#98971a','secondaryColor':'#d79921','tertiaryColor':'#cc241d','background':'#282828','mainBkg':'#fbf1c7','secondaryBkg':'#ebdbb2','textColor':'#3c3836','fontSize':'16px'}}}%%
graph LR
    A[ข้อมูลดิบ
Raw Data] --> B[จัดกลุ่ม
GroupBy] B --> C[รวบรวม
Aggregate] C --> D[ผลลัพธ์
Results] style A fill:#b8bb26,stroke:#3c3836,stroke-width:2px,color:#282828 style B fill:#fe8019,stroke:#3c3836,stroke-width:2px,color:#282828 style C fill:#d3869b,stroke:#3c3836,stroke-width:2px,color:#282828 style D fill:#83a598,stroke:#3c3836,stroke-width:2px,color:#282828

6.1 พื้นฐานการจัดกลุ่ม (Grouping Fundamentals)

การจัดกลุ่มข้อมูลเป็นกระบวนการที่แบ่งข้อมูลออกเป็นกลุ่มตามคุณสมบัติที่กำหนด แล้วทำการคำนวณในแต่ละกลุ่ม

6.1.1 หลักการทำงานของ GroupBy

GroupBy ใน Pandas ทำงานตามหลักการ Split-Apply-Combine:

  1. Split (แบ่ง): แบ่งข้อมูลออกเป็นกลุ่มตามค่าใน Column ที่กำหนด
  2. Apply (ประมวลผล): ใช้ฟังก์ชันคำนวณกับแต่ละกลุ่ม
  3. Combine (รวม): รวมผลลัพธ์จากทุกกลุ่มเป็น DataFrame ใหม่
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#fbf1c7','primaryTextColor':'#3c3836','primaryBorderColor':'#458588','lineColor':'#98971a','secondaryColor':'#d79921','tertiaryColor':'#cc241d','background':'#282828','mainBkg':'#fbf1c7','secondaryBkg':'#ebdbb2','textColor':'#3c3836','fontSize':'16px'}}}%%
flowchart TB
    subgraph Split["1. Split - แบ่งกลุ่ม"]
        A[DataFrame เดิม] --> B1[กลุ่ม A]
        A --> B2[กลุ่ม B]
        A --> B3[กลุ่ม C]
    end
    
    subgraph Apply["2. Apply - ประมวลผล"]
        B1 --> C1[คำนวณ sum/mean/count]
        B2 --> C2[คำนวณ sum/mean/count]
        B3 --> C3[คำนวณ sum/mean/count]
    end
    
    subgraph Combine["3. Combine - รวมผล"]
        C1 --> D[DataFrame ใหม่]
        C2 --> D
        C3 --> D
    end
    
    style A fill:#b8bb26,stroke:#3c3836,stroke-width:2px,color:#282828
    style B1 fill:#fe8019,stroke:#3c3836,stroke-width:2px,color:#282828
    style B2 fill:#fe8019,stroke:#3c3836,stroke-width:2px,color:#282828
    style B3 fill:#fe8019,stroke:#3c3836,stroke-width:2px,color:#282828
    style C1 fill:#d3869b,stroke:#3c3836,stroke-width:2px,color:#282828
    style C2 fill:#d3869b,stroke:#3c3836,stroke-width:2px,color:#282828
    style C3 fill:#d3869b,stroke:#3c3836,stroke-width:2px,color:#282828
    style D fill:#83a598,stroke:#3c3836,stroke-width:2px,color:#282828

6.1.2 การใช้งาน .groupby() เบื้องต้น

import pandas as pd
import numpy as np

def create_sales_data():
    """
    สร้างข้อมูลตัวอย่างยอดขาย
    
    Returns:
        DataFrame: ข้อมูลยอดขายพนักงานแบ่งตามภูมิภาคและผลิตภัณฑ์
    """
    data = {
        'พนักงาน': ['สมชาย', 'สมหญิง', 'วิชัย', 'อรุณ', 'สมชาย', 
                     'สมหญิง', 'วิชัย', 'อรุณ', 'สมชาย', 'สมหญิง'],
        'ภูมิภาค': ['เหนือ', 'เหนือ', 'กลาง', 'กลาง', 'เหนือ',
                    'เหนือ', 'กลาง', 'กลาง', 'ใต้', 'ใต้'],
        'ผลิตภัณฑ์': ['A', 'B', 'A', 'B', 'C', 
                      'A', 'B', 'C', 'A', 'B'],
        'ยอดขาย': [100, 150, 200, 120, 180,
                   160, 140, 190, 110, 170],
        'จำนวน': [5, 8, 10, 6, 9,
                  8, 7, 10, 6, 9]
    }
    return pd.DataFrame(data)

# สร้างข้อมูลตัวอย่าง
df = create_sales_data()
print("ข้อมูลดิบ (Raw Data):")
print(df)
print("\n" + "="*60 + "\n")

# ตัวอย่างที่ 1: จัดกลุ่มตามภูมิภาคและหายอดขายรวม
grouped_region = df.groupby('ภูมิภาค')['ยอดขาย'].sum()
print("ยอดขายรวมแต่ละภูมิภาค:")
print(grouped_region)
print("\n" + "="*60 + "\n")

# ตัวอย่างที่ 2: จัดกลุ่มหลาย Column
grouped_multi = df.groupby(['ภูมิภาค', 'ผลิตภัณฑ์'])['ยอดขาย'].sum()
print("ยอดขายแยกตามภูมิภาคและผลิตภัณฑ์:")
print(grouped_multi)

ผลลัพธ์:

ข้อมูลดิบ (Raw Data):
    พนักงาน ภูมิภาค ผลิตภัณฑ์  ยอดขาย  จำนวน
0    สมชาย   เหนือ        A    100      5
1   สมหญิง   เหนือ        B    150      8
...

ยอดขายรวมแต่ละภูมิภาค:
ภูมิภาค
กลาง    650
ใต้     280
เหนือ    590
Name: ยอดขาย, dtype: int64

6.1.3 การเข้าถึงข้อมูลหลัง GroupBy

เมื่อใช้ .groupby() จะได้ GroupBy Object ซึ่งยังไม่ได้คำนวณจริง (Lazy Evaluation) จนกว่าจะเรียกใช้ฟังก์ชันรวบรวม

def explore_groupby_object():
    """
    สำรวจคุณสมบัติของ GroupBy Object
    """
    df = create_sales_data()
    
    # สร้าง GroupBy Object
    grouped = df.groupby('ภูมิภาค')
    
    # 1. ดูจำนวนกลุ่ม
    print(f"จำนวนกลุ่มทั้งหมด: {grouped.ngroups}")
    
    # 2. ดูชื่อกลุ่มและขนาดของแต่ละกลุ่ม
    print("\nขนาดแต่ละกลุ่ม:")
    print(grouped.size())
    
    # 3. เข้าถึงกลุ่มเดียว
    print("\nข้อมูลในกลุ่ม 'เหนือ':")
    print(grouped.get_group('เหนือ'))
    
    # 4. วนลูปผ่านแต่ละกลุ่ม
    print("\nวนลูปแต่ละกลุ่ม:")
    for name, group in grouped:
        print(f"\n{name}:")
        print(group.head(2))

# เรียกใช้ฟังก์ชัน
explore_groupby_object()

6.1.4 การจัดกลุ่มแบบพิเศษ

การจัดกลุ่มด้วยฟังก์ชัน:

def groupby_with_function():
    """
    ตัวอย่างการใช้ฟังก์ชันในการจัดกลุ่ม
    """
    df = create_sales_data()
    
    # จัดกลุ่มตามความยาวของชื่อพนักงาน
    def name_length_category(name):
        """แบ่งกลุ่มตามความยาวชื่อ"""
        if len(name) <= 5:
            return 'สั้น'
        else:
            return 'ยาว'
    
    # ใช้ฟังก์ชันกับ Column
    grouped = df.groupby(df['พนักงาน'].apply(name_length_category))['ยอดขาย'].sum()
    print("ยอดขายแบ่งตามความยาวชื่อ:")
    print(grouped)

groupby_with_function()

6.2 การหาค่าสถิติในกลุ่ม (Statistical Aggregation)

หลังจากจัดกลุ่มข้อมูลแล้ว ขั้นตอนต่อไปคือการคำนวณค่าสถิติต่างๆ ภายในแต่ละกลุ่ม

6.2.1 ฟังก์ชันรวบรวมพื้นฐาน (Basic Aggregation Functions)

Pandas มีฟังก์ชันรวบรวมที่ใช้งานบ่อย:

ฟังก์ชัน ความหมาย ตัวอย่างการใช้
.sum() ผลรวม grouped.sum()
.mean() ค่าเฉลี่ย grouped.mean()
.median() มัธยฐาน grouped.median()
.min() ค่าต่ำสุด grouped.min()
.max() ค่าสูงสุด grouped.max()
.count() นับจำนวน grouped.count()
.std() ส่วนเบี่ยงเบนมาตรฐาน grouped.std()
.var() ความแปรปรวน grouped.var()
def basic_aggregations():
    """
    ตัวอย่างการใช้ฟังก์ชันรวบรวมพื้นฐาน
    """
    df = create_sales_data()
    grouped = df.groupby('ภูมิภาค')['ยอดขาย']
    
    print("=== สถิติพื้นฐานแต่ละภูมิภาค ===\n")
    
    # ผลรวม
    print("1. ยอดขายรวม (Sum):")
    print(grouped.sum())
    print()
    
    # ค่าเฉลี่ย
    print("2. ยอดขายเฉลี่ย (Mean):")
    print(grouped.mean().round(2))
    print()
    
    # นับจำนวน
    print("3. จำนวนรายการ (Count):")
    print(grouped.count())
    print()
    
    # ค่าสูงสุดและต่ำสุด
    print("4. ยอดขายสูงสุด-ต่ำสุด:")
    print(pd.DataFrame({
        'สูงสุด': grouped.max(),
        'ต่ำสุด': grouped.min()
    }))

basic_aggregations()

6.2.2 สมการทางคณิตศาสตร์

ค่าเฉลี่ย (Mean):

x ¯ = i=1 n x i n

โดยที่:

ส่วนเบี่ยงเบนมาตรฐาน (Standard Deviation):

σ = i=1 n ( x i - x ¯ ) 2 n - 1

โดยที่:

6.2.3 การใช้หลายฟังก์ชันพร้อมกัน

def multiple_aggregations():
    """
    ใช้ฟังก์ชันรวบรวมหลายตัวพร้อมกัน
    """
    df = create_sales_data()
    
    # วิธีที่ 1: ใช้ .agg() กับ list ของฟังก์ชัน
    result1 = df.groupby('ภูมิภาค')['ยอดขาย'].agg(['sum', 'mean', 'count', 'std'])
    result1.columns = ['ผลรวม', 'ค่าเฉลี่ย', 'จำนวน', 'ส่วนเบี่ยงเบน']
    
    print("=== สถิติแบบรวม ===")
    print(result1.round(2))
    print()
    
    # วิธีที่ 2: คำนวณแยก Column
    result2 = df.groupby('ภูมิภาค').agg({
        'ยอดขาย': ['sum', 'mean'],
        'จำนวน': ['sum', 'mean']
    })
    
    print("=== สถิติแยกตาม Column ===")
    print(result2.round(2))

multiple_aggregations()

6.2.4 การใช้ฟังก์ชัน Custom

def custom_aggregation():
    """
    สร้างฟังก์ชันรวบรวมแบบกำหนดเอง
    """
    df = create_sales_data()
    
    # ฟังก์ชัน custom: หาช่วงห่าง (range)
    def value_range(x):
        """คำนวณช่วงห่างระหว่างค่าสูงสุดและต่ำสุด"""
        return x.max() - x.min()
    
    # ฟังก์ชัน custom: หา coefficient of variation
    def coef_variation(x):
        """คำนวณค่า CV (ส่วนเบี่ยงเบน / ค่าเฉลี่ย * 100)"""
        return (x.std() / x.mean()) * 100 if x.mean() != 0 else 0
    
    result = df.groupby('ภูมิภาค')['ยอดขาย'].agg([
        ('ค่าเฉลี่ย', 'mean'),
        ('ช่วงห่าง', value_range),
        ('CV%', coef_variation)
    ])
    
    print("=== สถิติแบบ Custom ===")
    print(result.round(2))

custom_aggregation()

6.3 การใช้ .agg() ขั้นสูง (Advanced Aggregation)

.agg() (หรือ .aggregate()) เป็นเครื่องมือที่ทรงพลังสำหรับการคำนวณหลายค่าพร้อมกัน โดยสามารถระบุฟังก์ชันต่างกันสำหรับแต่ละ Column

6.3.1 รูปแบบการใช้ .agg() แบบต่างๆ

%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#fbf1c7','primaryTextColor':'#3c3836','primaryBorderColor':'#458588','lineColor':'#98971a','secondaryColor':'#d79921','tertiaryColor':'#cc241d','background':'#282828','mainBkg':'#fbf1c7','secondaryBkg':'#ebdbb2','textColor':'#3c3836','fontSize':'14px'}}}%%
graph TB
    A[.agg Methods
วิธีใช้ .agg] --> B[String Names
ชื่อฟังก์ชัน] A --> C[Function Objects
ตัวฟังก์ชัน] A --> D[Lambda
ฟังก์ชัน Lambda] A --> E[Dictionary
แบบ Dict] A --> F[List of Tuples
แบบ Tuple] B --> B1["'sum', 'mean'"] C --> C1["np.sum, np.mean"] D --> D1["lambda x: x.max()"] E --> E1["{'col1': 'sum',
'col2': 'mean'}"] F --> F1["[('total', 'sum'),
('avg', 'mean')]"] style A fill:#fe8019,stroke:#3c3836,stroke-width:3px,color:#282828 style B fill:#b8bb26,stroke:#3c3836,stroke-width:2px,color:#282828 style C fill:#b8bb26,stroke:#3c3836,stroke-width:2px,color:#282828 style D fill:#b8bb26,stroke:#3c3836,stroke-width:2px,color:#282828 style E fill:#b8bb26,stroke:#3c3836,stroke-width:2px,color:#282828 style F fill:#b8bb26,stroke:#3c3836,stroke-width:2px,color:#282828
def agg_various_methods():
    """
    ตัวอย่างการใช้ .agg() แบบต่างๆ
    """
    df = create_sales_data()
    grouped = df.groupby('ภูมิภาค')
    
    # วิธีที่ 1: ใช้ string ของชื่อฟังก์ชัน
    print("1. ใช้ชื่อฟังก์ชันแบบ string:")
    result1 = grouped['ยอดขาย'].agg(['sum', 'mean', 'max'])
    print(result1)
    print("\n" + "="*60 + "\n")
    
    # วิธีที่ 2: ใช้ dictionary กำหนดแยกตาม Column
    print("2. ใช้ dictionary กำหนดฟังก์ชันแยก Column:")
    result2 = grouped.agg({
        'ยอดขาย': ['sum', 'mean'],
        'จำนวน': ['sum', 'max'],
        'พนักงาน': 'count'  # นับจำนวนรายการ
    })
    print(result2)
    print("\n" + "="*60 + "\n")
    
    # วิธีที่ 3: ใช้ tuple เพื่อตั้งชื่อ Column ใหม่
    print("3. ใช้ tuple ตั้งชื่อ Column:")
    result3 = grouped['ยอดขาย'].agg([
        ('ยอดรวม', 'sum'),
        ('ยอดเฉลี่ย', 'mean'),
        ('ยอดสูงสุด', 'max')
    ])
    print(result3)
    print("\n" + "="*60 + "\n")
    
    # วิธีที่ 4: ผสม built-in และ custom functions
    print("4. ผสมฟังก์ชัน built-in และ custom:")
    result4 = grouped['ยอดขาย'].agg([
        'sum',
        'mean',
        ('range', lambda x: x.max() - x.min()),
        ('top_3_avg', lambda x: x.nlargest(3).mean())
    ])
    print(result4.round(2))

agg_various_methods()

6.3.2 การใช้ NumPy Functions

def agg_with_numpy():
    """
    ใช้ฟังก์ชันจาก NumPy ใน .agg()
    """
    df = create_sales_data()
    grouped = df.groupby('ภูมิภาค')['ยอดขาย']
    
    # ใช้ฟังก์ชันจาก NumPy
    result = grouped.agg([
        ('Mean', np.mean),
        ('Median', np.median),
        ('Std', np.std),
        ('Q1', lambda x: np.percentile(x, 25)),
        ('Q3', lambda x: np.percentile(x, 75))
    ])
    
    print("=== สถิติเชิงลึกด้วย NumPy ===")
    print(result.round(2))
    
    # คำนวณ IQR (Interquartile Range)
    result['IQR'] = result['Q3'] - result['Q1']
    print("\nพร้อม IQR:")
    print(result.round(2))

agg_with_numpy()

6.3.3 การใช้ Named Aggregation (Pandas 0.25+)

Named Aggregation ช่วยให้เขียน code ที่อ่านง่ายขึ้น โดยใช้ pd.NamedAgg:

def named_aggregation():
    """
    ใช้ Named Aggregation เพื่อความชัดเจน
    """
    df = create_sales_data()
    
    result = df.groupby('ภูมิภาค').agg(
        ยอดขายรวม=pd.NamedAgg(column='ยอดขาย', aggfunc='sum'),
        ยอดขายเฉลี่=pd.NamedAgg(column='ยอดขาย', aggfunc='mean'),
        จำนวนสนคารวม=pd.NamedAgg(column='จำนวน', aggfunc='sum'),
        จำนวนรายการ=pd.NamedAgg(column='พนักงาน', aggfunc='count')
    )
    
    print("=== Named Aggregation ===")
    print(result.round(2))
    
    # คำนวณ metric เพิ่มเติม
    result['ยอดเฉลี่ยต่อสินค้า'] = result['ยอดขายรวม'] / result['จำนวนสินค้ารวม']
    print("\nพร้อมการคำนวณเพิ่มเติม:")
    print(result.round(2))

named_aggregation()

6.3.4 การใช้ .transform() vs .agg()

ความแตกต่างสำคัญ:

คุณสมบัติ .agg() .transform()
Output Shape รวมกลุ่ม (ลดขนาด) ขนาดเท่าเดิม
Use Case สรุปผลแต่ละกลุ่ม เติมค่ากลับในข้อมูลเดิม
ตัวอย่าง หายอดขายรวมแต่ละภูมิภาค เติมค่าเฉลี่ยภูมิภาคในทุก row
def agg_vs_transform():
    """
    เปรียบเทียบ .agg() และ .transform()
    """
    df = create_sales_data()
    
    # ใช้ .agg() - ลดขนาดเหลือแค่กลุ่ม
    agg_result = df.groupby('ภูมิภาค')['ยอดขาย'].agg(['mean', 'sum'])
    print("=== ผลลัพธ์จาก .agg() ===")
    print(agg_result)
    print(f"Shape: {agg_result.shape}")
    print("\n" + "="*60 + "\n")
    
    # ใช้ .transform() - ขนาดเท่าเดิม
    df['ยอดเฉลี่ยภูมิภาค'] = df.groupby('ภูมิภาค')['ยอดขาย'].transform('mean')
    df['ส่วนต่างจากเฉลี่ย'] = df['ยอดขาย'] - df['ยอดเฉลี่ยภูมิภาค']
    
    print("=== ผลลัพธ์จาก .transform() ===")
    print(df[['พนักงาน', 'ภูมิภาค', 'ยอดขาย', 'ยอดเฉลี่ยภูมิภาค', 'ส่วนต่างจากเฉลี่ย']])
    print(f"Shape: {df.shape}")

agg_vs_transform()

การใช้ .transform() กับ Lambda:

z i = x i - x ¯ group σ group

โดยที่:

def standardize_by_group():
    """
    ทำ Standardization (Z-score) ภายในแต่ละกลุ่ม
    """
    df = create_sales_data()
    
    # คำนวณ Z-score สำหรับแต่ละภูมิภาค
    df['z_score'] = df.groupby('ภูมิภาค')['ยอดขาย'].transform(
        lambda x: (x - x.mean()) / x.std()
    )
    
    print("=== Z-score ภายในแต่ละภูมิภาค ===")
    print(df[['พนักงาน', 'ภูมิภาค', 'ยอดขาย', 'z_score']].round(3))

standardize_by_group()

6.4 Pivot Tables และ Cross Tabulation

Pivot Table เป็นเครื่องมือสำคัญในการสรุปและจัดกลุ่มข้อมูลแบบ 2 มิติ เหมาะสำหรับการวิเคราะห์เชิงลึกและสร้างรายงาน

6.4.1 การสร้าง Pivot Table ด้วย .pivot_table()

def create_pivot_table_basic():
    """
    สร้าง Pivot Table พื้นฐาน
    """
    # สร้างข้อมูลตัวอย่างที่ซับซ้อนขึ้น
    data = {
        'วันที่': pd.date_range('2024-01-01', periods=20, freq='D'),
        'ภูมิภาค': ['เหนือ', 'กลาง', 'ใต้', 'อีสาน'] * 5,
        'ผลิตภัณฑ์': ['A', 'B', 'C', 'A', 'B'] * 4,
        'ยอดขาย': np.random.randint(100, 500, 20),
        'กำไร': np.random.randint(20, 100, 20)
    }
    df = pd.DataFrame(data)
    
    # Pivot Table พื้นฐาน
    pivot1 = pd.pivot_table(
        df,
        values='ยอดขาย',              # Column ที่ต้องการสรุป
        index='ภูมิภาค',               # แถว
        columns='ผลิตภัณฑ์',           # คอลัมน์
        aggfunc='sum',                 # ฟังก์ชันรวบรวม
        fill_value=0                   # แทนที่ NaN ด้วย 0
    )
    
    print("=== Pivot Table: ยอดขายแต่ละภูมิภาคตามผลิตภัณฑ์ ===")
    print(pivot1)
    print("\n" + "="*60 + "\n")
    
    # เพิ่ม margins (รวมทั้งหมด)
    pivot2 = pd.pivot_table(
        df,
        values='ยอดขาย',
        index='ภูมิภาค',
        columns='ผลิตภัณฑ์',
        aggfunc='sum',
        fill_value=0,
        margins=True,                  # เพิ่มแถว/คอลัมน์รวม
        margins_name='รวม'
    )
    
    print("=== Pivot Table พร้อม Margins ===")
    print(pivot2)

create_pivot_table_basic()

6.4.2 Pivot Table แบบหลายค่ารวบรวม

def pivot_table_multiple_agg():
    """
    Pivot Table ที่ใช้หลายฟังก์ชันพร้อมกัน
    """
    data = {
        'ภูมิภาค': ['เหนือ', 'กลาง', 'ใต้', 'อีสาน'] * 5,
        'ผลิตภัณฑ์': ['A', 'B', 'C'] * 6 + ['A', 'B'],
        'ยอดขาย': np.random.randint(100, 500, 20),
        'จำนวน': np.random.randint(5, 20, 20)
    }
    df = pd.DataFrame(data)
    
    # Pivot Table หลายค่า
    pivot = pd.pivot_table(
        df,
        values=['ยอดขาย', 'จำนวน'],    # หลาย values
        index='ภูมิภาค',
        columns='ผลิตภัณฑ์',
        aggfunc={
            'ยอดขาย': ['sum', 'mean'],  # หลายฟังก์ชันต่อ column
            'จำนวน': 'sum'
        },
        fill_value=0
    )
    
    print("=== Pivot Table หลายค่าและหลายฟังก์ชัน ===")
    print(pivot.round(2))

pivot_table_multiple_agg()

6.4.3 การใช้ pd.crosstab()

Cross Tabulation ใช้สำหรับนับความถี่ระหว่าง 2 ตัวแปร:

def cross_tabulation_example():
    """
    ตัวอย่างการใช้ pd.crosstab()
    """
    # สร้างข้อมูลลูกค้า
    data = {
        'เพศ': ['ชาย', 'หญิง', 'ชาย', 'หญิง'] * 10,
        'ช่วงอายุ': ['18-25', '26-35', '36-45', '46+'] * 10,
        'ผลิตภัณฑ์ที่ซื้อ': np.random.choice(['A', 'B', 'C'], 40)
    }
    df = pd.DataFrame(data)
    
    # Cross-tab พื้นฐาน: นับความถี่
    crosstab1 = pd.crosstab(
        df['เพศ'],
        df['ช่วงอายุ'],
        margins=True,
        margins_name='รวม'
    )
    
    print("=== Cross Tabulation: จำนวนลูกค้าแต่ละกลุ่ม ===")
    print(crosstab1)
    print("\n" + "="*60 + "\n")
    
    # Cross-tab แบบเปอร์เซ็นต์
    crosstab2 = pd.crosstab(
        df['เพศ'],
        df['ช่วงอายุ'],
        normalize='index'  # normalize ตามแถว (0-1)
    ) * 100  # แปลงเป็น %
    
    print("=== Cross Tabulation: เปอร์เซ็นต์ ===")
    print(crosstab2.round(2))
    print("\n" + "="*60 + "\n")
    
    # Cross-tab 3 มิติ
    crosstab3 = pd.crosstab(
        [df['เพศ'], df['ช่วงอายุ']],  # หลาย index
        df['ผลิตภัณฑ์ที่ซื้อ']
    )
    
    print("=== Cross Tabulation: 3 มิติ ===")
    print(crosstab3)

cross_tabulation_example()

6.4.4 Pivot Table vs GroupBy: เมื่อไหร่ควรใช้อะไร

%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#fbf1c7','primaryTextColor':'#3c3836','primaryBorderColor':'#458588','lineColor':'#98971a','secondaryColor':'#d79921','tertiaryColor':'#cc241d','background':'#282828','mainBkg':'#fbf1c7','secondaryBkg':'#ebdbb2','textColor':'#3c3836','fontSize':'14px'}}}%%
graph TB
    A{ต้องการอะไร?
What do you need?} A -->|รูปแบบตาราง 2 มิติ
2D Table Format| B[Pivot Table] A -->|การคำนวณที่ซับซ้อน
Complex Calculations| C[GroupBy + Agg] A -->|นับความถี่
Frequency Count| D[Crosstab] B --> B1[ใช้เมื่อ:
- ต้องการรูปแบบ matrix
- มี 2+ มิติในการวิเคราะห์
- ต้องการ fill_value] C --> C1[ใช้เมื่อ:
- ต้องการควบคุมการคำนวณ
- ต้องการ transform
- การคำนวณหลายขั้นตอน] D --> D1[ใช้เมื่อ:
- นับความถี่เท่านั้น
- ข้อมูล categorical
- ต้องการ normalize] style A fill:#fe8019,stroke:#3c3836,stroke-width:3px,color:#282828 style B fill:#b8bb26,stroke:#3c3836,stroke-width:2px,color:#282828 style C fill:#83a598,stroke:#3c3836,stroke-width:2px,color:#282828 style D fill:#d3869b,stroke:#3c3836,stroke-width:2px,color:#282828 style B1 fill:#ebdbb2,stroke:#3c3836,stroke-width:1px,color:#282828 style C1 fill:#ebdbb2,stroke:#3c3836,stroke-width:1px,color:#282828 style D1 fill:#ebdbb2,stroke:#3c3836,stroke-width:1px,color:#282828
เกณฑ์การเลือก GroupBy + Agg Pivot Table Crosstab
รูปแบบผลลัพธ์ Series/DataFrame DataFrame (2D) DataFrame (2D)
ความยืดหยุ่น สูงมาก ปานกลาง น้อย
ความเหมาะสม การคำนวณซับซ้อน รายงานสรุป นับความถี่
Performance เร็ว ปานกลาง เร็ว
การใช้งาน ทุกประเภท ข้อมูลที่ต้องการแกน 2 มิติ Categorical data
def comparison_groupby_pivot():
    """
    เปรียบเทียบผลลัพธ์ระหว่าง GroupBy และ Pivot Table
    """
    df = create_sales_data()
    
    # วิธี GroupBy
    print("=== ใช้ GroupBy ===")
    groupby_result = df.groupby(['ภูมิภาค', 'ผลิตภัณฑ์'])['ยอดขาย'].sum()
    print(groupby_result)
    print("\n" + "="*60 + "\n")
    
    # วิธี Pivot Table (ให้ผลลัพธ์เหมือนกัน แต่รูปแบบต่าง)
    print("=== ใช้ Pivot Table ===")
    pivot_result = pd.pivot_table(
        df,
        values='ยอดขาย',
        index='ภูมิภาค',
        columns='ผลิตภัณฑ์',
        aggfunc='sum',
        fill_value=0
    )
    print(pivot_result)
    print("\n" + "="*60 + "\n")
    
    # แปลง Pivot กลับเป็น Long Format (Unpivot)
    print("=== Unpivot (melt) กลับ ===")
    melted = pivot_result.reset_index().melt(
        id_vars='ภูมิภาค',
        var_name='ผลิตภัณฑ์',
        value_name='ยอดขาย'
    )
    print(melted)

comparison_groupby_pivot()

6.4.5 ตัวอย่างการใช้งานจริง: สร้างรายงานยอดขาย

def sales_report_example():
    """
    ตัวอย่างการสร้างรายงานยอดขายแบบครบวงจร
    """
    # สร้างข้อมูลยอดขายรายเดือน
    np.random.seed(42)
    dates = pd.date_range('2024-01-01', periods=365, freq='D')
    
    data = {
        'วันที่': np.random.choice(dates, 1000),
        'ภูมิภาค': np.random.choice(['เหนือ', 'กลาง', 'ใต้', 'อีสาน'], 1000),
        'ผลิตภัณฑ์': np.random.choice(['A', 'B', 'C', 'D'], 1000),
        'ยอดขาย': np.random.randint(100, 1000, 1000),
        'ต้นทุน': np.random.randint(50, 500, 1000)
    }
    df = pd.DataFrame(data)
    df['กำไร'] = df['ยอดขาย'] - df['ต้นทุน']
    df['เดือน'] = df['วันที่'].dt.to_period('M')
    
    # รายงานที่ 1: สรุปรายเดือนแต่ละภูมิภาค
    report1 = pd.pivot_table(
        df,
        values=['ยอดขาย', 'กำไร'],
        index='เดือน',
        columns='ภูมิภาค',
        aggfunc='sum',
        margins=True,
        margins_name='รวมทั้งหมด'
    )
    
    print("=== รายงานยอดขายรายเดือนแต่ละภูมิภาค ===")
    print(report1.head(10))
    print("\n" + "="*60 + "\n")
    
    # รายงานที่ 2: Top Products
    report2 = df.groupby('ผลิตภัณฑ์').agg({
        'ยอดขาย': ['sum', 'mean', 'count'],
        'กำไร': 'sum'
    }).round(2)
    report2.columns = ['ยอดรวม', 'ยอดเฉลี่ย', 'จำนวนรายการ', 'กำไรรวม']
    report2 = report2.sort_values('ยอดรวม', ascending=False)
    report2['% กำไร'] = (report2['กำไรรวม'] / report2['ยอดรวม'] * 100).round(2)
    
    print("=== รายงานผลิตภัณฑ์ขายดี ===")
    print(report2)

sales_report_example()

สรุป (Summary)

การรวบรวมและจัดกลุ่มข้อมูลเป็นทักษะสำคัญที่สุดในการวิเคราะห์ข้อมูลด้วย Pandas สามารถสรุปประเด็นสำคัญได้ดังนี้:

จุดสำคัญที่ต้องจำ:

  1. GroupBy คือหัวใจหลัก - ใช้หลัก Split-Apply-Combine ในการจัดกลุ่มและคำนวณ
  2. เลือกฟังก์ชันให้เหมาะสม - ใช้ .agg() สำหรับการคำนวณหลายค่า, .transform() สำหรับเติมค่ากลับ
  3. Pivot Table สำหรับรายงาน - เหมาะกับการสรุปข้อมูลแบบ 2 มิติและสร้างรายงานที่อ่านง่าย
  4. Crosstab สำหรับนับความถี่ - ใช้กับข้อมูล categorical เพื่อดูความสัมพันธ์

Best Practices:

เทคนิคขั้นสูง:

%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#fbf1c7','primaryTextColor':'#3c3836','primaryBorderColor':'#458588','lineColor':'#98971a','secondaryColor':'#d79921','tertiaryColor':'#cc241d','background':'#282828','mainBkg':'#fbf1c7','secondaryBkg':'#ebdbb2','textColor':'#3c3836','fontSize':'16px'}}}%%
flowchart LR
    subgraph Input["ข้อมูลเริ่มต้น
Input Data"] A[Raw DataFrame] end subgraph Process["ประมวลผล
Processing"] B[GroupBy] C[Aggregation] D[Transform] E[Pivot] end subgraph Output["ผลลัพธ์
Output"] F[Summary Stats
สถิติสรุป] G[Reports
รายงาน] H[Enriched Data
ข้อมูลเสริม] end A --> B B --> C B --> D B --> E C --> F D --> H E --> G style A fill:#b8bb26,stroke:#3c3836,stroke-width:2px,color:#282828 style B fill:#fe8019,stroke:#3c3836,stroke-width:2px,color:#282828 style C fill:#d3869b,stroke:#3c3836,stroke-width:2px,color:#282828 style D fill:#d3869b,stroke:#3c3836,stroke-width:2px,color:#282828 style E fill:#d3869b,stroke:#3c3836,stroke-width:2px,color:#282828 style F fill:#83a598,stroke:#3c3836,stroke-width:2px,color:#282828 style G fill:#83a598,stroke:#3c3836,stroke-width:2px,color:#282828 style H fill:#83a598,stroke:#3c3836,stroke-width:2px,color:#282828

เอกสารอ้างอิง (References)

เอกสารทางการ (Official Documentation):

  1. Pandas Official Documentation - Group By

  2. Pandas API Reference - pandas.DataFrame.groupby

  3. Pandas Official Documentation - Pivot Tables

  1. Python for Data Analysis โดย Wes McKinney (ผู้สร้าง Pandas)

  2. Pandas Cookbook โดย Theodore Petrou

บทความและ Tutorial:

  1. Real Python - Pandas GroupBy Tutorial

  2. Towards Data Science - Advanced Pandas Aggregation

เครื่องมือเสริม:

  1. NumPy Documentation

  2. Matplotlib/Seaborn Documentation


หมายเหตุ: เอกสารนี้เป็นส่วนหนึ่งของคู่มือ Pandas ฉบับสมบูรณ์ สามารถนำไปประยุกต์ใช้กับข้อมูลจริงได้ทันที ตัวอย่าง code ทั้งหมดทดสอบแล้วกับ Pandas version 2.0+