🛫 Openair Preflight amb Autofix (R)

Funció de verificació prèvia per assegurar que un dataframe és segur abans d’utilitzar openair. Ara amb autofix integrat.

📦 Funció openair_preflight() amb Autofix

✅ Detecta i arregla problemes segurs automàticament

openair_preflight <- function(
  df,
  key = c("date","pollutant"),
  tz = "UTC",
  min_days = 21,
  autofix = TRUE,
  verbose = TRUE
) {
  library(dplyr)
  library(lubridate)

  report <- list()

  # 1️⃣ Columnes bàsiques
  required_cols <- c("date", "pollutant", "value")
  missing_cols <- setdiff(required_cols, names(df))
  if(length(missing_cols) > 0) stop("❌ Falten columnes: ", paste(missing_cols, collapse = ", "))
  report$columns <- "OK"

  # 2️⃣ Tipus de dades
  if(!inherits(df$date,"POSIXct")) {
    if(verbose) message("⚠️ 'date' no és POSIXct → convertint")
    df$date <- as.POSIXct(df$date, tz=tz)
  }
  df$pollutant <- as.factor(df$pollutant)
  df$value     <- as.numeric(df$value)
  report$types <- "OK"

  # 3️⃣ NA crítics
  na_rows <- sum(is.na(df$date) | is.na(df$pollutant) | is.na(df$value))
  report$na_rows <- na_rows
  if(na_rows>0 & verbose) message("⚠️ Files amb NA crítics: ", na_rows)

  # 4️⃣ Autofix segur
  if(autofix) {
    # elimina NA crítics
    df <- df %>% filter(!is.na(date), !is.na(pollutant), !is.na(value))
    # ordena dates
    if(is.unsorted(df$date)) {
      df <- df[order(df$date),]
      if(verbose) message("🔧 Dates ordenades")
    }
    # factors nets
    df$pollutant <- droplevels(df$pollutant)
    # duplicats exactes
    dup <- df %>% count(across(all_of(key))) %>% filter(n>1)
    if(nrow(dup)>0) {
      if(verbose) message("🔧 Duplicats exactes detectats: ", nrow(dup), " → col·lapsant")
      df <- df %>% group_by(across(all_of(key))) %>%
        summarise(value = mean(value), .groups="drop")
    }
  }

  # 5️⃣ Duplicats reals
  dup_check <- df %>% count(across(all_of(key))) %>% filter(n>1)
  report$duplicate_groups <- nrow(dup_check)
  if(nrow(dup_check)>0 & verbose) warning("❌ Duplicats reals detectats (clau: ", paste(key, collapse="+"), ")")

  # 6️⃣ Cobertura temporal
  coverage <- df %>% group_by(pollutant) %>%
    summarise(ndays=n_distinct(as.Date(date)), .groups="drop")
  report$coverage <- coverage
  if(any(coverage$ndays < min_days) & verbose) message("⚠️ Pollutants amb poca cobertura temporal")

  # 7️⃣ Informe final
  if(verbose) {
    message("✅ Preflight complet")
    message("   Files finals: ", nrow(df))
    message("   Pollutants: ", paste(levels(df$pollutant), collapse=", "))
  }

  return(list(data=df, report=report))
}
👉 Autofix aplica només coses segures: ordena dates, elimina NA crítics, neteja factors, col·lapsa duplicats exactes.

▶️ Ús recomanat


# 1️⃣ Executa preflight amb autofix
pf <- openair_preflight(df, autofix = TRUE)

# 2️⃣ Agafa dades netes
df_ok <- pf$data

# 3️⃣ Consulta report
pf$report

# 4️⃣ Crida openair amb seguretat
timeVariation(mydata = df_ok, pollutant = "NO2")
✅ Tot en una línia: detecta problemes i aplica fixes segurs automàticament.

🧠 Què detecta i arregla

✔ Columnes necessàries
✔ Tipus correctes (POSIXct, factor, numeric)
✔ NA crítics (elimina files)
✔ Ordre temporal (ordenació)
✔ Factors nets (droplevels)
✔ Duplicats exactes (col·lapsa amb mitjana)
✔ Cobertura mínima per pollutant (alerta)

🧭 Filosofia

Preflight amb Autofix ≠ neteja agressiva
Detecta problemes i arregla només els segurs abans que openair falli.
Si el preflight passa → els plots són fiables