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

การจัดโครงสร้างขั้นตอนการแปลงเป็นฟังก์ชัน

แยกสมุดบันทึกของคุณเป็นฟังก์ชันดึงข้อมูล แปลงข้อมูล และโหลดข้อมูล โดยแต่ละฟังก์ชันรับและส่งคืน DataFrame เพื่อให้ทดสอบได้ง่าย

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

ก้าวต่อไปจากโน้ตบุ๊ก

โน้ตบุ๊ก Jupyter เหมาะสำหรับการสำรวจข้อมูล แต่ไม่เหมาะกับสายงานข้อมูลระดับใช้งานจริง โค้ดที่กระจัดกระจายอยู่ใน 50 เซลล์ มีสถานะส่วนกลาง และไม่มีการทดสอบนั้นเปราะบาง การเปลี่ยนเซลล์หนึ่งอาจทำให้อีกเซลล์เสียหายโดยไม่แสดงข้อผิดพลาด ทางเลือกแบบมืออาชีพคือแยกการแปลงข้อมูลแต่ละรายการออกเป็น ฟังก์ชันที่มีชื่อ ซึ่งรับ DataFrame และคืนค่า DataFrame การแยกหน้าที่ในลักษณะนี้เป็นพื้นฐานของโค้ดวิศวกรรมข้อมูลที่ดูแลรักษาได้

# Notebook-style (fragile)
df = pd.read_csv('orders.csv')
df = df.dropna(subset=['revenue'])
df = df[df['quantity'] > 0]
df['revenue_per_unit'] = df['revenue'] / df['quantity']

# Function-style (robust)
def extract(path):
    return pd.read_csv(path)

def transform(df):
    return (
        df.dropna(subset=['revenue'])
        .query('quantity > 0')
        .assign(revenue_per_unit=lambda d: d['revenue'] / d['quantity'])
    )

df = transform(extract('orders.csv'))

รูปแบบ ETL: สกัด แปลง และโหลด

รูปแบบ ETL แบ่งสายงานข้อมูลออกเป็นสามระยะ ได้แก่ Extract (อ่านจากแหล่งข้อมูล), Transform (ทำความสะอาดและเพิ่มข้อมูล) และ Load (เขียนไปยังปลายทาง) แต่ละระยะเป็นฟังก์ชันแยกกัน การแยกเช่นนี้ทำให้เปลี่ยนแหล่งข้อมูลได้ง่าย (CSV หรือฐานข้อมูล) เปลี่ยนตรรกะการทำความสะอาด หรือเปลี่ยนรูปแบบผลลัพธ์ได้โดยไม่ต้องแก้ไขอีกสองระยะ ทุกสายงานระดับใช้งานจริงควรใช้โครงสร้างนี้

def extract(config):
    return pd.read_csv(config['input_path'], parse_dates=['order_date'])

def transform(df, config):
    return (
        df
        .dropna(subset=config['required_cols'])
        .query('quantity > 0')
        .assign(revenue=lambda d: d['quantity'] * d['unit_price'])
    )

def load(df, config):
    df.to_parquet(config['output_path'], index=False)
    print(f'Saved {len(df)} rows.')

หลักความรับผิดชอบเดียว

ฟังก์ชันการแปลงแต่ละฟังก์ชันควรทำ เพียงอย่างเดียว ฟังก์ชันชื่อ clean_data() ที่ลบค่าว่าง จำกัดค่าผิดปกติ แปลงวันที่ และเข้ารหัสหมวดหมู่พร้อมกัน จะทดสอบและแก้จุดบกพร่องได้ยาก ดังนั้นให้เขียน drop_nulls(), cap_outliers(), parse_dates() และ encode_categories() เป็นฟังก์ชันแยกกัน ความละเอียดระดับนี้ทำให้ข้าม แทนที่ หรือจัดลำดับขั้นตอนใดขั้นตอนหนึ่งใหม่ได้ง่าย

def drop_null_rows(df, required_cols):
    return df.dropna(subset=required_cols)

def remove_returns(df):
    return df[df['quantity'] > 0]

def compute_revenue(df):
    return df.assign(revenue=lambda d: d['quantity'] * d['unit_price'])

def add_date_features(df):
    return df.assign(
        year=lambda d: d['order_date'].dt.year,
        month=lambda d: d['order_date'].dt.month
    )

การเชื่อมขั้นตอนด้วย pipe()

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

import pandas as pd

REQUIRED = ['order_id', 'revenue', 'order_date']

def transform(raw_df):
    return (
        raw_df
        .pipe(drop_null_rows, required_cols=REQUIRED)
        .pipe(remove_returns)
        .pipe(compute_revenue)
        .pipe(add_date_features)
    )

df_clean = transform(pd.read_csv('orders.csv', parse_dates=['order_date']))
print(df_clean.shape)

การคืนค่าจำนวนแถวเพื่อการตรวจสอบ

ฟังก์ชันการแปลงแต่ละฟังก์ชันควรบันทึกจำนวนแถวที่ได้รับและจำนวนแถวที่คืนค่าได้ตามต้องการ การห่อส่วนเนื้อหาของฟังก์ชันด้วยการบันทึกจำนวนแถวก่อนและหลัง เป็นประวัติการตรวจสอบแบบเรียบง่ายที่ช่วยให้เห็นได้อย่างรวดเร็วว่าแถวถูกลบออกที่ใดระหว่างการทำงานของสายงาน ให้เก็บจำนวนเหล่านี้ไว้ในรายการ แล้วพิมพ์เป็นรายงานเมื่อการทำงานแต่ละครั้งสิ้นสุดลง

audit_log = []

def audited(func):
    def wrapper(df, *args, **kwargs):
        before = len(df)
        result = func(df, *args, **kwargs)
        after = len(result)
        audit_log.append({'step': func.__name__, 'in': before, 'out': after, 'dropped': before - after})
        return result
    return wrapper

@audited
def drop_null_rows(df, required_cols):
    return df.dropna(subset=required_cols)

ฟังก์ชันที่ทดสอบได้ด้วย DataFrame ขนาดเล็ก

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

import pandas as pd

def test_drop_null_rows():
    test_df = pd.DataFrame({
        'order_id': [1, 2, 3],
        'revenue': [100.0, None, 200.0]
    })
    result = drop_null_rows(test_df, required_cols=['revenue'])
    assert len(result) == 2, 'Should have 2 non-null rows'
    assert result['revenue'].isna().sum() == 0, 'No nulls in revenue'
    print('test_drop_null_rows PASSED')

test_drop_null_rows()

การจัดฟังก์ชันเป็นมอดูล

เมื่อสายงานมีขนาดใหญ่ขึ้น ให้แยกฟังก์ชันไว้ในไฟล์มอดูล Python ได้แก่ extract.py, transform.py, load.py และ validate.py สคริปต์หลัก pipeline.py จะนำเข้าและควบคุมการทำงานของมอดูลเหล่านี้ โครงสร้างไฟล์นี้อ่านเข้าใจง่าย ทดสอบด้วย pytest ได้ และนำไปติดตั้งเป็นแพ็กเกจ Python ได้ โดยมีรูปแบบสอดคล้องกับโครงสร้างมาตรฐานที่ทีมวิศวกรรมข้อมูลซึ่งใช้เครื่องมืออย่าง dbt หรือ Airflow นิยมใช้

# pipeline.py
# from extract import extract_orders
# from transform import transform
# from load import load_to_parquet
# from validate import validate_sales_df

# def run_pipeline(config):
#     raw = extract_orders(config)
#     clean = transform(raw, config)
#     validate_sales_df(clean)
#     load_to_parquet(clean, config)

print('Module-based pipeline structure shown above (imports commented for demo)')

การทำงานแบบไอดอมโพเทนต์: เรียกใช้ซ้ำได้อย่างปลอดภัย

ฟังก์ชันสายงานที่ออกแบบมาอย่างดีควรมีคุณสมบัติ ไอดอมโพเทนต์ กล่าวคือ เมื่อเรียกใช้สองครั้งกับข้อมูลขาเข้าเดียวกัน จะได้ผลลัพธ์เหมือนเดิมและไม่ก่อให้เกิดผลข้างเคียง หลีกเลี่ยงการเปลี่ยนแปลงข้อมูลในที่เดิม (df.drop(..., inplace=True)) และใช้ df.copy() ที่จุดเริ่มต้นของฟังก์ชันที่แก้ไขคอลัมน์เสมอ ฟังก์ชันแบบไอดอมโพเทนต์สามารถเรียกใช้ซ้ำหลังเกิดข้อผิดพลาดได้โดยไม่ทำให้ข้อมูลผลลัพธ์เสียหาย

def add_revenue_flag(df, threshold=500):
    # Use copy to avoid mutating the input
    df = df.copy()
    df['is_large_order'] = df['revenue'] >= threshold
    return df

# Running twice gives the same result
df1 = add_revenue_flag(df_clean)
df2 = add_revenue_flag(df_clean)
assert df1.equals(df2), 'Function is not idempotent!'
print('Idempotency check passed.')

คำใบ้ชนิดข้อมูลเพื่อการอธิบายตัวเอง

เพิ่มคำใบ้ชนิดข้อมูลของ Python ให้กับลายเซ็นฟังก์ชันการแปลง เพื่อระบุข้อตกลงให้ชัดเจน: def transform(df: pd.DataFrame) -> pd.DataFrame คำใบ้ชนิดข้อมูลช่วยอธิบายตัวเอง — นักพัฒนาคนใดก็ตามที่อ่านฟังก์ชันจะทราบทันทีว่าฟังก์ชันต้องการอะไรและคืนค่าอะไร โดยไม่ต้องอ่านเนื้อหาภายใน นอกจากนี้ยังช่วยให้เครื่องมือวิเคราะห์แบบสถิติเช่น mypy และการเติมโค้ดอัตโนมัติในสภาพแวดล้อมพัฒนา ตรวจจับข้อผิดพลาดของชนิดข้อมูลก่อนการทำงานจริงได้

from typing import List

def drop_null_rows(df: pd.DataFrame, required_cols: List[str]) -> pd.DataFrame:
    return df.dropna(subset=required_cols)

def compute_revenue(df: pd.DataFrame) -> pd.DataFrame:
    return df.assign(revenue=lambda d: d['quantity'] * d['unit_price'])

print('Type-annotated functions ready for production use.')

การจัดทำเอกสารแต่ละขั้นตอนด้วย Docstring

ฟังก์ชันการแปลงแต่ละฟังก์ชันควรมี docstring หนึ่งบรรทัดที่ระบุว่าฟังก์ชันทำอะไร ต้องใช้คอลัมน์ใด และเพิ่มหรือลบคอลัมน์ใด Docstring ที่ดีทำให้ค้นพบฟังก์ชันได้ผ่าน help() และคำแนะนำเครื่องมือในสภาพแวดล้อมพัฒนา นอกจากนี้ยังทำหน้าที่เป็นเอกสารข้อกำหนดด้วย กล่าวคือ การตรวจสอบที่ล้มเหลวและขัดแย้งกับ docstring คือข้อผิดพลาด ส่วน docstring ที่ไม่ตรงกับโค้ดคือข้อผิดพลาดด้านเอกสาร

def compute_revenue(df: pd.DataFrame) -> pd.DataFrame:
    """Add 'revenue' column as quantity * unit_price.

    Requires: 'quantity' (numeric), 'unit_price' (numeric) columns.
    Returns: df with new 'revenue' float column appended.
    """
    return df.assign(revenue=lambda d: d['quantity'] * d['unit_price'])

help(compute_revenue)

การเรียกใช้สายงานทั้งหมด

ควบคุมการทำงานของ ETL ทั้งหมดโดยเรียกใช้สามระยะตามลำดับ ได้แก่ extract, transform และ load เพิ่มการจับเวลาแต่ละระยะเพื่อวัดว่าใช้เวลาไปกับส่วนใด ดักจับข้อยกเว้นจากแต่ละระยะแยกกัน เพื่อให้ข้อความข้อผิดพลาดระบุได้ว่าระยะใดล้มเหลว บันทึกเวลาเริ่มต้นและสิ้นสุดของการทำงานทั้งหมด รวมถึงระบุว่าสำเร็จหรือล้มเหลวเพื่อใช้ในการติดตาม

import time

CONFIG = {
    'input_path': 'orders.csv',
    'output_path': 'orders_clean.parquet',
    'required_cols': ['order_id', 'revenue']
}

t0 = time.time()
raw = extract(CONFIG)
clean = transform(raw, CONFIG)
load(clean, CONFIG)

print(f'Pipeline completed in {time.time()-t0:.1f}s')
print(f'Audit log: {audit_log}')

ตรวจสอบความเข้าใจอย่างรวดเร็ว

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

สรุปบทเรียน

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

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

บทเรียน “การจัดโครงสร้างขั้นตอนการแปลงเป็นฟังก์ชัน” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “การจัดโครงสร้างขั้นตอนการแปลงเป็นฟังก์ชัน”

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

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

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

บทเรียน “การจัดโครงสร้างขั้นตอนการแปลงเป็นฟังก์ชัน” ใช้เวลานานแค่ไหน

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

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

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

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

  1. การจัดโครงสร้างขั้นตอนการแปลงเป็นฟังก์ชัน
  2. การกำหนดพารามิเตอร์ให้กระบวนการด้วยพจนานุกรมการตั้งค่า
  3. การทดสอบขั้นตอนกระบวนการด้วยการยืนยันเงื่อนไข
  4. การจัดเวลาและบันทึกการทำงานของกระบวนการ
← กลับไปที่ Pandas & NumPy Academy