Análise do Consumo de Combustível - Aerometrics

Modelagem Preditiva e Processamento de Dados

Levi Gurgel de Lima, Yves Gabriel Q. de Sousa, Marco Aurélio C. Risardi

Introdução

A Aerometrics desenvolveu um sistema preditivo para estimar o consumo de combustível a partir de dados históricos de operações aéreas.

Nesta apresentação serão mostrados:

Setup e Estrutura dos Dados

library(tidyverse)
library(lubridate)
library(e1071)
library(neuralnet)
library(caret)
library(tree)

set.seed(123)

load("data_project_train.Rda")
load("data_project_test.Rda")
## # A tibble: 6 × 13
##   flight_id orig  dest  dep_time            arr_time            airline
##       <dbl> <fct> <fct> <dttm>              <dttm>              <fct>  
## 1 816911099 SBSP  SBNF  2023-06-12 22:25:43 2023-06-12 23:08:13 TAM    
## 2 848573075 SBPA  SBSP  2023-10-25 15:01:19 2023-10-25 16:22:48 TAM    
## 3 822515328 SBVT  SBGR  2023-07-06 23:02:27 2023-07-07 00:31:17 TAM    
## 4 853338898 SBRJ  SBPA  2023-11-17 00:49:03 2023-11-17 02:29:31 TAM    
## 5 848627219 SBSP  SBLO  2023-10-25 19:51:39 2023-10-25 20:39:28 TAM    
## 6 797381975 SBSP  SBBR  2023-03-15 09:11:29 2023-03-15 10:27:30 TAM    
## # ℹ 7 more variables: aircraft_type <fct>, flown_distance_enr <dbl>,
## #   flight_duration <dbl>, kpi_inefficiency_dep <dbl>,
## #   kpi_inefficiency_enr <dbl>, kpi_inefficiency_arr <dbl>, fuel_burn <dbl>

Feature Engineering

Nesta etapa realizamos a criação de novas variáveis derivadas das informações brutas do dataset.
A engenharia de atributos é fundamental para melhorar a representação dos dados e aumentar o poder preditivo dos modelos.

As principais transformações incluem:

Esses atributos aprimoram a capacidade dos modelos em capturar padrões relevantes associados ao consumo de combustível.

Feature Engineering - Dataset de Treinamento

df_train <- data_train %>%
  mutate(
    avg_speed = flown_distance_enr / (flight_duration / 60),        # NM/h
    fuel_per_nm = fuel_burn / flown_distance_enr,
    fuel_per_min = fuel_burn / flight_duration,
    total_ineff = kpi_inefficiency_dep + kpi_inefficiency_arr,
    flight_range = case_when(
      flown_distance_enr < 400 ~ "Curto",
      flown_distance_enr < 800 ~ "Médio",
      TRUE ~ "Longo"
    ),
    flight_range = factor(flight_range, levels = c('Curto', 'Médio', 'Longo')),
    dep_hour = hour(dep_time),
    dep_period = case_when(
      dep_hour >= 5 & dep_hour < 12 ~ "Manhã",
      dep_hour >= 12 & dep_hour < 18 ~ "Tarde",
      TRUE ~ "Noite"
    ),
    dep_period = factor(dep_period, levels = c("Manhã", "Tarde", "Noite"))
  )

Feature Engineering - Dataset de Teste

# Aplicar o mesmo processo à base de teste (sem fuel_burn)
df_test <- data_test %>%
  mutate(
    avg_speed = flown_distance_enr / (flight_duration / 60),
    total_ineff = kpi_inefficiency_dep + kpi_inefficiency_arr,
    flight_range = case_when(
      flown_distance_enr < 400 ~ "Curto",
      flown_distance_enr < 800 ~ "Médio",
      TRUE ~ "Longo"
    ),
    flight_range = factor(flight_range, levels = c('Curto', 'Médio', 'Longo')),
    dep_hour = hour(dep_time),
    dep_period = case_when(
      dep_hour >= 5 & dep_hour < 12 ~ "Manhã",
      dep_hour >= 12 & dep_hour < 18 ~ "Tarde",
      TRUE ~ "Noite"
    ),
    dep_period = factor(dep_period, levels = c("Manhã", "Tarde", "Noite"))
  )

Análise Exploratória

A análise exploratória tem como finalidade compreender o comportamento dos dados de voo e identificar padrões que influenciam o consumo de combustível.

Principais objetivos:

Análise Exploratória

Análise Exploratória

Análise Exploratória

Análise Exploratória

Análise Exploratória

Modelo de Rede Neural

A Aerometrics utilizou uma rede neural simples com função de ativação tanh para prever consumo de combustível em escala logaritmizada.

PCA – Redução de Dimensionalidade

Rede Neural - Treinamento

data_train_pc <- data.frame(pca_out$x)
data_train_pc$fuel_burn <- data_train$fuel_burn %>% log1p() %>% scale()
nn_model <- neuralnet(fuel_burn ~ PC1 + PC2 + PC3, data=data_train_pc, hidden=c(3), 
                      act.fct='tanh', linear.output=TRUE)

Rede Neural

## RMSE (Rede Neural) = 0.08932497

Modelo de Árvore de Decisão

Também treinamos um modelo baseado em árvore de decisão, que permite interpretar a influência das variáveis de forma mais direta e intuitiva.

Código

tree_model <- tree(
fuel_burn ~ airline + aircraft_type + flown_distance_enr +
flight_duration + total_ineff + flight_range + dep_hour,
df_train, mindev=0.001
)
## [1] "RMSE (Árvore): 0.0690004290673126"

Árvore de Decisão

Árvore de Decisão

Validação Cruzada - Árvore

Ensemble - Combinação NN + Árvore

df_ensemble <- data.frame(fuel_burn = data_train$fuel_burn, nn = y_pred_nn, dt = y_pred_dt)
model_ensemble <- lm(fuel_burn ~ nn + dt, df_ensemble)
summary(model_ensemble)
## 
## Call:
## lm(formula = fuel_burn ~ nn + dt, data = df_ensemble)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -2030.0  -208.5   -11.7   176.4 10169.8 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -36.887662  10.461805  -3.526 0.000425 ***
## nn            0.150088   0.008179  18.351  < 2e-16 ***
## dt            0.859152   0.007923 108.434  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 423.3 on 7276 degrees of freedom
## Multiple R-squared:  0.9726, Adjusted R-squared:  0.9726 
## F-statistic: 1.294e+05 on 2 and 7276 DF,  p-value: < 2.2e-16

Ensemble: Combinação dos Modelos

Com o objetivo de melhorar a capacidade preditiva final, foi criado um modelo ensemble linear combinando:

Os pesos foram estimados por regressão linear (LM), permitindo que o ensemble aprenda automaticamente qual modelo tem maior poder explicativo.

Resultado

data_train$prediction_ensemble <- predict(model_ensemble, df_ensemble)

mean(abs(data_train$fuel_burn - data_train$prediction_ensemble) / data_train$fuel_burn)
## [1] 0.06470415

Previsões do Conjunto de Teste

Após treinar e calibrar, aplicamos o pipeline completo ao conjunto de teste:

mean_train <- mean(log1p(data_train$fuel_burn))
sd_train   <- sd(log1p(data_train$fuel_burn))

x_test <- subset(data_test, select=c(8:12))
data_test_pc <- predict(pca_out, newdata=x_test) |> as.data.frame()
data_test_pc$fuel_burn <- (log1p(data_test$fuel_burn) - mean_train) / sd_train

y_test_scaled <- predict(nn_model, data_test_pc)
y_pred_nn <- expm1(y_test_scaled * sd_train + mean_train)
y_pred_dt <- predict(tree_model, df_test)

w <- coef(model_ensemble)
data_test$fuel_burn <- w["nn"] * y_pred_nn + w["dt"] * y_pred_dt

Conclusões