Programming for Data Processing

Lectures: Nguyễn Tuấn Phong, Đào Việt Anh

Labs: Trịnh Ngọc Huỳnh, Phạm Tiến Du

Semester: 2025-2026-1

Institute for AI, VNU-UET

Cute panda time series illustration

Lecture 8
Time Series
Data Processing

Today

  • From parsing → resampling → rolling → TZ/DST → outliers
  • Live notebooks: Real-world data for Hanoi/Vietnam (link) + Household Power Consumption (link)
  • Focus: clean indices, correct frequency, reliable aggregates

Why Processing Time Series?

  • Many signals are time-ordered: apps, sensors, finance, operations
  • Order reveals trends, seasonality, autocorrelation
  • Clean time axes → reliable KPIs, monitoring, forecasts
  • Real-world wrinkles: TZ/DST, gaps, duplicates, irregular sampling

What Goes Wrong Without It?

  • Duplicate or unsorted timestamps break asfreq() & rolling
  • Hidden gaps bias aggregates after resample()
  • Bad TZ/DST handling shifts data or drops hours
  • Random (non-time) splits cause leakage
  • Imputation over long spans collapses variance
  • Mixing asfreq and resample intents leads to silent errors

Date & Time Basics

  • Parse with pd.to_datetime(..., errors="coerce")
  • Set DateTimeIndex; always sort_index()
  • Keep raw strings until parsing is verified

Indexing & Duplicates

  • Time slicing: s["2022-10":"2022-12"]
  • Detect duplicates: .index.duplicated()
  • Resolve: groupby(level=0).mean() or drop_duplicates(keep="last")

Date Ranges & Frequencies

  • Generate with pd.date_range
  • Common codes: D, H, T, MS, QS
  • Shift values with s.shift(k) for lags

Time Zones & DST

  • Localize tz-naive: Raw data did not specify timezone, then use tz_localize("Europe/Paris")
  • Convert tz-aware: Raw data used a different timezone, then use tz_convert("Asia/Ho_Chi_Minh")
  • Daylight Saving Time (DST): handle nonexistent & ambiguous times

Upsampling vs Downsampling

  • Upsample: increase frequency (e.g., daily → hourly)
  • Downsample: decrease frequency (e.g., minute → hourly)
  • Two main methods: .asfreq() vs .resample()

.asfreq()

Reveal / Align to a Fixed Grid

  • Reindex to a target frequency without aggregation
  • Preserves values at exact timestamps; others become NaN
  • Use to expose gaps, prep interpolate("time") / ffill(), align multiple series
  • Typical: hourly → 15min grid, then impute

.resample()

Aggregate / Change Granularity

  • Bins by time; apply aggregations like mean(), sum()
  • Downsample: e.g., 1min → 15min with mean()
  • Upsample: prefer .asfreq() + impute (aggregation alone won’t create values)
  • Compare: asfreq = align grid (no math); resample(...).mean() = within-bin summary

Grouped Resampling & Calendar Features

  • Weekly/monthly views: resample("W").sum()
  • Per-key groups: df.groupby("city").resample("D").mean()
  • Calendar: index.dayofweek, index.month

Takeaways

  • Rule of thumb:
    • asfreq to align/expose gaps, then interpolate/ffill
    • resample to summarize/change granularity
  • Be explicit about intent: asfreq() ≠ mean()

Moving Windows

Capture trends & local patterns

  • Row-based: rolling(30, min_periods=15)
  • Time-based: rolling("30D")
  • Use center=True when visual trend matters

Missing Data & Imputation

  • Reveal gaps: asfreq("D")
  • Fill: ffill, bfill, interpolate("time")
  • Beware variance collapse on long spans

Outliers

  • Local rule: |z| > 3 with rolling mean/std
  • Another option: rolling MAD
  • Guard: ignore tiny sd windows

Wrap-up

  • Parse → DateTimeIndex → TZ/DST policy
  • Reveal gaps → asfreq vs resample
  • Impute sparingly → rolling
  • Outliers in context → Baselines before models