0Pricing
Pandas & NumPy Academy · Lección

Limpieza de datos e ingeniería de características

Aplique la lista de comprobación avanzada de limpieza, cree características de fecha y columnas de proporciones, y valide el conjunto de datos limpio mediante aserciones.

Limpieza de datos e ingeniería de características es una lección gratuita de Pandas & NumPy Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Pandas & NumPy Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Pandas & NumPy Academy incluye 4 lecciones en total.

Partes de esta lección aún no han sido traducidas y se muestran en inglés.

Loading the Audited Dataset

Continuing the capstone project, load the clean Parquet checkpoint saved in the previous step. This decouples the cleaning stage from ingestion — you can iterate on cleaning logic without re-running the slow merge and parse operations. Read the clean_orders Parquet and verify the row count and dtypes match expectations from the audit summary. The Parquet file preserves all dtypes including categorical columns, so no re-casting is needed.

import pandas as pd

# Load from checkpoint
df = pd.read_parquet('output/clean_orders.parquet')
print(f'Loaded: {df.shape}')
print(df.dtypes)
print(f'Date range: {df["order_date"].min().date()} to {df["order_date"].max().date()}')

Applying the Advanced Cleaning Checklist

With the merged dataset loaded, apply the advanced cleaning checklist: remove exact and partial duplicates on order_id, cap outliers in unit_price using IQR fencing, standardise inconsistent category values (e.g., 'Electronics' vs 'electronics' vs 'ELECTRONIC'), and validate that quantity × unit_price is always positive for normal orders. Each step is a function that accepts and returns a DataFrame, making the pipeline testable and reversible.

import pandas as pd
import numpy as np

def remove_duplicates(df):
    n_before = len(df)
    df = df.drop_duplicates(subset=['order_id'], keep='first')
    print(f'Duplicates removed: {n_before - len(df)}')
    return df

def standardise_categories(df):
    df['category'] = (df['category']
                      .astype(str)
                      .str.strip()
                      .str.title())
    return df

def cap_price_outliers(df):
    q1, q3 = df['unit_price'].quantile([0.25, 0.75])
    iqr = q3 - q1
    upper = q3 + 3 * iqr
    df['unit_price'] = df['unit_price'].clip(upper=upper)
    return df

df = remove_duplicates(df)
df = standardise_categories(df)
df = cap_price_outliers(df)
print('Cleaning complete.')

Engineering Date Features

Extract temporal features from the order date using the .dt accessor. Create year, month, quarter, day_of_week, and week_of_year columns. These features power groupby analyses (monthly revenue), time-based filters (Q3 only), and visualisations. Also create a year_month string column (e.g., '2024-06') as a human-readable period label for chart axes.

import pandas as pd

df['year'] = df['order_date'].dt.year
df['month'] = df['order_date'].dt.month
df['quarter'] = df['order_date'].dt.quarter
df['day_of_week'] = df['order_date'].dt.dayofweek  # 0=Monday
df['week'] = df['order_date'].dt.isocalendar().week.astype('int32')
df['year_month'] = df['order_date'].dt.to_period('M').astype(str)

print('Date features created:')
print(df[['order_date', 'year', 'month', 'quarter', 'year_month']].head())

Computing Line-Item Revenue

The most fundamental feature in a sales dataset is line-item revenue: revenue = quantity × unit_price. Create this as a new column. Also create a gross_margin column if cost data is available: margin = (price - cost) / price. These derived columns are the building blocks of all revenue KPIs. Always use vectorised column operations rather than loops — a single assignment on the full column is orders of magnitude faster than iterating row by row.

import pandas as pd

# Line-item revenue
df['revenue'] = (df['quantity'] * df['unit_price']).astype('float32')

# Gross margin (if unit_cost column exists)
if 'unit_cost' in df.columns:
    df['gross_margin'] = (df['unit_price'] - df['unit_cost']) / df['unit_price']

# Discount flag: orders with more than 20% off list price
if 'list_price' in df.columns:
    df['is_discounted'] = df['unit_price'] < df['list_price'] * 0.8

print('Revenue stats:')
print(df['revenue'].describe().round(2))

Customer Cohort Assignment

A cohort is a group of customers who share a characteristic — typically the time period they first made a purchase. Assign each customer to their acquisition cohort by finding their first order date. Use groupby('customer_id')['order_date'].min() to compute the first order date per customer, then create a cohort_month column by converting it to a year-month period. This cohort label is the foundation of the retention matrix in the next lesson.

import pandas as pd

# First purchase date per customer
first_purchase = (
    df.groupby('customer_id')['order_date']
    .min()
    .dt.to_period('M')
    .astype(str)
    .rename('cohort_month')
)

# Merge back onto orders
df = df.merge(first_purchase, on='customer_id', how='left')
print('Cohort distribution (top 5):')
print(df['cohort_month'].value_counts().head())
print(f'Distinct cohorts: {df["cohort_month"].nunique()}')

Customer Lifetime Value Features

Compute customer-level summary features: total orders, total revenue, average order value, days since first/last purchase, and number of distinct categories purchased. Merge these back onto the main DataFrame as customer-level enrichment columns. These features are used for segmentation (e.g., high-value vs. low-value customers) and as inputs to machine learning models predicting churn or upsell probability.

import pandas as pd

customer_stats = df.groupby('customer_id').agg(
    total_orders=('order_id', 'nunique'),
    total_revenue=('revenue', 'sum'),
    avg_order_value=('revenue', 'mean'),
    categories_purchased=('category', 'nunique'),
    first_order=('order_date', 'min'),
    last_order=('order_date', 'max')
).reset_index()

customer_stats['days_as_customer'] = (
    (customer_stats['last_order'] - customer_stats['first_order'])
    .dt.days
)

print(customer_stats.describe().round(2))

Schema Validation with Assertions

After engineering features, run schema validation assertions to confirm the data meets expectations before passing it to the analysis stage. Check that all expected columns exist, revenue is non-negative for normal orders, cohort_month is non-null, and year_month matches the analysis year. These assertions act as a contract: if any fails, the pipeline stops immediately with a clear error message rather than silently producing wrong results.

import pandas as pd

def validate_clean_df(df):
    required_cols = ['order_id', 'customer_id', 'revenue', 'year_month',
                     'cohort_month', 'category', 'region', 'order_date']
    missing = [c for c in required_cols if c not in df.columns]
    assert not missing, f'Missing columns: {missing}'

    assert (df['revenue'] >= 0).all(), 'Negative revenue found'
    assert df['cohort_month'].notna().all(), 'Null cohort_month values'
    assert df['order_date'].notna().all(), 'Null order dates'
    print(f'Validation passed: {len(df):,} rows, {len(df.columns)} columns')

validate_clean_df(df)

Handling Low-Frequency Categories

In real datasets, some categories or regions appear only a handful of times — too few for meaningful analysis. Replace low-frequency values with 'Other' to avoid charts cluttered with dozens of single-order categories. A practical threshold: values appearing in fewer than 0.5% of rows are collapsed. This is also important for machine learning: one-hot encoding 200 rare categories wastes memory and adds noise.

import pandas as pd

def collapse_rare_values(df, col, min_pct=0.005):
    threshold = len(df) * min_pct
    counts = df[col].value_counts()
    rare = counts[counts < threshold].index
    n_rare = len(rare)
    df[col] = df[col].apply(lambda x: 'Other' if x in rare else x)
    print(f'{col}: collapsed {n_rare} rare values into "Other"')
    return df

df = collapse_rare_values(df, 'category', min_pct=0.005)
df = collapse_rare_values(df, 'region', min_pct=0.005)
print('Category distribution after collapsing:')
print(df['category'].value_counts())

Saving the Feature-Engineered Dataset

Save the fully cleaned and feature-engineered DataFrame as a Parquet checkpoint. This is the analysis-ready dataset — the output of all ingestion, cleaning, and feature engineering steps. Subsequent pipeline stages (KPI computation, visualisation, reporting) read from this checkpoint. The checkpoint also serves as a deliverable: it can be shared with team members or uploaded to a data lake for others to query with SQL or Pandas.

import pandas as pd

# Convert category columns back to Categorical for memory efficiency
for col in ['category', 'region', 'acquisition_channel']:
    if col in df.columns:
        df[col] = df[col].astype('category')

# Save analysis-ready dataset
df.to_parquet('output/analysis_ready.parquet', index=False)

# Also save customer stats for segmentation
customer_stats.to_parquet('output/customer_stats.parquet', index=False)

print(f'Analysis-ready dataset: {df.shape}')
print(f'Memory usage: {df.memory_usage(deep=True).sum()/1e6:.1f} MB')

Feature Engineering Summary

Good feature engineering balances adding predictive signal with keeping the pipeline interpretable and testable. The features created in this stage — revenue, date parts, cohort_month, customer stats, collapsed categories — were all driven by the analysis goals defined in the project setup. Resist the temptation to create dozens of features speculatively. For each feature, ask: will this be used in at least one KPI or visualisation? If not, defer it. Keep the analysis-ready dataset lean and purposeful.

import pandas as pd

# Summary of engineered features
df = pd.read_parquet('output/analysis_ready.parquet')

original_cols = ['order_id', 'customer_id', 'order_date', 'quantity', 'unit_price']
engineered_cols = [c for c in df.columns if c not in original_cols]

print('Original columns:', original_cols)
print('Engineered features:', engineered_cols)
print(f'Total columns: {len(df.columns)}')
print(f'Total rows: {len(df):,}')

Documenting Decisions in Code

For every non-obvious cleaning or engineering decision, add a comment explaining the why. Future you (or a team member) will not remember why you set the IQR multiplier to 3 instead of 1.5, or why you used 0.5% as the rare-category threshold. Inline comments and a decisions log (a simple list in the project README or a markdown file) make the pipeline maintainable and auditable. Clean, documented code is the true deliverable — the output files are just its consequence.

# Engineering decisions log
DECISIONS = '''
# Data Cleaning & Feature Engineering Decisions

## Duplicate handling
Kept first occurrence of duplicate order_id (most likely the original order).

## Outlier capping
Unit price capped at Q3 + 3*IQR (not 1.5*IQR) because price distribution
has legitimate high-value enterprise orders that should not be removed.

## Rare category threshold: 0.5%
Values below 0.5% of total orders collapsed to "Other" to keep charts readable
and avoid spurious significance in category-level tests.

## Cohort assignment
Cohort = first purchase calendar month (not signup month), because many users
sign up but do not purchase until months later.
'''
print(DECISIONS)

Quick Check

Test your understanding of Data Analysis concepts from this lesson.

Lesson Recap

In this lesson you learned: modular cleaning functions (remove duplicates, cap outliers, standardise categories) each accept and return a DataFrame for easy testing, feature engineering created date parts, line-item revenue, customer cohorts, and summary statistics, and schema validation assertions guard the transition from cleaning to analysis. Next up we compute the project's core KPIs: monthly cohort retention, product revenue, and rolling active users.

Preguntas frecuentes

¿La lección «Limpieza de datos e ingeniería de características» es gratis?

Sí — el texto completo de «Limpieza de datos e ingeniería de características» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Pandas & NumPy Academy, actualiza a CoddyKit PRO. El curso de Pandas & NumPy Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Limpieza de datos e ingeniería de características»?

Aplique la lista de comprobación avanzada de limpieza, cree características de fecha y columnas de proporciones, y valide el conjunto de datos limpio mediante aserciones. Practicas Pandas & NumPy Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Pandas & NumPy Academy?

No se requiere experiencia previa. Pandas & NumPy Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Limpieza de datos e ingeniería de características»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Pandas & NumPy Academy?

Sí. Cada lección de Pandas & NumPy Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Configuración del proyecto e ingestión de datos
  2. Limpieza de datos e ingeniería de características
  3. Análisis y cálculo de KPI
  4. Visualización final y exportación del informe
← Volver a Pandas & NumPy Academy