Data Analysis for Engineers/Module 6

Module 6 · Section 8 of 8

Capstone Project

Pick one track and produce a short technical report (analysis notebook + 1-2 page write-up). Each track exercises the full workflow from Modules 2-5: load -> clean -> explore -> model -> interpret.

Track A - Extreme-Value Analysis (any discipline)

Given an annual-maximum series (peak rainfall, peak demand, peak load, worst-case process upset), fit a Gumbel distribution, build a return-period table (10/25/50/100 yr), check the fit, and recommend a design value with justification.

Track B - Consumption / Demand Forecasting

Given a monthly series with trend and seasonality, build a forecast model (trend+seasonal regression, or ARIMA/SARIMA), produce a 12-month forecast with uncertainty, back-test it on held-out data, and state the expected error.

Track C - Classification / Anomaly Detection

Given a labeled dataset of "normal" vs "fault" (equipment, samples, transactions), train a classifier, evaluate it honestly with a train/test split and precision/recall, and identify which features matter most.

Starter Datasets

import numpy as np, pandas as pd
from scipy import stats

# Three starter datasets - pick ONE track for your capstone report.

# --- Track A: 35-year annual maximum series (frame the units to your discipline) ---
np.random.seed(201)
track_a = pd.DataFrame({
    "year": np.arange(1989, 2024),
    "annual_max": np.round(stats.gumbel_r.rvs(loc=200, scale=48, size=35, random_state=201), 1),
})

# --- Track B: 120 months with a linear trend + a 12-month seasonal cycle + noise ---
np.random.seed(202)
tb = np.arange(120)
track_b = pd.DataFrame({
    "date": pd.date_range("2014-01-01", periods=120, freq="MS"),
    "value": np.round(5000 + 9 * tb + 400 * np.sin(2 * np.pi * tb / 12)
                      + np.random.normal(0, 120, 120), 1),
})

# --- Track C: 600 labelled units - 3 numeric features + a 0/1 fault label ---
np.random.seed(203)
n = 600
f1 = np.random.uniform(0, 10, n)
f2 = np.random.uniform(20, 80, n)
f3 = np.random.uniform(100, 5000, n)
# a hidden weighted rule (with noise) decides the label
score = 0.4 * (f1 - 5) + 0.05 * (f2 - 50) + 0.0004 * (f3 - 2500) + np.random.normal(0, 1, n)
track_c = pd.DataFrame({"feat_1": f1.round(2), "feat_2": f2.round(1),
                        "feat_3": f3.round(0), "fault": (score > 0.6).astype(int)})

print("Track A:", track_a.shape, " Track B:", track_b.shape, " Track C:", track_c.shape)
print(track_c["fault"].value_counts().to_dict())   # check the class balance for Track C
track_a.head()

Suggested Workflow (all tracks)

  1. Inspect - shape, dtypes, missing values, obvious outliers.
  2. Clean - decide drop vs. fill, and document the choice.
  3. Explore - at least two charts that show the structure you're about to model.
  4. Model - fit, and state your assumptions.
  5. Validate - a fit check (Track A), a back-test (Track B), or a train/test split with precision/recall (Track C).
  6. Interpret - 2-3 sentences a non-specialist decision-maker could act on, plus the main limitation of your analysis.

Grading Rubric

CriterionPoints
Data handled correctly (inspect / clean, documented)20
Appropriate model, correctly applied30
Honest validation (fit check / back-test / held-out metrics)25
Charts that support the argument15
Written interpretation with a stated limitation10
Total100

63 / 63 sections · Course home · Join the coaching cohort