0Pricing
Pandas & NumPy Academy · Ders

Dönüşüm Adımlarını İşlevler Olarak Yapılandırma

Kolay test edilebilmesi için not defterinizi her biri DataFrame kabul edip döndüren ayıklama, dönüştürme ve yükleme işlevlerine ayırın.

Dönüşüm Adımlarını İşlevler Olarak Yapılandırma, CoddyKit'te ücretsiz bir Pandas & NumPy Academy dersidir. Bu, 4 dersinin 1. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, Pandas & NumPy Academy öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. Pandas & NumPy Academy kursu toplamda 4 dersten oluşur.

Bu dersin bazı bölümleri henüz çevrilmemiş olup İngilizce olarak gösterilmektedir.

Moving Beyond Notebooks

Jupyter notebooks are great for exploration but poor for production data pipelines. Code scattered across 50 cells with global state and no tests is fragile: changing one cell can silently break another. The professional alternative is to extract each transformation into a named function that accepts a DataFrame and returns a DataFrame. This separation of concerns is the foundation of maintainable data engineering code.

# 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'))

The ETL Pattern: Extract, Transform, Load

The ETL pattern divides a data pipeline into three phases: Extract (read from source), Transform (clean and enrich), and Load (write to destination). Each phase is a separate function. This separation makes it easy to swap data sources (CSV vs. database), change cleaning logic, or change the output format without touching the other two phases. Every production pipeline should follow this structure.

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.')

Single Responsibility Principle

Each transformation function should do exactly one thing. A function called clean_data() that drops nulls, caps outliers, parses dates, and encodes categories is hard to test and debug. Instead, write drop_nulls(), cap_outliers(), parse_dates(), and encode_categories() as separate functions. This granularity makes it easy to skip, replace, or reorder any single step.

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
    )

Chaining Steps with pipe()

Connect single-responsibility functions using pipe() to build the full transform phase as a readable chain. The chain reads like a recipe: each line is a step, and the arrow of data flows top to bottom. Any step can be commented out or reordered without renaming variables. The final result is the clean, enriched DataFrame ready for the load phase.

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)

Returning Row Counts for Audit

Each transformation function should optionally log how many rows it received and how many it returned. Wrapping the function body in a before/after row count log is a lightweight audit trail that lets you quickly see where rows are being dropped in a pipeline run. Store these counts in a list and print them as a report at the end of each run.

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)

Testable Functions with Small DataFrames

The biggest advantage of named functions is testability. Write a small test DataFrame that represents a realistic edge case and assert the function's output. Test the drop_null_rows function with one row that has a null and verify it is dropped, and another without a null and verify it is kept. Unit tests on transformation functions catch regressions when the pipeline code changes.

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()

Organising Functions into Modules

As the pipeline grows, split functions into Python module files: extract.py, transform.py, load.py, and validate.py. The main pipeline.py script imports and orchestrates them. This file structure is readable, testable with pytest, and deployable as a Python package. It mirrors the standard layout used by data engineering teams using tools like dbt or 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)')

Idempotency: Run Multiple Times Safely

A well-designed pipeline function is idempotent: running it twice on the same input produces the same output and does not cause side effects. Avoid in-place mutations (df.drop(..., inplace=True)) and always use df.copy() at the start of functions that modify columns. Idempotent functions can be re-run after failures without corrupting the output data.

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.')

Type Hints for Self-Documentation

Add Python type hints to transformation function signatures to make the contract explicit: def transform(df: pd.DataFrame) -> pd.DataFrame. Type hints are self-documenting — any developer reading the function knows exactly what it expects and returns without reading the body. They also enable static analysis tools like mypy and IDE auto-complete to catch type errors before runtime.

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.')

Documenting Each Step with Docstrings

Each transformation function should have a one-line docstring stating what it does, what columns it requires, and what columns it adds or removes. Good docstrings make the function discoverable via help() and in IDE tooltips. They also serve as specification documents: a failing assertion that contradicts the docstring is a bug; a docstring that disagrees with the code is a documentation error.

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)

Running the Full Pipeline

Orchestrate the complete ETL by calling the three phases in sequence: extract, transform, and load. Add timing around each phase to measure where time is spent. Catch exceptions from each phase separately so error messages identify which phase failed. Log the start and end time of the full run and whether it succeeded or failed for monitoring purposes.

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}')

Quick Check

Test your understanding of Data Analysis concepts from this lesson.

Lesson Recap

In this lesson you learned: structuring pipelines as ETL functions with single responsibility, making functions testable, idempotent, and self-documenting with type hints and docstrings, and orchestrating the full pipeline with timing and audit logging. Next up we explore parameterising pipelines with config dicts for reusability across different datasets.

Sıkça Sorulan Sorular

“Dönüşüm Adımlarını İşlevler Olarak Yapılandırma” dersi ücretsiz mi?

Evet — “Dönüşüm Adımlarını İşlevler Olarak Yapılandırma” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve Pandas & NumPy Academy kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. Pandas & NumPy Academy kursu toplamda 4 dersten oluşur.

“Dönüşüm Adımlarını İşlevler Olarak Yapılandırma” dersinde ne öğreneceğim?

Kolay test edilebilmesi için not defterinizi her biri DataFrame kabul edip döndüren ayıklama, dönüştürme ve yükleme işlevlerine ayırın. Pandas & NumPy Academy ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

Pandas & NumPy Academy öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te Pandas & NumPy Academy, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 1. dersidir.

“Dönüşüm Adımlarını İşlevler Olarak Yapılandırma” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu Pandas & NumPy Academy dersinde kod yazıp çalıştırabilir miyim?

Evet. Her Pandas & NumPy Academy dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. Dönüşüm Adımlarını İşlevler Olarak Yapılandırma
  2. İşlem Hatlarını Yapılandırma Sözlükleriyle Parametreleştirme
  3. İşlem Hattı Adımlarını Assertion'larla Test Etme
  4. İşlem Hattı Çalıştırmalarını Zamanlama ve Günlüğe Kaydetme
← Pandas & NumPy Academy Sayfasına Dön