What I Learned Trying to Predict Kanpur's Air Quality

A student's attempt at next-day AQI forecasting — and why 67% accuracy taught me more than 90% would have.

Kanpur has some of the worst air in India. In winter, PM2.5 regularly crosses 200 µg/m³ — several times the WHO's safe limit. Every year, I read headlines about it. This year, I decided to try to predict it.

The question I started with was simple: can tomorrow's AQI category be predicted from today's pollution readings?

This is what I found, what surprised me, and what I'd do differently.

The data

I used real CPCB station data — about 105,000 fifteen-minute observations from Kanpur stations, aggregated to 365 daily rows. No synthetic data. The raw export from CPCB's CCR portal is messy (metadata rows above the header, inconsistent column names), so I wrote a header-sniffing function that finds the real header row and a column-alias map that normalizes names like PM2.5 (µg/m³) and pm25ugm3 to the same feature.

This part alone took longer than I expected. Real-world data is not clean. The tutorials never tell you that.

The model

I trained a Random Forest classifier to predict the next day's AQI category (Good, Satisfactory, Moderate, Poor, Very Poor, Severe). Inputs were today's pollutant readings (PM2.5, PM10, NO2, SO2, CO, O3), weather features (temperature, humidity, wind speed), today's AQI, and a cyclical encoding of the season.

A few decisions I made deliberately:

The result

Persistence baseline 61.6%
Random Forest model 67.1%

So the model beats the dumb baseline by 5.5 percentage points. That's real, but it's small. And this is where the interesting part starts.

What surprised me

Two things.

First, most of the accuracy comes from the easy cases. The confusion matrix shows the model is very good at predicting Satisfactory and Moderate days — which are most days. It almost never predicts Poor, Very Poor, or Severe correctly, because those days are rare in the data. On the days that matter most — the days when Kanpur's air is genuinely dangerous — the model is weakest.

Second, my AQI formula was wrong at first. I initially computed AQI as PM2.5 × 1.5, which is a common simplification. But real CPCB AQI is non-linear. At PM2.5 = 90, my formula gave AQI = 135 (Moderate). The real formula gives 200 (top of Moderate). At PM2.5 = 120, mine gave 180, real gives 300 (Poor). I was systematically underestimating the exact high-pollution days the model needed to learn. After fixing the formula with real CPCB breakpoints, the labels changed significantly.

What I'd do differently

  1. Add weather forecast features. Right now the model only sees today's weather. But tomorrow's temperature, wind speed, and wind direction matter enormously for AQI. If I had tomorrow's forecast as input, the model would probably improve substantially.
  2. Use a time-series model. Random Forest treats each day as independent. But AQI is highly autocorrelated — yesterday's AQI strongly predicts today's. An LSTM or a simple ARIMA model would likely beat my classifier on this task.
  3. Model the AQI value, not the category. Predicting a continuous AQI number and then converting to a category is closer to the actual physical process. Classification discards information.

Why this matters to me

The 67% number isn't the point. The point is that I now understand why the model is limited. Next-day AQI prediction is hard because it depends on weather, emissions events, and long-range transport — things yesterday's numbers can't capture alone.

That's not a failure of my model. That's a fact about the problem. And knowing that fact is more valuable than a model that gets 90% accuracy on synthetic data.

The code is here: github.com/Happ3ns/Weather-predictor.


Feedback welcome at akshatkharkwal39@gmail.com.