Data Science & AI Capstone Projects
A complete data cleaning and aggregation analytics pipeline calculating revenue trends, customer lifetime value, and top-selling product categories:
# =========================================================================
# PROJECT 1: E-COMMERCE SALES ANALYTICS PIPELINE
# =========================================================================
class SalesAnalyticsPipeline:
def __init__(self, raw_transactions):
self.raw_data = raw_transactions
self.cleaned_data = []
def clean_data(self):
"""Imputes missing values and removes corrupted entries."""
for item in self.raw_data:
# Drop records with invalid order IDs:
if not item.get("order_id"):
continue
cleaned_row = item.copy()
# Impute default quantity if missing:
if cleaned_row.get("quantity") is None or cleaned_row["quantity"] <= 0:
cleaned_row["quantity"] = 1
# Calculate total line revenue:
cleaned_row["revenue"] = cleaned_row["quantity"] * cleaned_row["unit_price"]
self.cleaned_data.append(cleaned_row)
print(f"๐งน Cleaned {len(self.cleaned_data)} valid transactions.")
def compute_category_summary(self):
summary = {}
for row in self.cleaned_data:
cat = row["category"]
if cat not in summary:
summary[cat] = {"total_revenue": 0.0, "units_sold": 0}
summary[cat]["total_revenue"] += row["revenue"]
summary[cat]["units_sold"] += row["quantity"]
return summary
# Run Project 1 Demonstration:
sample_transactions = [
{"order_id": "ORD1", "category": "Electronics", "unit_price": 2499.0, "quantity": 2},
{"order_id": "ORD2", "category": "Accessories", "unit_price": 399.0, "quantity": None}, # Missing qty
{"order_id": "ORD3", "category": "Electronics", "unit_price": 4999.0, "quantity": 1},
{"order_id": None, "category": "Electronics", "unit_price": 1000.0, "quantity": 1}, # Corrupted
{"order_id": "ORD4", "category": "Accessories", "unit_price": 1299.0, "quantity": 3}
]
pipeline = SalesAnalyticsPipeline(sample_transactions)
pipeline.clean_data()
cat_stats = pipeline.compute_category_summary()
print("\n--- ๐ E-Commerce Category Performance Report ---")
for cat, stats in cat_stats.items():
print(f"โข {cat:15}: Total Revenue: โน{stats['total_revenue']:>10,.2f} | Units Sold: {stats['units_sold']}")
Encapsulates data ingestion, anomaly validation, missing field imputation, and multi-dimensional aggregations inside reusable class methods.
A multi-feature regression model predicting residential property valuations based on square footage, bedroom count, and age:
# =========================================================================
# PROJECT 2: REAL ESTATE PRICE PREDICTION ENGINE
# =========================================================================
class HousePricePredictor:
"""Multi-variable linear pricing engine."""
def __init__(self, base_price=20.0, price_per_sqft=0.06, price_per_bed=15.0, age_depreciation=0.5):
self.base_price = base_price
self.price_per_sqft = price_per_sqft
self.price_per_bed = price_per_bed
self.age_depreciation = age_depreciation
def estimate_price(self, sqft, bedrooms, age_years):
valuation = (
self.base_price +
(sqft * self.price_per_sqft) +
(bedrooms * self.price_per_bed) -
(age_years * self.age_depreciation)
)
return max(valuation, 10.0) # Price floor
# Run Project 2 Demonstration:
predictor = HousePricePredictor()
test_houses = [
{"desc": "Modern 2BHK Apartment", "sqft": 1200, "beds": 2, "age": 2},
{"desc": "Spacious 3BHK Villa", "sqft": 2400, "beds": 3, "age": 5},
{"desc": "Older 4BHK Family Home", "sqft": 3000, "beds": 4, "age": 18}
]
print("--- ๐ก Real Estate Valuation Estimates ---")
for h in test_houses:
val = predictor.estimate_price(h["sqft"], h["beds"], h["age"])
print(f"โข {h['desc']:25} ({h['sqft']} sqft, {h['beds']} beds) -> Estimated Price: โน{val:,.2f} Lakhs")
Calculates multi-dimensional feature weights ($y = w_1 x_1 + w_2 x_2 + w_3 x_3 + b$) to estimate continuous market valuations.
A binary classification model evaluating whether a subscription customer is likely to cancel based on usage metrics and support tickets:
# =========================================================================
# PROJECT 3: CUSTOMER CHURN CLASSIFICATION ENGINE
# =========================================================================
import math
class CustomerChurnClassifier:
"""Logistic probability engine for predicting customer churn risk."""
def calculate_churn_probability(self, monthly_logins, support_tickets, days_since_last_active):
# Linear log-odds score:
z = -2.0 - (0.15 * monthly_logins) + (0.80 * support_tickets) + (0.05 * days_since_last_active)
# Sigmoid function converting log-odds to probability (0.0 to 1.0):
probability = 1.0 / (1.0 + math.exp(-z))
return probability
def evaluate_risk(self, customer_name, logins, tickets, inactivity_days):
prob = self.calculate_churn_probability(logins, tickets, inactivity_days)
risk_tier = "๐ด HIGH RISK (Churn Likely)" if prob >= 0.65 else ("๐ก MODERATE" if prob >= 0.35 else "๐ข HEALTHY")
return {
"customer": customer_name,
"churn_probability": f"{prob * 100:.1f}%",
"risk_status": risk_tier
}
# Run Project 3 Demonstration:
classifier = CustomerChurnClassifier()
c1 = classifier.evaluate_risk("Balaji Dev (Enterprise Plan)", logins=45, tickets=1, inactivity_days=1)
c2 = classifier.evaluate_risk("Alex Smith (Basic Plan)", logins=3, tickets=5, inactivity_days=25)
c3 = classifier.evaluate_risk("Chloe Davis (Pro Plan)", logins=12, tickets=2, inactivity_days=8)
print("--- ๐จ Customer Churn Risk Assessment ---")
for c in [c1, c2, c3]:
print(f"โข {c['customer']:32} | Probability: {c['churn_probability']:>6} | Status: {c['risk_status']}")
The Sigmoid function $\sigma(z) = \frac{1}{1 + e^{-z}}$ maps any real number to a valid probability between 0 and 1.
An unsupervised clustering model grouping customers into distinct demographic personas based on annual income and spending score:
# =========================================================================
# PROJECT 4: K-MEANS CUSTOMER CLUSTERING SIMULATOR
# =========================================================================
class CustomerSegmentationClusterer:
"""Assigns customers to clusters based on Income and Spending Score."""
# Pre-computed cluster centroids:
CENTROIDS = {
"VIP High-Spenders": {"income": 95, "spending": 85},
"Budget Conscious": {"income": 35, "spending": 25},
"Conservative Savers": {"income": 90, "spending": 20}
}
def assign_cluster(self, income_k, spending_score):
best_cluster = None
min_distance = float("inf")
# Calculate Euclidean Distance to each centroid:
for name, center in self.CENTROIDS.items():
dist = ((income_k - center["income"]) ** 2 + (spending_score - center["spending"]) ** 2) ** 0.5
if dist < min_distance:
min_distance = dist
best_cluster = name
return best_cluster
# Run Project 4 Demonstration:
clusterer = CustomerSegmentationClusterer()
test_shoppers = [
("Customer A", 100, 90),
("Customer B", 30, 20),
("Customer C", 85, 15),
("Customer D", 92, 88)
]
print("--- ๐ฅ Unsupervised Customer Segmentation (K-Means) ---")
for name, inc, spend in test_shoppers:
cluster = clusterer.assign_cluster(inc, spend)
print(f"โข {name} (Income: โน{inc}k, Spend Score: {spend}) -> Assigned Persona: ๐ท๏ธ [{cluster}]")
Calculates minimum distance $d = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2}$ to assign points to the nearest centroid.
A financial time-series analytics model calculating 7-day Simple Moving Averages (SMA), daily volatility, and trend momentum:
# =========================================================================
# PROJECT 5: FINANCIAL STOCK TIME-SERIES ANALYZER
# =========================================================================
class StockTimeSeriesAnalyzer:
def __init__(self, ticker, closing_prices):
self.ticker = ticker
self.prices = closing_prices
def compute_moving_average(self, window=3):
"""Calculates Rolling Simple Moving Average."""
sma = []
for i in range(len(self.prices)):
if i < window - 1:
sma.append(None)
else:
window_slice = self.prices[i - window + 1 : i + 1]
sma.append(round(sum(window_slice) / window, 2))
return sma
def compute_volatility(self):
"""Calculates Standard Deviation of Daily Price Returns."""
returns = [(self.prices[i] - self.prices[i-1]) / self.prices[i-1] for i in range(1, len(self.prices))]
mean_ret = sum(returns) / len(returns)
variance = sum((r - mean_ret) ** 2 for r in returns) / len(returns)
volatility_pct = (variance ** 0.5) * 100
return volatility_pct
# Run Project 5 Demonstration:
tcs_prices = [4150.0, 4180.0, 4220.0, 4200.0, 4260.0, 4310.0, 4290.0]
analyzer = StockTimeSeriesAnalyzer("TCS", tcs_prices)
sma_3day = analyzer.compute_moving_average(window=3)
vol = analyzer.compute_volatility()
print(f"--- ๐ Financial Analysis for [{analyzer.ticker}] ---")
print(f"โข Daily Closing Prices: {tcs_prices}")
print(f"โข 3-Day Rolling SMA: {sma_3day}")
print(f"โข Daily Volatility: {vol:.2f}% (Price Stability: High)")
In real Pandas: df['SMA_7'] = df['Close'].rolling(window=7).mean() computes rolling moving averages across millions of rows instantaneously.
A high correlation coefficient (e.g. r = 0.95) between ice cream sales and shark attacks does not mean ice cream causes shark attacks (the confounding variable is summer temperature). Always validate business logic beyond raw correlation metrics.
Use the HousePricePredictor from Project 2 to estimate the valuation of a 1,800 sqft, 3-bedroom house that is 4 years old.
predictor = HousePricePredictor()
val = predictor.estimate_price(sqft=1800, bedrooms=3, age_years=4)
print(f"Valuation for 1,800 sqft 3BHK: โน{val:,.2f} Lakhs")
Q What is the difference between AI, Machine Learning, and Deep Learning?
Artificial Intelligence (AI) is the broad science of simulating human intelligence. Machine Learning (ML) is a subset of AI using statistical models that learn from data. Deep Learning (DL) is a subset of ML using multi-layered artificial neural networks (CNNs, Transformers, LLMs).
Q What is Overfitting vs Underfitting in Machine Learning?
Overfitting occurs when a model memorizes noise in the training set and performs poorly on unseen test data (high variance). Underfitting occurs when a model is too simple to capture patterns in the data (high bias).
Q Why is Pandas / NumPy vectorization faster than Python for loops?
Vectorization delegates iteration and mathematical computation to low-level compiled C routines running directly on CPU registers with SIMD vector instructions, avoiding Python interpreter bytecode overhead.