Pandas & NumPy Academy · บทเรียน

apply() กับ GroupBy

ส่งฟังก์ชันที่ทำงานกับหลายแถวให้ groupby().apply() เพื่อคำนวณสรุประดับกลุ่มที่ซับซ้อน ซึ่ง agg() ไม่สามารถแสดงได้

บทเรียน 2 จาก 413 ขั้นตอน

apply() กับ GroupBy เป็นบทเรียน Pandas & NumPy Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Pandas & NumPy Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Pandas & NumPy Academy มีบทเรียนทั้งหมด 4 บทเรียน

เหตุใด GroupBy จึงต้องใช้ apply()

เมธอด agg() ในตัวรองรับการรวมข้อมูลแบบง่าย เช่น sum, mean และ count โดยให้ผลลัพธ์เป็นค่าหนึ่งค่าต่อกลุ่ม แต่การคำนวณบางอย่างในระดับกลุ่มจำเป็นต้องพิจารณา DataFrame ย่อยทั้งหมดของกลุ่ม ไม่ใช่เพียงคอลัมน์เดียว groupby().apply(func) จะส่ง DataFrame ทั้งกลุ่มไปยังฟังก์ชันของคุณและรวบรวมผลลัพธ์ ทำให้สามารถสร้างสรุปข้อมูลที่ซับซ้อน ซึ่ง agg() ไม่สามารถแสดงได้

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'region': ['North', 'North', 'South', 'South', 'North'],
    'product': ['A', 'B', 'A', 'A', 'C'],
    'revenue': [100, 250, 180, 90, 300]
})
print(df)

การคืนค่าเดียวต่อกลุ่ม

เมื่อฟังก์ชันที่ส่งให้ groupby().apply() คืนค่าหนึ่งค่า ผลลัพธ์จะเป็น Series ที่มีดัชนีเป็นคีย์ของกลุ่ม ซึ่งเหมือนกับผลลัพธ์จาก agg() รูปแบบนี้มีประโยชน์เมื่อค่าดังกล่าวต้องอาศัยตรรกะจากหลายคอลัมน์ เช่น การคำนวณอัตราส่วนรายได้ของสินค้าที่ทำรายได้สูงสุดต่อรายได้รวมของกลุ่ม ซึ่งไม่สามารถระบุด้วยข้อกำหนดคอลัมน์เดียวใน agg() ได้

def top_product_share(group):
    top = group['revenue'].max()
    total = group['revenue'].sum()
    return top / total

share = df.groupby('region').apply(top_product_share)
print(share)

การคืนค่า Series ต่อกลุ่ม

เมื่อฟังก์ชันคืนค่าเป็น pd.Series ผลลัพธ์จะมี MultiIndex โดยระดับนอกสุดคือคีย์ของกลุ่ม และระดับในคือดัชนีของ Series วิธีนี้เหมาะสำหรับการคำนวณสถิติหลายรายการต่อกลุ่มด้วยการเรียกใช้ apply เพียงครั้งเดียว ทำให้ได้ตารางสรุปที่แต่ละกลุ่มมีหลายแถวของตัวชี้วัด

def group_stats(group):
    return pd.Series({
        'total': group['revenue'].sum(),
        'top_product': group.loc[group['revenue'].idxmax(), 'product'],
        'n_products': group['product'].nunique()
    })

result = df.groupby('region').apply(group_stats)
print(result)

การคืนค่า DataFrame ต่อกลุ่ม

เมื่อฟังก์ชันคืนค่าเป็น pd.DataFrame groupby().apply() จะนำ DataFrame ย่อยทั้งหมดมาต่อกันในแนวตั้ง นี่เป็นรูปแบบที่ทรงพลังที่สุด เพราะช่วยให้คุณกรองหรือแปลงแถวภายในแต่ละกลุ่ม แล้วคืนค่าชุดข้อมูลย่อยที่แก้ไขแล้วได้ ตัวอย่างเช่น เก็บไว้เฉพาะสินค้า 2 อันดับแรกตามรายได้ในแต่ละภูมิภาค

def top2_per_region(group):
    return group.nlargest(2, 'revenue')

top2 = df.groupby('region').apply(top2_per_region)
print(top2.reset_index(drop=True))

การคำนวณค่า Z ภายในกลุ่ม

การใช้งานทั่วไปของ groupby().apply() คือการคำนวณค่ามาตรฐานภายในกลุ่ม แทนที่จะปรับรายได้ให้เป็นมาตรฐานเมื่อเทียบกับคำสั่งซื้อทั้งหมด ซึ่งจะทำให้ข้อมูลจากหลายภูมิภาคปะปนกัน ให้คำนวณค่า Z ของรายได้ภายในแต่ละภูมิภาค ค่า Z เท่ากับ 2 ในภาคเหนือหมายถึง “สูงกว่าค่าเฉลี่ยของภาคเหนือ 2 ส่วนเบี่ยงเบนมาตรฐาน” ซึ่งเป็นเกณฑ์เปรียบเทียบที่มีความหมายมากกว่าการสูงกว่าค่าเฉลี่ยรวม 2 ส่วนเบี่ยงเบนมาตรฐาน

def within_group_zscore(group):
    mean = group['revenue'].mean()
    std = group['revenue'].std()
    group = group.copy()
    group['revenue_z'] = (group['revenue'] - mean) / std
    return group

df_z = df.groupby('region').apply(within_group_zscore).reset_index(drop=True)
print(df_z)

การรวมข้อมูลแบบกำหนดเอง: ค่าเฉลี่ย Winsorised

ค่าเฉลี่ย Winsorised จะตัดค่าที่สุดโต่งให้อยู่ภายในขอบเขตก่อนคำนวณค่าเฉลี่ย ทำให้ทนต่อการกระจายที่เบ้ได้ดีกว่าค่าเฉลี่ยแบบธรรมดา การรวมข้อมูลแบบกำหนดเองนี้ไม่สามารถเขียนด้วยฟังก์ชันมาตรฐานของ agg() ได้ แต่ทำได้โดยตรงด้วย groupby().apply() โดยตัดค่าที่เปอร์เซ็นไทล์ที่ 5 และ 95 ภายในแต่ละกลุ่ม จากนั้นจึงคำนวณค่าเฉลี่ยของค่าที่ถูกตัดแล้ว

def winsorised_mean(group, lower_pct=0.05, upper_pct=0.95):
    col = group['revenue']
    lo = col.quantile(lower_pct)
    hi = col.quantile(upper_pct)
    clipped = col.clip(lo, hi)
    return clipped.mean()

wins_mean = df.groupby('region').apply(winsorised_mean)
print('Winsorised mean by region:')
print(wins_mean)

หน้าต่างเลื่อนแบบกำหนดเองต่อกลุ่ม

หน้าต่างเลื่อนที่นำไปใช้กับ DataFrame ทั้งหมดจะไม่สนใจขอบเขตของกลุ่ม หากคุณคำนวณค่าเฉลี่ยแบบหน้าต่างเลื่อน 3 แถวบนอนุกรมเวลาที่สลับข้อมูลของสินค้าสองรายการ หน้าต่างจะข้ามขอบเขตของสินค้าอย่างไม่ถูกต้อง ให้ใช้หน้าต่างเลื่อนภายใน groupby().apply() เพื่อให้แน่ใจว่าการคำนวณแต่ละครั้งยังอยู่ภายในกลุ่มของตนเอง เช่น การคำนวณค่าเฉลี่ยรายได้แบบหน้าต่างเลื่อน 3 เดือนต่อภูมิภาค

def group_rolling_mean(group, window=3):
    group = group.sort_values('order_date').copy()
    group['rolling_revenue'] = group['revenue'].rolling(window, min_periods=1).mean()
    return group

# df_ts has order_date column
# df_rolled = df_ts.groupby('region').apply(group_rolling_mean).reset_index(drop=True)
print('Rolling window per group applied inside apply()')

พารามิเตอร์ include_groups (Pandas 2.2 ขึ้นไป)

ใน Pandas 2.2 และรุ่นถัดไป groupby().apply() จะแสดง FutureWarning หากคีย์ของ groupby ปรากฏอยู่ใน DataFrame ที่ฟังก์ชันได้รับ ให้ส่ง include_groups=False เพื่อไม่รวมคอลัมน์ของ groupby ไว้ใน DataFrame ย่อยที่ส่งให้ฟังก์ชัน วิธีนี้จะหลีกเลี่ยงทั้งคำเตือนและการดำเนินการโดยไม่ตั้งใจกับคอลัมน์คีย์ภายในเนื้อหาของฟังก์ชัน

def revenue_only(group):
    # group does not include 'region' column when include_groups=False
    return group['revenue'].sum()

# result = df.groupby('region').apply(revenue_only, include_groups=False)
print('include_groups=False avoids FutureWarning in Pandas 2.2+')

การรวมผลลัพธ์จาก apply() ด้วย reset_index

เมื่อ groupby().apply() คืนค่า DataFrame ต่อกลุ่ม ผลลัพธ์จะมี MultiIndex ซึ่งมีคีย์ของกลุ่มเป็นระดับนอกสุด ใช้ reset_index(drop=True) เพื่อทำให้ดัชนีกลับเป็นช่วงจำนวนเต็มแบบธรรมดา หรือใช้ reset_index(level=0) เพื่อยกระดับคีย์ของกลุ่มให้เป็นคอลัมน์ ทำให้คีย์ดังกล่าวปรากฏอย่างชัดเจนในผลลัพธ์

result = df.groupby('region').apply(top2_per_region)
print('With MultiIndex:')
print(result.head())

print('\nAfter reset_index:')
print(result.reset_index(drop=True))

เมื่อใดควรเลือกใช้ transform() แทน apply()

groupby().apply() ใช้สำหรับการคำนวณที่ซับซ้อนในระดับกลุ่ม ซึ่งคืนผลลัพธ์ที่มีรูปร่างแตกต่างจากข้อมูลนำเข้า ส่วน groupby().transform() ใช้สำหรับการคำนวณที่เพิ่มคอลัมน์ใหม่ให้สอดคล้องกับดัชนีเดิม เช่น การกระจายค่าเฉลี่ยของกลุ่มกลับไปยังแต่ละแถวเพื่อปรับข้อมูลให้เป็นมาตรฐาน ใช้ transform เมื่อคุณต้องการให้ผลลัพธ์มีรูปร่างเดียวกับ DataFrame เดิม และใช้ apply เมื่อรูปร่างของผลลัพธ์แตกต่างออกไป

# transform: adds group mean back to each row
df['group_mean_revenue'] = df.groupby('region')['revenue'].transform('mean')

# apply: computes one scalar per group
group_totals = df.groupby('region')['revenue'].apply(sum)

print(df[['region', 'revenue', 'group_mean_revenue']])

เคล็ดลับด้านประสิทธิภาพ: หลีกเลี่ยงการใช้ apply() กับการรวมข้อมูลแบบง่าย

groupby().apply() ช้ากว่า groupby().agg() อย่างมากสำหรับการดำเนินการแบบง่าย เนื่องจาก apply() จะสร้างอ็อบเจ็กต์ Python แบบเต็มสำหรับแต่ละกลุ่ม สำหรับผลรวม ค่าเฉลี่ย และจำนวน ให้ใช้ agg() เสมอ เก็บ apply() ไว้ใช้ในกรณีที่จำเป็นต้องใช้ DataFrame ทั้งกลุ่มจริง ๆ เช่น ตรรกะแบบมีเงื่อนไขจากหลายคอลัมน์ สถิติแบบกำหนดเอง หรือการกรองภายในกลุ่ม

# SLOW: apply for simple sum
slow = df.groupby('region').apply(lambda g: g['revenue'].sum())

# FAST: native agg
fast = df.groupby('region')['revenue'].sum()

print('Both produce the same result:')
print(fast.equals(slow))

ตรวจสอบความเข้าใจ

ทดสอบความเข้าใจแนวคิดการวิเคราะห์ข้อมูลจากบทเรียนนี้

ทบทวนบทเรียน

ในบทเรียนนี้ คุณได้เรียนรู้เกี่ยวกับ การคืนค่าเดียว, Series และ DataFrame จาก groupby().apply() การคำนวณค่า Z ภายในกลุ่มและสถิติแบบกำหนดเองที่ทนต่อค่าผิดปกติ และ การเลือกระหว่าง apply(), agg() และ transform() สำหรับการดำเนินการในระดับกลุ่ม ลำดับถัดไป เราจะสำรวจ map() และ applymap() สำหรับการดำเนินการกับสมาชิกแต่ละตัวใน Series และ DataFrames

เริ่มต้นได้ฟรี

เรียนรู้ Python ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
30
บทเรียน
120

คำถามที่พบบ่อย

บทเรียน “apply() กับ GroupBy” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “apply() กับ GroupBy” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Pandas & NumPy Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Pandas & NumPy Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “apply() กับ GroupBy”

ส่งฟังก์ชันที่ทำงานกับหลายแถวให้ groupby().apply() เพื่อคำนวณสรุประดับกลุ่มที่ซับซ้อน ซึ่ง agg() ไม่สามารถแสดงได้ คุณปฏิบัติ Pandas & NumPy Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Pandas & NumPy Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Pandas & NumPy Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “apply() กับ GroupBy” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Pandas & NumPy Academy นี้ได้ไหม

ได้ บทเรียน Pandas & NumPy Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. apply() กับคอลัมน์และแถว
  2. apply() กับ GroupBy
  3. map() และ applymap() สำหรับการดำเนินการทีละองค์ประกอบ
  4. การต่อเมธอดด้วย pipe()
← กลับไปที่ Pandas & NumPy Academy