1. Introduction

Urban air pollution is an important smart-city issue because it affects public health, transport planning, and environmental management. This project explores air-quality and weather measurements in the TRAQID dataset using R, tidyverse, and ggplot2.

2. Objectives

  1. Prepare and validate the dataset for analysis.
  2. Explore air-quality patterns across seasons and day/night periods.
  3. Visualize AQI, particulate matter, and weather variables with ggplot2.
  4. Examine correlations and fit an exploratory regression model.
  5. Summarize findings and limitations for a smart-city dashboard.

3. Dataset Description

The supplied TRAQID.csv contains traffic-related air-quality observations with timestamps, PM2.5, PM10, temperature, humidity, AQI, season, day/night period, AQI category, and image identifiers. It contains 26,678 rows and 10 original columns. The period spans 22 October 2022 to 9 July 2024.

4. Data Preparation

The workflow parses created_at as date-time, derives date and hour fields, checks missing values, removes exact duplicate rows, and writes a cleaned CSV. Implausible temperature values are flagged separately rather than silently removed. Any filtered analysis should disclose the threshold and rationale.

library(tidyverse)
library(lubridate)
raw <- read_csv("data/TRAQID.csv", show_col_types = FALSE)
data <- raw %>%
  mutate(created_at = ymd_hms(created_at),
         date = as.Date(created_at),
         hour = hour(created_at),
         Season = factor(Season, levels = c("Summer","Monsoon","Winter")),
         Day_or_Night = factor(Day_or_Night, levels = c("Day","Night")),
         aqi_cat = factor(aqi_cat,
           levels = c("Good","Satisfactory","Moderate","Poor","Very Poor","Severe"))) %>%
  distinct()
glimpse(data)
## Rows: 26,678
## Columns: 12
## $ created_at   <dttm> 2022-10-22 18:00:00, 2022-10-22 18:00:00, 2022-10-22 18:…
## $ Image        <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17…
## $ PM2.5        <dbl> 26.115, 26.823, 26.823, 27.118, 26.882, 26.764, 27.000, 2…
## $ PM10         <dbl> 41.0112, 41.9240, 41.3698, 41.0438, 40.1962, 39.5116, 40.…
## $ Temperature  <dbl> 26.95, 26.96, 26.98, 26.99, 26.96, 26.97, 26.97, 26.93, 2…
## $ Humidity     <dbl> 43.451, 43.463, 43.566, 43.598, 43.676, 43.757, 43.859, 4…
## $ aqi          <dbl> 43.52500, 44.70500, 44.70500, 45.19667, 44.80333, 44.6066…
## $ Season       <fct> Monsoon, Monsoon, Monsoon, Monsoon, Monsoon, Monsoon, Mon…
## $ Day_or_Night <fct> Night, Night, Night, Night, Night, Night, Night, Night, N…
## $ aqi_cat      <fct> Good, Good, Good, Good, Good, Good, Good, Good, Good, Goo…
## $ date         <date> 2022-10-22, 2022-10-22, 2022-10-22, 2022-10-22, 2022-10-…
## $ hour         <int> 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 1…
summarise(data, rows=n(), missing_cells=sum(is.na(across(everything()))))
## # A tibble: 1 × 2
##    rows missing_cells
##   <int>         <int>
## 1 26678             0

5. Exploratory Data Analysis

data %>% count(Season)
## # A tibble: 3 × 2
##   Season      n
##   <fct>   <int>
## 1 Summer   5901
## 2 Monsoon 10567
## 3 Winter  10210
data %>% count(Day_or_Night)
## # A tibble: 2 × 2
##   Day_or_Night     n
##   <fct>        <int>
## 1 Day          13789
## 2 Night        12889
data %>% count(aqi_cat)
## # A tibble: 6 × 2
##   aqi_cat          n
##   <fct>        <int>
## 1 Good           744
## 2 Satisfactory 13033
## 3 Moderate      7453
## 4 Poor          2283
## 5 Very Poor     2233
## 6 Severe         932
summary(select(data, `PM2.5`, PM10, Temperature, Humidity, aqi))
##      PM2.5             PM10         Temperature        Humidity     
##  Min.   : 12.92   Min.   : 32.18   Min.   :-93.47   Min.   : 9.408  
##  1st Qu.: 39.09   1st Qu.: 57.77   1st Qu.: 28.17   1st Qu.:29.630  
##  Median : 52.37   Median : 72.73   Median : 30.94   Median :38.177  
##  Mean   : 66.57   Mean   :126.18   Mean   : 31.01   Mean   :38.602  
##  3rd Qu.: 73.44   3rd Qu.:157.02   3rd Qu.: 33.74   3rd Qu.:46.527  
##  Max.   :400.86   Max.   :669.85   Max.   :181.37   Max.   :76.167  
##       aqi        
##  Min.   : 36.55  
##  1st Qu.: 69.78  
##  Median : 97.12  
##  Mean   :143.60  
##  3rd Qu.:171.32  
##  Max.   :599.95

6. Visualizations

data %>% group_by(Season) %>%
  summarise(mean_aqi=mean(aqi, na.rm=TRUE), .groups="drop") %>%
  ggplot(aes(Season, mean_aqi, fill=Season)) + geom_col() +
  labs(title="Average AQI by Season", x="Season", y="Mean AQI") + theme_minimal()

data %>% group_by(Day_or_Night) %>%
  summarise(mean_aqi=mean(aqi, na.rm=TRUE), .groups="drop") %>%
  ggplot(aes(Day_or_Night, mean_aqi, fill=Day_or_Night)) + geom_col() +
  labs(title="Average AQI: Day vs Night", x="Period", y="Mean AQI") + theme_minimal()

data %>% count(aqi_cat) %>%
  ggplot(aes(aqi_cat, n, fill=aqi_cat)) + geom_col() +
  labs(title="Distribution of AQI Categories", x="AQI category", y="Observations") +
  theme_minimal() + theme(axis.text.x=element_text(angle=25,hjust=1))

ggplot(data, aes(`PM2.5`, aqi)) + geom_point(alpha=.2) +
  geom_smooth(method="lm", se=TRUE) +
  labs(title="PM2.5 vs AQI", x="PM2.5", y="AQI") + theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

daily <- data %>% group_by(date) %>% summarise(mean_aqi=mean(aqi, na.rm=TRUE), .groups="drop")
ggplot(daily, aes(date, mean_aqi)) + geom_line() + geom_smooth(se=FALSE, method="loess") +
  labs(title="Daily Average AQI Over Time", x="Date", y="Mean AQI") + theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

cor_data <- data %>% select(`PM2.5`, PM10, Temperature, Humidity, aqi)
cor_long <- cor(cor_data, use="pairwise.complete.obs") %>% as.data.frame() %>%
  rownames_to_column("variable") %>% pivot_longer(-variable, names_to="variable2", values_to="r")
ggplot(cor_long, aes(variable, variable2, fill=r)) + geom_tile() +
  geom_text(aes(label=round(r,2))) + scale_fill_gradient2() +
  labs(title="Correlation Matrix", x=NULL, y=NULL, fill="Pearson r") + theme_minimal()

The visualizations show the seasonal and day/night distribution of AQI, AQI category frequencies, particulate matter association with AQI, daily change over time, and pairwise numeric correlations. Exact interpretations should be tied to the generated output and should not be treated as causal claims.

7. Statistical Analysis / Model

cor(select(data, `PM2.5`, PM10, Temperature, Humidity, aqi), use="complete.obs")
##                   PM2.5       PM10 Temperature   Humidity         aqi
## PM2.5        1.00000000  0.7511374 -0.05117279 -0.1433395  0.92203749
## PM10         0.75113743  1.0000000  0.11375038 -0.3635268  0.89363236
## Temperature -0.05117279  0.1137504  1.00000000 -0.4295664  0.02610646
## Humidity    -0.14333949 -0.3635268 -0.42956642  1.0000000 -0.23453439
## aqi          0.92203749  0.8936324  0.02610646 -0.2345344  1.00000000
model <- lm(aqi ~ `PM2.5` + PM10 + Temperature + Humidity + Season + Day_or_Night, data=data)
summary(model)
## 
## Call:
## lm(formula = aqi ~ PM2.5 + PM10 + Temperature + Humidity + Season + 
##     Day_or_Night, data = data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -201.416  -11.306   -4.568    4.764  240.035 
## 
## Coefficients:
##                     Estimate Std. Error t value Pr(>|t|)    
## (Intercept)       -17.877300   1.617687 -11.051  < 2e-16 ***
## PM2.5               1.333489   0.005560 239.827  < 2e-16 ***
## PM10                0.457367   0.002634 173.614  < 2e-16 ***
## Temperature         0.298580   0.034896   8.556  < 2e-16 ***
## Humidity            0.098826   0.017976   5.498 3.89e-08 ***
## SeasonMonsoon       4.162078   0.698923   5.955 2.63e-09 ***
## SeasonWinter       -2.215632   0.540589  -4.099 4.17e-05 ***
## Day_or_NightNight   2.313597   0.363742   6.361 2.04e-10 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 25.82 on 26670 degrees of freedom
## Multiple R-squared:  0.9439, Adjusted R-squared:  0.9439 
## F-statistic: 6.415e+04 on 7 and 26670 DF,  p-value: < 2.2e-16

The linear model estimates associations between AQI and particulate/weather/time variables. It is exploratory: correlated predictors, temporal dependence, and unusual sensor values may affect coefficients. It does not establish causality.

8. Major Findings

The data contain substantial variation in AQI and particulate measurements. Seasonal and day/night comparisons can identify patterns worth investigating. AQI category counts show that observations are distributed across multiple air-quality levels. The PM2.5–AQI plot and correlation matrix help quantify relationships among the measured variables. Confirm the exact numerical interpretation after running the report.

9. Conclusion

This project demonstrates a reproducible R workflow for preparing urban air-quality observations, exploring temporal and categorical patterns, visualizing relationships with ggplot2, and fitting an exploratory statistical model. Future work could join a separate traffic-volume or public-transport dataset by compatible time/location fields, if suitable matched records are available.

10. Individual Contributions

11. References

  1. Kathalkar, O., Nilesh, N., Chaudhari, S., and Namboodiri, A. TRAQID: Traffic-Related Air Quality Image Dataset. ICVGIP 2024. DOI: 10.1145/3702250.3702260.
  2. TRAQID project page: https://omkathalkar.github.io/traqid/
  3. R Core Team. R: A Language and Environment for Statistical Computing. https://www.r-project.org/
  4. Wickham, H. ggplot2: Elegant Graphics for Data Analysis. https://ggplot2.tidyverse.org/