BlogData Analytics SeriesChapter 16
SERIES · CHAPTER 16Intermediate

Time Series Analysis for Data Analysts — India Edition

Time series components (trend, seasonality, noise), decomposition, moving averages, Indian festive season patterns (Diwali, Navratri, FY end), year-on-year analysis, and forecasting basics — with Python pandas and statsmodels code throughout.

DATA ANALYTICS SERIES:← Ch 15: Hypothesis TestingCh 16: Time Series ←Ch 17: A/B Testing →

The Three Components of a Time Series

Every business time series can be broken into three components. Understanding which component is driving a change is the first job of a time series analyst.

Trend (T)
The long-term direction — upward, downward, or flat — after removing seasonal and random effects.
INDIAN EXAMPLE:
GMV growing from ₹8 crore/month in 2023 to ₹14 crore/month in 2026 is an upward trend. A category declining over 24 months has a downward trend.
QUESTION IT ANSWERS:
Is the business growing or shrinking over time?
Seasonality (S)
Regular, recurring patterns within a fixed period — daily, weekly, monthly, or annual. The period is known and constant.
INDIAN EXAMPLE:
Sales spike every October–November (Diwali). Orders are 35% higher on Saturdays than Tuesdays. Revenue dips every monsoon (June–August). Salary-date spikes in first week of every month.
QUESTION IT ANSWERS:
What consistent patterns repeat on a schedule?
Residual/Noise (R)
The irregular, unpredictable component remaining after removing trend and seasonality. Contains genuine random variation and anomalies.
INDIAN EXAMPLE:
A delivery partner strike in Chennai drops orders for 4 days. A flash sale creates a one-time spike. A competitor app crash sends traffic to your platform for one week.
QUESTION IT ANSWERS:
What is left that trend and seasonality cannot explain?

Indian Business Seasonality Calendar

India's seasonality patterns are more complex than Western markets — driven by the Hindu lunar calendar (festivals shift dates annually), the April–March financial year, monsoon patterns, and salary credit cycles. Every analyst working with Indian business data must internalise these patterns.

PeriodKey EventsBusiness EffectIndian FY Quarter
JanuaryRepublic DayMinor uptick in apparel, flagsQ3 (Oct–Dec close)
February–MarchValentine's Day, Holi, FY EndGifts, colours, B2B spending surge (FY budget burn)Q4 — FY close
April–MaySummer + IPL + New FYCooling products, electronics, ad cost spikeQ1 starts
June–SeptemberMonsoonUmbrellas/rainwear up, outdoor/travel down; school seasonQ1–Q2
October–NovemberNavratri, Dussehra, Diwali, Dhanteras🔥 Peak season: 2×–4× normal sales for electronics, jewellery, apparel, FMCGQ2–Q3
DecemberChristmas, Year-EndUrban gifting, return of diaspora visitors, travelQ3
DIWALI CAUTION: Diwali shifts by 2–4 weeks each year (lunar calendar). Auto-decomposition models that assume fixed annual seasonality will misattribute Diwali effects to trend or noise. Always check Diwali dates for each year in your dataset and create explicit indicator variables before applying forecasting models.

Moving Averages — Smoothing Out Noise

A moving average smooths short-term fluctuations to reveal the underlying trend. It replaces each data point with the average of a surrounding window of points.

Python · Moving Averages on Indian E-Commerce Daily Sales
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

df = pd.read_csv('india_daily_sales.csv', parse_dates=['date'])
df = df.set_index('date').sort_index()

# === SIMPLE MOVING AVERAGES ===
df['ma_7']  = df['revenue_lakhs'].rolling(window=7).mean()   # 7-day: weekly smoothing
df['ma_28'] = df['revenue_lakhs'].rolling(window=28).mean()  # 28-day: monthly smoothing

# 7-day MA → removes day-of-week effect (Mon/Tue lower, Sat/Sun higher)
# 28-day MA → removes weekly fluctuations; reveals monthly/seasonal trend

# === EXPONENTIAL MOVING AVERAGE (EMA) — more weight on recent data ===
df['ema_7']  = df['revenue_lakhs'].ewm(span=7,  adjust=False).mean()
df['ema_28'] = df['revenue_lakhs'].ewm(span=28, adjust=False).mean()

# Plot
fig, axes = plt.subplots(2, 1, figsize=(14, 8), sharex=True)

axes[0].plot(df.index, df['revenue_lakhs'], alpha=0.3, color='gray', linewidth=0.8, label='Daily')
axes[0].plot(df.index, df['ma_7'],          color='#f97316', linewidth=1.5, label='7-day MA')
axes[0].plot(df.index, df['ma_28'],         color='#7c2d12', linewidth=2,   label='28-day MA')
axes[0].set_ylabel('Revenue (₹ Lakh)'); axes[0].legend(); axes[0].grid(alpha=0.3)
axes[0].set_title('Revenue with Simple Moving Averages', fontweight='bold')

axes[1].plot(df.index, df['revenue_lakhs'], alpha=0.3, color='gray', linewidth=0.8, label='Daily')
axes[1].plot(df.index, df['ema_7'],          color='#0f766e', linewidth=1.5, label='7-day EMA')
axes[1].plot(df.index, df['ema_28'],         color='#134e4a', linewidth=2,   label='28-day EMA')
axes[1].set_ylabel('Revenue (₹ Lakh)'); axes[1].legend(); axes[1].grid(alpha=0.3)
axes[1].set_title('Revenue with Exponential Moving Averages', fontweight='bold')

axes[1].xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))
plt.xticks(rotation=45); plt.tight_layout(); plt.show()

# === DAY-OF-WEEK AND MONTHLY SEASONALITY ===
df['dow']       = df.index.day_name()
df['month']     = df.index.month_name()
df['week_num']  = df.index.isocalendar().week

# Day-of-week pattern
dow_avg = df.groupby('dow')['revenue_lakhs'].mean().reindex(
    ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'])
print("Average Revenue by Day of Week:")
for day, val in dow_avg.items():
    bar = '█' * int(val / dow_avg.max() * 20)
    print(f"  {day:10s}: ₹{val:.1f}L {bar}")

# Monthly pattern (account for Diwali using absolute dates, not month name)
monthly_avg = df.groupby('month')['revenue_lakhs'].mean()
print("\nAverage Revenue by Month:", monthly_avg.to_dict())

Time Series Decomposition

Decomposition algorithmically separates a time series into trend, seasonality, and residual components — letting you analyse each independently.

Python · Decomposition + Year-on-Year Analysis
from statsmodels.tsa.seasonal import seasonal_decompose
import pandas as pd
import matplotlib.pyplot as plt

# Use weekly aggregation for cleaner decomposition
weekly = df['revenue_lakhs'].resample('W').sum()

# Multiplicative decomposition (use when seasonal amplitude grows with trend)
result = seasonal_decompose(weekly, model='multiplicative', period=52)
# period=52 → looks for annual seasonality in weekly data

result.plot()
plt.suptitle('Time Series Decomposition — India E-Commerce Revenue', fontweight='bold', y=1.01)
plt.tight_layout(); plt.show()

# Extract components
trend      = result.trend.dropna()
seasonal   = result.seasonal.dropna()
residual   = result.resid.dropna()

print(f"Trend direction: {'Up' if trend.iloc[-1] > trend.iloc[0] else 'Down'}")
print(f"Seasonal peak multiplier: {seasonal.max():.2f}x")
print(f"Residual std: {residual.std():.3f} (closer to 1.0 = model fits well)")

# ============================================================
# YEAR-ON-YEAR ANALYSIS — Best approach for Indian businesses
# (controls for shifting festival dates across years)
# ============================================================
monthly = df['revenue_lakhs'].resample('ME').sum().reset_index()
monthly.columns = ['month', 'revenue']
monthly['year']       = monthly['month'].dt.year
monthly['month_num']  = monthly['month'].dt.month

yoy = monthly.pivot(index='month_num', columns='year', values='revenue')
yoy['growth_pct'] = (yoy[2026] - yoy[2025]) / yoy[2025] * 100

print("\nYear-on-Year Revenue Growth (2025 → 2026):")
month_names = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
for m_num, row in yoy.iterrows():
    print(f"  {month_names[m_num-1]:4s}: ₹{row[2025]:.1f}L → ₹{row[2026]:.1f}L  ({row['growth_pct']:+.1f}%)")

# Highlight anomalies in residuals (months where actual deviated from model)
residual_monthly = residual.resample('ME').mean()
anomalies = residual_monthly[abs(residual_monthly - 1) > 0.2]  # >20% from expected
print(f"\nAnomalous periods (residual > 1.2 or < 0.8):")
for date, val in anomalies.items():
    direction = 'SPIKE' if val > 1 else 'DIP'
    print(f"  {date.strftime('%b %Y')}: {val:.2f}x expected  ← {direction}")

Basic Forecasting — Prophet for Indian Data

Prophet (Meta's forecasting library) is particularly popular in India because it handles irregular holidays (like Diwali) explicitly. You can add custom holiday effects for any date pattern.

Python · Prophet with Indian Holidays
from prophet import Prophet
import pandas as pd

# Prophet requires columns: 'ds' (datestamp) and 'y' (value)
daily = df[['date', 'revenue_lakhs']].rename(columns={'date': 'ds', 'revenue_lakhs': 'y'})

# Define Indian holidays (Diwali shifts each year — add actual dates)
indian_holidays = pd.DataFrame({
    'holiday': [
        'diwali', 'diwali', 'diwali',         # main festival
        'navratri', 'navratri', 'navratri',    # lead-up period
        'dhanteras', 'dhanteras', 'dhanteras', # day before Diwali — peak buying
        'fy_end', 'fy_end', 'fy_end',          # financial year end
    ],
    'ds': pd.to_datetime([
        '2024-11-01', '2025-10-20', '2026-11-08',
        '2024-10-03', '2025-09-22', '2026-10-10',
        '2024-10-29', '2025-10-18', '2026-11-06',
        '2024-03-31', '2025-03-31', '2026-03-31',
    ]),
    'lower_window': [-7,  -7,  -7,  -3, -3, -3, -1, -1, -1,  -5,  -5,  -5],
    'upper_window': [  2,   2,   2,   0,  0,  0,  1,  1,  1,   0,   0,   0],
})

# Fit the model
model = Prophet(
    holidays       = indian_holidays,
    yearly_seasonality = True,
    weekly_seasonality = True,
    daily_seasonality  = False,  # True only for sub-daily data
    seasonality_mode   = 'multiplicative',  # for growing businesses
)
model.fit(daily)

# Forecast next 90 days
future   = model.make_future_dataframe(periods=90)
forecast = model.predict(future)

# Plot
fig1 = model.plot(forecast)
fig1.suptitle('Revenue Forecast with Diwali & Indian Holidays', fontweight='bold')
plt.show()

# Component plot — shows trend + weekly + holiday effects separately
fig2 = model.plot_components(forecast)
plt.show()

# Extract holiday impacts
holiday_effects = forecast[forecast['ds'].isin(indian_holidays['ds'])][
    ['ds', 'yhat', 'yhat_lower', 'yhat_upper'] + list(indian_holidays['holiday'].unique())
].head(20)
print(holiday_effects)

Time Series Techniques — When to Use What

TechniqueUse WhenPython MethodOutput
7-day moving averageRemove day-of-week effect from daily data.rolling(7).mean()Weekly-smoothed trend line
28-day moving averageRemove weekly + monthly noise; see quarterly trend.rolling(28).mean()Monthly-smoothed trend line
Exponential MATrend that reacts faster to recent changes.ewm(span=7).mean()Weighted trend line
Additive decompositionFixed seasonal amplitude regardless of trend levelseasonal_decompose(model="additive")Trend + Seasonal + Residual components
Multiplicative decompositionSeasonal amplitude grows with trend (most Indian e-commerce)seasonal_decompose(model="multiplicative")Trend × Seasonal × Residual components
Year-on-YearControl for seasonality; compare equivalent periods.pivot() then compute % changeMonthly/weekly growth % vs same period last year
ProphetBusiness forecasting with custom holidays (Diwali)Prophet().fit() + .predict()Forecast with confidence intervals + holiday effects
Residual analysisDetect anomalies — unexpected spikes or dipsresult.residIdentify months/weeks where actual ≠ trend + seasonal
Continue the Series
← Ch 15: Hypothesis TestingCh 17: A/B Testing →

Frequently Asked Questions

What makes time series analysis different from regular data analysis?

In regular cross-sectional analysis, you might compare average order values across cities — each data point is independent. In time series analysis, the ORDER of observations matters and consecutive observations are related. Today's sales depend partly on yesterday's sales, last week's, last year's. This temporal dependency means standard statistical methods (which assume independence) are not directly applicable. Time series requires special techniques: decomposition to separate trend from seasonality, autocorrelation analysis to understand lag relationships, and forecasting methods that respect the time ordering. For Indian businesses, this matters enormously — sales in October are not comparable to April without accounting for the Diwali seasonality effect. An analyst who compares October 2025 vs April 2025 and concludes October is 40% better has found the festive season, not business improvement.

How do you handle Indian seasonality like Diwali in time series?

Diwali falls on different dates each year (the Hindu lunar calendar), which makes automatic seasonal decomposition unreliable for Indian e-commerce data — standard models assume fixed seasonal patterns (same week each year). Several approaches help: (1) Create Diwali indicator features: a binary column marking the 14 days around Diwali for each year, and lag/lead features for run-up and post-festival periods. (2) Use year-on-year (YoY) comparisons instead of month-on-month — comparing this Diwali to last Diwali controls for seasonality. (3) Normalise by weeks from Diwali rather than calendar week. (4) Decompose the series after removing Diwali effects (residual analysis). Other key Indian seasonality events: financial year end (March/April) creates demand spikes in B2B; salary credit dates (1st–5th of each month) cause weekly micro-seasonality in consumer spending; IPL season (April–May) affects advertising costs; monsoon (June–September) affects certain category sales (umbrellas up, outdoor furniture down).

What is the difference between additive and multiplicative decomposition?

Both decompose a time series into three components: Trend (T), Seasonality (S), and Residual/Noise (R). The difference is how these combine: Additive: Y = T + S + R. Use when the seasonal fluctuation stays roughly constant in magnitude regardless of the level of the series. Example: a business that always sells ₹50 lakh more in October regardless of whether annual revenue is ₹5 crore or ₹10 crore. Multiplicative: Y = T × S × R. Use when seasonal fluctuation scales with the level — a business that spikes 40% every Diwali. If annual revenue doubles, the Diwali spike also roughly doubles in absolute terms. For most Indian e-commerce and FMCG businesses, multiplicative decomposition is more appropriate — the Diwali effect is percentage-based (40% uplift), not a fixed absolute amount. If your data shows seasonal swings that grow over time, use multiplicative. If the amplitude is stable, use additive.

What forecasting methods should a data analyst in India know?

For data analyst roles (not data scientist), these are the most relevant forecasting methods to know: (1) Moving Averages — simple but powerful for smoothing noise and spotting trends. Know 7-day (weekly) and 28-day (monthly) MAs. (2) Exponential Smoothing — gives more weight to recent observations. Simple Exponential Smoothing for stationary series; Holt-Winters for trend and seasonality. (3) Year-on-Year growth projection — for business forecasting: take last year's monthly actuals and apply a growth rate. Widely used in Indian companies. (4) ARIMA — the standard statistical forecasting model. Know what AR (autoregressive), I (integrated), and MA (moving average) mean conceptually, even if you use auto-ARIMA to choose parameters. Prophet (Meta's forecasting library) is very popular in India for business time series because it handles holidays (including Diwali) well. Deep learning models (LSTM) are for data science roles, not analyst-level expectation.

EVIKA ACADEMY · NOIDA SECTOR 51

Master Time Series Analysis for Indian Businesses

Our curriculum covers time series decomposition, forecasting with Prophet, and Indian seasonality analysis applied to real datasets from e-commerce, FMCG, and fintech companies.

Book Free Demo Class →
🎓 Free Demo Class — Online & Offline · Noida Sector 51