100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Data Analysis & Feature Engineering
45 minintermediate

Phase 1 — EDA and Data Quality Audit

Phase 1 is the foundation of the capstone — you cannot engineer good features from data you do not understand, and you cannot clean data you have not inspected. In this phase you will profile every column, identify and document data-quality issues, understand the distribution of the target and its relationship with the most important raw features, and produce a written quality report that will guide your decisions in Phases 2 and 3. The motto of Phase 1 is observe before you act: every decision in the subsequent phases should be traceable to a specific finding here.

Analogy🏏Cricket
🏏 Think of it like cricket: Before the match, a seasoned captain walks the pitch, examines the surface, studies the weather, and watches the opposition warm up — all observation, no decisions yet. Only after the complete pre-match inspection does he decide on team composition, batting order, and field settings. Just as the captain's observations drive every match-day decision, Phase 1 observations drive every feature-engineering and cleaning decision. Just as skipping the inspection leads to poor decisions based on assumptions, skipping EDA leads to features built on misunderstood data. The insight is that Phase 1 is the pre-match inspection that makes every subsequent decision deliberate rather than accidental.

Step 1 — Load and Initial Profile

Load the CricketStream dataset, inspect its schema, check dtypes, count rows and columns, and compute basic summary statistics. Flag any immediate concerns — columns with unexpected dtypes, surprising ranges, or names suggesting redundancy. This initial scan takes ten minutes and prevents hours of wasted work on bad assumptions.

Analogy🏏Cricket
🏏 Think of it like cricket: an initial data profile is the captain's first walk to the middle at dawn, glancing at the pitch, the outfield and the sky before committing to any plan. Just as that ten-minute inspection — is the surface cracked, is the grass damp, which way does the slope run — prevents choosing the wrong eleven and losing the toss advantage, loading the dataset to check schema, dtypes, row and column counts and basic summary statistics flags immediate concerns before you build on false assumptions. Just as a captain who spots unexpected moisture reconsiders batting first, you flag columns with surprising ranges, wrong dtypes, or names hinting at redundancy. And just as skipping the walk-out means discovering the turning pitch only after you're three wickets down, skipping the profile means discovering bad data only after hours of wasted feature work. The payoff: ten minutes of upfront inspection that saves hours of building on a misread surface.
python
import pandas as pd
import numpy as np

df = pd.read_csv("data/cricketstream_churn.csv", parse_dates=["snapshot_date","signup_date"])
print(f"Shape: {df.shape}")
print(f"\nDtypes:\n{df.dtypes}")
print(f"\nFirst 3 rows:\n{df.head(3)}")
print(f"\nBasic stats:\n{df.describe(include='all').T}")
print(f"\nTarget distribution:\n{df['churned'].value_counts(normalize=True).round(3)}")

Step 2 — Missing Value and Variance Audit

For every column compute the missing-value rate and the variance. Flag columns with more than twenty percent missing as requiring a strategy decision, and flag near-constant columns (variance below a threshold) for removal. Document the mechanism of missingness for the highest-missing columns — are they MCAR, MAR, or MNAR? — because the mechanism drives the imputation choice in Phase 2.

Analogy🏏Cricket
🏏 Think of it like cricket: the missing-value and variance audit is reviewing a squad's availability sheet and each player's recent scores before selection. Just as you flag a player who has missed more than a fifth of the season's matches as needing a fitness decision, you flag every column with more than twenty percent missing as requiring a strategy call. Just as you drop a batsman who scores the same single every innings — no variance, no distinguishing value — you flag near-constant, low-variance columns for removal. Crucially, just as you diagnose why a player is absent — injured at random, rested for tough away games, or hiding a chronic problem — you document whether missingness is MCAR, MAR, or MNAR, because that mechanism dictates the fix, exactly as the reason for absence dictates whether you wait, rotate, or replace. The payoff: a clear-eyed roster of which columns to drop and which to impute, with the reason driving the Phase 2 imputation choice.
python
# Missing value audit
missing = df.isnull().mean().sort_values(ascending=False)
print("Missing rates (all columns):")
print(missing[missing > 0].round(3))

# Near-constant / low-variance columns (numeric only)
numeric_cols = df.select_dtypes(include="number").columns
variances = df[numeric_cols].var()
near_const = variances[variances < 0.01]
print(f"\nNear-constant numeric columns (var < 0.01):\n{near_const}")

# Missingness indicator: is missingness correlated with churn?
for col in missing[missing > 0].index:
    miss_flag = df[col].isnull().astype(int)
    corr = miss_flag.corr(df["churned"])
    if abs(corr) > 0.1:
        print(f"  WARNING: missingness in '{col}' correlates {corr:.2f} with churn -> MAR/MNAR")

Step 3 — Distribution Analysis

Plot and summarise the distribution of each numeric feature, focusing on skewness and outliers. For weekly_watch_mins and total_sessions compute the skewness statistic and identify the percentile where the long tail begins. For categorical features compute value-count distributions and flag any high-cardinality or rare-category columns. These findings directly inform the transform decisions in Phase 2.

Analogy🏏Cricket
🏏 Think of it like cricket: distribution analysis is studying each player's scoring pattern to see whether it is steady or lopsided. Just as a batsman who makes ducks most innings but one giant double-century has a wildly skewed record with a fat tail, weekly_watch_mins and total_sessions are right-skewed, so you compute the skewness statistic and find the percentile where the long tail of heavy watchers begins. Just as you note a bowler who is only ever picked for one rare opponent — a high-cardinality, rare-usage case — you compute value-count distributions for categorical features and flag high-cardinality or rare-category columns. And just as spotting a skewed record tells the coach to apply a special conditioning plan, these findings tell you which columns need a log transform or special encoding. The payoff: a precise map of skew, outliers and rare categories that directly drives the transform decisions in Phase 2.
python
from scipy import stats

numeric_cols = df.select_dtypes(include="number").drop(columns=["churned"]).columns
print("Skewness of numeric features:")
for col in numeric_cols:
    sk = stats.skew(df[col].dropna())
    pct95 = df[col].quantile(0.95)
    print(f"  {col:35}: skew={sk:+.2f}, 95th pct={pct95:.1f}")

print("\nCategorical value counts:")
cat_cols = df.select_dtypes(include="object").columns
for col in cat_cols:
    vc = df[col].value_counts()
    print(f"  {col}: {len(vc)} unique, rarest={vc.min()} ({vc.idxmin()})")

print("\nTarget correlation with raw numeric features (absolute, top-5):")
corrs = df[numeric_cols].corrwith(df["churned"]).abs().sort_values(ascending=False)
print(corrs.head(5).round(3))

Step 4 — Temporal Structure Check

Check that the dataset's time structure is intact: verify that snapshot_date spans the expected range, that there are no future-dated snapshots, and that the thirty-day churn window correctly precedes each snapshot. Plot the churn rate over time to detect any temporal drift that would affect the train-test split strategy. A time-ordered dataset with stable churn rate is the ideal; temporal drift requires stratification in the split.

Analogy🏏Cricket
🏏 Think of it like cricket: the temporal structure check is verifying the fixture calendar is sound before planning a season-long campaign. Just as you confirm the schedule spans the right dates, contains no impossible future-dated matches, and that each result was recorded after its game was played — never before — you verify snapshot_date covers the expected range, has no future-dated snapshots, and that the thirty-day churn window correctly precedes each snapshot. Just as a selector plots win rate across the season to catch a mid-year collapse in form that would wreck a naive split of early versus late matches, you plot churn rate over time to detect temporal drift that would break the train-test split strategy. And just as a stable-form season is the easy case to plan around while a sudden slump forces a rethink, a stable churn rate is ideal while drift demands care. The payoff: confidence that time is intact, so your later train-test split reflects reality rather than leaking the future.
python
print("Temporal structure audit:")
print(f"  snapshot_date range: {df['snapshot_date'].min()} to {df['snapshot_date'].max()}")
print(f"  tenure_days range:   {df['tenure_days'].min()} to {df['tenure_days'].max()}")

# Churn rate over time (monthly)
df["snapshot_month"] = df["snapshot_date"].dt.to_period("M")
monthly_churn = df.groupby("snapshot_month")["churned"].mean()
print("\nMonthly churn rate (should be stable for clean train/test split):")
print(monthly_churn.round(3))

# Proposed time-ordered split: last 20% of snapshots as test
split_date = df["snapshot_date"].quantile(0.8)
train_n = (df["snapshot_date"] <= split_date).sum()
test_n  = (df["snapshot_date"] > split_date).sum()
print(f"\nProposed split: train={train_n} rows (before {split_date.date()}), "
      f"test={test_n} rows (after)")

Step 5 — Quality Report

Compile your findings into a structured quality report: list every data-quality issue found, the columns affected, the severity, and the planned remediation in Phase 2. Include the near-constant column to be dropped, the columns requiring imputation and the chosen strategy, the skewed columns requiring transformation, and any temporal anomalies. This report is a Phase 2 input document — treat it as an instruction list for yourself.

Analogy🏏Cricket
🏏 Think of it like cricket: the quality report is the captain's formal pitch-and-squad report handed to the coaching staff who will set the game plan. Just as that report lists every condition observed — the cracked length, the damp outfield, the two players carrying niggles — with its severity and the intended response, your report lists every data-quality issue, the columns affected, the severity, and the planned Phase 2 remediation. Just as it names the player to be rested, the ones needing a fitness plan, and any calendar anomaly, it names the near-constant column to drop, the columns to impute and their chosen strategy, the skewed columns to transform, and any temporal anomaly. And just as the coaching staff act directly on that report rather than re-inspecting the ground, Phase 2 consumes this document as a formal contract. The payoff: a structured, actionable handover that turns scattered findings into an explicit remediation plan the next phase executes.
python
quality_report = {
    "near_constant_cols": [],        # fill from near_const above
    "high_missing_cols": {},         # col -> (rate, mechanism, strategy)
    "skewed_cols": {},               # col -> (skew, planned_transform)
    "unseen_cat_risk": [],           # categoricals with rare values
    "temporal_drift": False,         # set True if monthly churn varies > 0.05
    "split_date": str(split_date.date()) if hasattr(split_date,"date") else str(split_date),
    "notes": []
}

print("Quality Report Template (fill with your findings):")
import json
print(json.dumps(quality_report, indent=2))
print("\nThis structured report is your Phase 2 instruction list.")
print("Every cleaning and transform decision in Phase 2 must trace back to a finding here.")

Warning: Do not start Phase 2 until the quality report is complete. Jumping to feature engineering before understanding the data produces features built on wrong assumptions — imputing with the mean when the mechanism is MNAR, transforming a column that was already near-normal, or missing the near-constant column that will corrupt your variance threshold. The quality report is the map; Phase 2 without it is navigating blind.

  • Phase 1 is pure observation: profile every column, audit missingness and variance, analyse distributions, and check temporal structure.
  • Document every quality issue with severity and planned remediation before touching any features.
  • Flag near-constant columns for removal, high-missing columns for imputation strategy decisions, and skewed columns for transforms.
  • Check temporal structure: snapshot-date range, churn-rate stability over time, and the proposed time-ordered split boundary.
  • Every Phase 2 decision must trace to a specific Phase 1 finding — the quality report is the link between observation and action.
Lesson 32 of 35
0% complete