Aquesta funció unifica la validació estructural i la correcció automàtica de dades per a la llibreria openair. Està dissenyada per evitar els errors més comuns de format que fan fallar els gràfics.
Copia i enganxa aquest codi a la teva sessió d'R:
# openair_preflight v2.1 (Edició Final Unificada)
openair_preflight <- function(
df,
key = c("date", "pollutant"),
tz = "UTC",
min_days = 21,
autofix = TRUE,
verbose = TRUE
) {
# 0. Dependències internes
if (!requireNamespace("dplyr", quietly = TRUE)) stop("Cal instal·lar 'dplyr'")
if (!requireNamespace("lubridate", quietly = TRUE)) stop("Cal instal·lar 'lubridate'")
library(dplyr)
library(lubridate)
report <- list()
orig_rows <- nrow(df)
if (verbose) message("--- Iniciant Preflight ---")
# 1. Verificació de columnes obligatòries
required <- c("date", "pollutant", "value")
missing <- setdiff(required, names(df))
if(length(missing) > 0) {
stop("❌ Error Crític: Falten columnes: ", paste(missing, collapse = ", "))
}
# 2. Conversió de Tipus
# Forcem el format POSIXct necessari per openair
if(!inherits(df$date, "POSIXct")) {
if(verbose) message("⚠️ 'date' no és POSIXct -> convertint...")
df$date <- as.POSIXct(df$date, tz = tz)
}
# Forcem factor per a pollutants i numèric per a valors
df$pollutant <- as.factor(df$pollutant)
df$value <- as.numeric(as.character(df$value))
# 3. Gestió de NAs en camps clau
na_count <- sum(is.na(df$date) | is.na(df$pollutant) | is.na(df$value))
report$na_rows_removed <- na_count
if(autofix && na_count > 0) {
df <- df %>% filter(!is.na(date), !is.na(pollutant), !is.na(value))
if(verbose) message("🔧 Eliminades ", na_count, " files amb dades incompletes.")
}
# 4. Ordre Cronològic
if(is.unsorted(df$date)) {
if(autofix) {
df <- df[order(df$date), ]
if(verbose) message("🔧 Dades ordenades cronològicament.")
} else {
warning("⚠️ L'ordre de les dates no és correcte. Això pot afectar els plots de sèries temporals.")
}
}
# 5. Duplicats i Col·lapse (Lògica de mitjana)
dup_check <- df %>% count(across(all_of(key))) %>% filter(n > 1)
report$duplicate_groups <- nrow(dup_check)
if(autofix && nrow(dup_check) > 0) {
if(verbose) message("🔧 ", nrow(dup_check), " duplicats trobats -> calculant mitjana per clau...")
df <- df %>%
group_by(across(all_of(key))) %>%
summarise(value = mean(value, na.rm = TRUE), .groups = "drop")
# Netegem nivells de factors que hagin pogut quedar buits
df$pollutant <- droplevels(df$pollutant)
}
# 6. Cobertura de dades
coverage <- df %>%
group_by(pollutant) %>%
summarise(
dies_actius = n_distinct(as.Date(date)),
data_inici = min(date),
data_fi = max(date),
.groups = "drop"
)
report$coverage <- coverage
insuficient <- coverage %>% filter(dies_actius < min_days)
if(nrow(insuficient) > 0 && verbose) {
message("⚠️ Alerta: Cobertura baixa (< ", min_days, " dies) a: ",
paste(insuficient$pollutant, collapse = ", "))
}
# 7. Resum de sortida
if(verbose) {
message("✅ Preflight finalitzat amb èxit.")
message(" Files inicials: ", orig_rows)
message(" Files finals: ", nrow(df))
message(" Pollutants: ", paste(levels(df$pollutant), collapse = ", "))
}
return(list(data = df, report = report))
}
df.
res <- openair_preflight(df).
res$data per als teus anàlisis.
timeVariation(res$data, pollutant = "NO2").
key: openair_preflight(df, key = c("date", "pollutant", "site")).