library(tidyverse)
library(ROSE)
library(randomForest)
library(caret)
library(e1071)
library(rpart)
library(rpart.plot)

1 Introdução

Este projeto tem como objetivo criar um modelo de classificação que preveja se uma máquina irá falhar ou não e posteriormente fazer um dashboard com os gráficos produzidos e a predição. O conjunto de dados foi fornecido S. Matzka, 2020 e está disponível no UCI. Neste conjunto, temos 10000 linhas e 10 variáveis. São elas:

  • ID: Um identificador da máquina;
  • Product ID: Um classificador com código de qualidade de produtos da máquina. Assume H, M, L (alta, média e baixa respectivamente);
  • Type: Classificador resumido de Product ID;
  • Temperatura do ar em K;
  • Temperatura do processo em K;
  • Velocidade de rotação em rpm;
  • Torque em Nm;
  • Desgaste de ferramente em min;
  • Falha: variável binária indicando se houve ou não falha;
  • Tipo de falha.

2 Metodologia

2.1 Carregamento dos dados

set.seed(1981)

maquinas <- read.csv("predictive_maintenance.csv")

summary(maquinas)
##       UDI         Product.ID            Type           Air.temperature..K.
##  Min.   :    1   Length:10000       Length:10000       Min.   :295.3      
##  1st Qu.: 2501   Class :character   Class :character   1st Qu.:298.3      
##  Median : 5000   Mode  :character   Mode  :character   Median :300.1      
##  Mean   : 5000                                         Mean   :300.0      
##  3rd Qu.: 7500                                         3rd Qu.:301.5      
##  Max.   :10000                                         Max.   :304.5      
##  Process.temperature..K. Rotational.speed..rpm.  Torque..Nm.    Tool.wear..min.
##  Min.   :305.7           Min.   :1168           Min.   : 3.80   Min.   :  0    
##  1st Qu.:308.8           1st Qu.:1423           1st Qu.:33.20   1st Qu.: 53    
##  Median :310.1           Median :1503           Median :40.10   Median :108    
##  Mean   :310.0           Mean   :1539           Mean   :39.99   Mean   :108    
##  3rd Qu.:311.1           3rd Qu.:1612           3rd Qu.:46.80   3rd Qu.:162    
##  Max.   :313.8           Max.   :2886           Max.   :76.60   Max.   :253    
##      Target       Failure.Type      
##  Min.   :0.0000   Length:10000      
##  1st Qu.:0.0000   Class :character  
##  Median :0.0000   Mode  :character  
##  Mean   :0.0339                     
##  3rd Qu.:0.0000                     
##  Max.   :1.0000

É possível notar que não há dados faltantes, mas algumas colunas não estão com o tipo de variável adequadas.

2.2 Limpeza e tratamento dos dados

# Mudando as variáveis tipo e tipo de defeito para factor

maquinas <- maquinas |> mutate(Type = as.factor(Type),
                               Target = as.factor(Target),
                               Failure.Type = as.factor(Failure.Type))


# As colunas Product ID e UDI não interessam

maquinas <- maquinas |> select(-c(UDI, Product.ID))

# Convertendo K em C

maquinas[c(2,3)] <- maquinas[c(2,3)] - 273.15

summary(maquinas)
##  Type     Air.temperature..K. Process.temperature..K. Rotational.speed..rpm.
##  H:1003   Min.   :22.15       Min.   :32.55           Min.   :1168          
##  L:6000   1st Qu.:25.15       1st Qu.:35.65           1st Qu.:1423          
##  M:2997   Median :26.95       Median :36.95           Median :1503          
##           Mean   :26.85       Mean   :36.86           Mean   :1539          
##           3rd Qu.:28.35       3rd Qu.:37.95           3rd Qu.:1612          
##           Max.   :31.35       Max.   :40.65           Max.   :2886          
##   Torque..Nm.    Tool.wear..min. Target                     Failure.Type 
##  Min.   : 3.80   Min.   :  0     0:9661   Heat Dissipation Failure: 112  
##  1st Qu.:33.20   1st Qu.: 53     1: 339   No Failure              :9652  
##  Median :40.10   Median :108              Overstrain Failure      :  78  
##  Mean   :39.99   Mean   :108              Power Failure           :  95  
##  3rd Qu.:46.80   3rd Qu.:162              Random Failures         :  18  
##  Max.   :76.60   Max.   :253              Tool Wear Failure       :  45

É importante verificar se não há incongruências entre as variáveis Target e Failure Type. Além disso, nota-se que no conjunto de dados há muito mais casos de não falha.

maquinas <- maquinas |> filter(!(Target == 0 & Failure.Type != 'No Failure'| Target == 1 & Failure.Type == 'No Failure') )
nrow(maquinas)
## [1] 9973

Como não é possível identificar o erro, as observações não-conformes foram retiradas.

2.3 Análise Exploratória de Dados

2.3.1 Distribuição dos Tipos de Falhas

cores <- c('#704C5E', '#825C6E', '#946C7E', '#B88C9E', '#F1C8DB')
cor <- '#704C5E'

maquinas |> filter(Failure.Type != 'No Failure') |> 
  ggplot(aes(x = '', fill =  fct_infreq(Failure.Type) )) +
  geom_bar(position = 'fill') +
  scale_fill_manual(values = cores) +
  labs(fill = '', x = "", y = "Percentual") +
  ggtitle('Percentual das falhas apresentadas')+
  coord_flip()+
  scale_y_continuous(labels = scales::percent) +
  annotate(geom = 'text', label = c( '13%',' 24%',' 29%','34%'), x = c( 1, 1, 1, 1), y = c (0.07, 0.25, 0.52, 0.85), color = 'white')+
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) 

2.3.2 Número de falhas por tipo

maquinas |> filter(Target == 1) |> count(Type) |> 
  ggplot(aes(x =fct_rev(fct_reorder(Type, n)) , y = n)) +
  geom_bar(stat = 'identity', fill = cor) +
  geom_text(aes(label= n %>% round(digits = 2)), vjust=1.6, color="white", size=3.5) +
  ggtitle("Numero de falhas por tipo") +
  labs(x = 'Tipo de maquina', y = 'Numero de ocorrencias')+
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) 

Existem mais falhas em máquinas com baixa qualidade de produção, seguida por média e alta qualidade.

### Dispersão Rotação vs Torque

maquinas |> filter(Target==1) |> 
  ggplot(aes(x=Rotational.speed..rpm., y=Torque..Nm.,color = Failure.Type)) +
  geom_point() +
  labs(fill = '', x = "Velocidade de Rotação (rpm)", y = "Torque (Nm)") +
  ggtitle('Tipos de Falhas por Velocidade de Rotação vs Torque ')+
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) 

Podemos obsevar que nos maiores torques ocorrem as falhas por calor, esforço e energia. Enquanto nas maiores velocidades de rotação (e menores torques) acontecem as falhas de energia. Já as falhas por desgaste de ferramenta aparecem numa faixa intermediária de torque e velocidade de rotação.

2.3.3 Boxplots das variáveis numéricas e correlação entre elas

matrizcor <- maquinas |> select_if(is.numeric) |>  cor()
corrplot::corrplot(matrizcor, method = 'color', addCoef.col = 'black',tl.srt = 45)

maquinas |> select_if(is.numeric) |> select(- Rotational.speed..rpm.)|> stack() |> 
  ggplot(aes(x = ind, y = values)) +
  geom_boxplot(fill = '#B88C9E') + 
  labs(x = '', y = '') +
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) 

maquinas |> select(Rotational.speed..rpm.) |> stack() |> 
  ggplot(aes(x = ind, y = values)) +
  geom_boxplot(fill = '#B88C9E') + 
  labs(x = '', y = '') +
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank())

Pelo gráfico de correlação podemos observar duas correlações esperadas empiricamente que são entre as temperaturas do ar e do processo e entre torque e velocidade de rotação, esta negativa. Ambas as correlações são bastante fortes. Já a variável desgaste de ferramenta não está correlacionada a nenhuma das outras colunas numéricas.

Já pelos boxplots podemos observar que a temperatura do processo é em média maior que a temperatura do ar e que o torque tem outliers à direita e à esquerda, enquanto a velocidade de rotação tem outliers apenas à direita.

2.3.4 Médias das variáveis numéricas

## Temp. do Ar

maquinas |> group_by(Target) |> summarise(media = mean(Air.temperature..K.)) |> 
  ggplot(aes(x = Target, y = media)) +
  geom_bar(stat = 'identity', fill = cor) +
  geom_text(aes(label= media %>% round(digits = 2)), vjust=1.6, color="white", size=3.5) +
  ggtitle("Médias de Temperatura do Ar") +
  labs(x = 'Falha', y = 'Média de Temperatura (°C)')+
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) 

maquinas |> group_by(Type) |> summarise(media = mean(Air.temperature..K.)) |> 
  ggplot(aes(x = Type, y = media)) +
  geom_bar(stat = 'identity', fill = cor) +
  geom_text(aes(label= media %>% round(digits = 2)), vjust=1.6, color="white", size=3.5) +
  ggtitle("Médias de Temperatura do Ar por Tipo de Máquina") +
  labs(x = 'Tipo', y = 'Média de Temperatura (°C)')+
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) 

## Temp. do Processo

maquinas |> group_by(Target) |> summarise(media = mean(Process.temperature..K.)) |> 
  ggplot(aes(x = Target, y = media)) +
  geom_bar(stat = 'identity', fill = cor) +
  geom_text(aes(label= media %>% round(digits = 2)), vjust=1.6, color="white", size=3.5) +
  ggtitle("Médias de Temperatura do Processo") +
  labs(x = 'Falha', y = 'Média de Temperatura (°C)')+
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) 

maquinas |> group_by(Type) |> summarise(media = mean(Process.temperature..K.)) |> 
  ggplot(aes(x = Type, y = media)) +
  geom_bar(stat = 'identity', fill = cor) +
  geom_text(aes(label= media %>% round(digits = 2)), vjust=1.6, color="white", size=3.5) +
  ggtitle("Médias de Temperatura do Processo por Tipo de Máquina") +
  labs(x = 'Tipo', y = 'Média de Temperatura (°C)')+
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) 

#  Velocidade de rotação

maquinas |> group_by(Target) |> summarise(media = mean(Rotational.speed..rpm.)) |> 
  ggplot(aes(x = Target, y = media)) +
  geom_bar(stat = 'identity', fill = cor) +
  geom_text(aes(label= media %>% round(digits = 2)), vjust=1.6, color="white", size=3.5) +
  ggtitle("Médias de Velocidade de Rotação do Processo") +
  labs(x = 'Falha', y = 'Média de Velocidade (rpm)')+
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank())

maquinas |> group_by(Type) |> summarise(media = mean(Rotational.speed..rpm.)) |> 
  ggplot(aes(x = Type, y = media)) +
  geom_bar(stat = 'identity', fill = cor) +
  geom_text(aes(label= media %>% round(digits = 2)), vjust=1.6, color="white", size=3.5) +
  ggtitle("Médias de Velocidade de Rotação do Processo por Tipo de Máquina") +
  labs(x = 'Tipo', y = 'Média de Velocidade (rpm)')+
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) 

# Torque

maquinas |> group_by(Target) |> summarise(media = mean(Torque..Nm.)) |> 
  ggplot(aes(x = Target, y = media)) +
  geom_bar(stat = 'identity', fill = cor) +
  geom_text(aes(label= media %>% round(digits = 2)), vjust=1.6, color="white", size=3.5) +
  ggtitle("Médias de Torque do Processo") +
  labs(x = 'Falha', y = 'Média de Torque (Nm)')+
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank())

maquinas |> group_by(Type) |> summarise(media = mean(Torque..Nm.)) |> 
  ggplot(aes(x = Type, y = media)) +
  geom_bar(stat = 'identity', fill = cor) +
  geom_text(aes(label= media %>% round(digits = 2)), vjust=1.6, color="white", size=3.5) +
  ggtitle("Médias de Torque do Processo por Tipo de Máquina") +
  labs(x = 'Tipo', y = 'Média de Torque (Nm)')+
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) 

# Tool wear

maquinas |> group_by(Target) |> summarise(media = mean(Tool.wear..min.)) |> 
  ggplot(aes(x = Target, y = media)) +
  geom_bar(stat = 'identity', fill = cor) +
  geom_text(aes(label= media %>% round(digits = 2)), vjust=1.6, color="white", size=3.5) +
  ggtitle("Médias de Desgaste de Ferramenta do Processo") +
  labs(x = 'Falha', y = 'Média de Desgaste (min)')+
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank())

maquinas |> group_by(Type) |> summarise(media = mean(Tool.wear..min.)) |> 
  ggplot(aes(x = Type, y = media)) +
  geom_bar(stat = 'identity', fill = cor) +
  geom_text(aes(label= media %>% round(digits = 2)), vjust=1.6, color="white", size=3.5) +
  ggtitle("Médias de Desgaste de Ferramenta do Processo por Tipo de Máquina") +
  labs(x = 'Tipo', y = 'Média de Desgaste (min)')+
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank())

De modo geral, podemos obsevar que o tipo de máquina não influencia nas variáveis numéricas. Já as máquinas que falharam tiveram maior desgaste de ferramenta, maior torque e menor velocidade rotação, enquanto a temperatura não parece influenciar tanto se há ou não falha.

3 Modelagem

3.1 Divisão dos Dados

maquinas <- maquinas |> mutate_if(is.numeric, scale)
maquinas <- maquinas |> select(-Failure.Type)

ind <- sample(2, nrow(maquinas), replace = TRUE, prob = c(0.7, 0.3))
train <- maquinas[ind==1,]
test <- maquinas[ind==2,]

maquinas |> group_by(Target) |> 
  summarise(n = n()) |> 
  mutate(freq = n/sum(n))
## # A tibble: 2 × 3
##   Target     n   freq
##   <fct>  <int>  <dbl>
## 1 0       9643 0.967 
## 2 1        330 0.0331
train |> group_by(Target) |> 
  summarise(n = n()) |> 
  mutate(freq = n/sum(n))
## # A tibble: 2 × 3
##   Target     n   freq
##   <fct>  <int>  <dbl>
## 1 0       6699 0.967 
## 2 1        226 0.0326
test |> group_by(Target) |> 
  summarise(n = n()) |> 
  mutate(freq = n/sum(n))
## # A tibble: 2 × 3
##   Target     n   freq
##   <fct>  <int>  <dbl>
## 1 0       2944 0.966 
## 2 1        104 0.0341

Temos um problema de desbalanceamento dos dados. No entanto, tanto os dados de treino quanto os dados de teste seguiram uma proporção próxima aos dados totais. No entanto, será utilizada a técnica de undersampling para comparação entre os modelos. Além disso, a variável tipo de falha foi retirada, pois um dos levels era justamente ’Sem Falha”, o que facilitaria muito para os modelos, deixando a análise sem sentido.

3.2 Undersampling

under <- ovun.sample(Target~., data=maquinas[-8], method = "under")$data

sample_data <-  createDataPartition(under$Target, p = 0.70, list = F)
train_data <- under[sample_data,]
test_data <- under[-sample_data,]

under |> group_by(Target) |> 
  summarise(n = n()) |> 
  mutate(freq = n/sum(n))
## # A tibble: 2 × 3
##   Target     n  freq
##   <fct>  <int> <dbl>
## 1 0        322 0.494
## 2 1        330 0.506
train_data |> group_by(Target) |> 
  summarise(n = n()) |> 
  mutate(freq = n/sum(n))
## # A tibble: 2 × 3
##   Target     n  freq
##   <fct>  <int> <dbl>
## 1 0        226 0.495
## 2 1        231 0.505
test_data |> group_by(Target) |> 
  summarise(n = n()) |> 
  mutate(freq = n/sum(n))
## # A tibble: 2 × 3
##   Target     n  freq
##   <fct>  <int> <dbl>
## 1 0         96 0.492
## 2 1         99 0.508

Assim como os dados desbalanceados, os dados de treino e teste mantém a mesma proporção.

3.3 Árvore de decisões

3.3.1 Desbalanceada

tree <- rpart(Target ~., data = train)

p <- predict(tree, test, type = 'class')
confusionMatrix(p, test$Target, positive='1', mode = 'everything')
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction    0    1
##          0 2924   29
##          1   20   75
##                                           
##                Accuracy : 0.9839          
##                  95% CI : (0.9788, 0.9881)
##     No Information Rate : 0.9659          
##     P-Value [Acc > NIR] : 8.491e-10       
##                                           
##                   Kappa : 0.7455          
##                                           
##  Mcnemar's Test P-Value : 0.2531          
##                                           
##             Sensitivity : 0.72115         
##             Specificity : 0.99321         
##          Pos Pred Value : 0.78947         
##          Neg Pred Value : 0.99018         
##               Precision : 0.78947         
##                  Recall : 0.72115         
##                      F1 : 0.75377         
##              Prevalence : 0.03412         
##          Detection Rate : 0.02461         
##    Detection Prevalence : 0.03117         
##       Balanced Accuracy : 0.85718         
##                                           
##        'Positive' Class : 1               
## 

3.3.2 Undersampling

tree <- rpart(Target ~., data = train_data)

p <- predict(tree, test_data, type = 'class')
confusionMatrix(p, test_data$Target, positive='1', mode = 'everything')
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  0  1
##          0 84  1
##          1 12 98
##                                          
##                Accuracy : 0.9333         
##                  95% CI : (0.8887, 0.964)
##     No Information Rate : 0.5077         
##     P-Value [Acc > NIR] : < 2.2e-16      
##                                          
##                   Kappa : 0.8664         
##                                          
##  Mcnemar's Test P-Value : 0.005546       
##                                          
##             Sensitivity : 0.9899         
##             Specificity : 0.8750         
##          Pos Pred Value : 0.8909         
##          Neg Pred Value : 0.9882         
##               Precision : 0.8909         
##                  Recall : 0.9899         
##                      F1 : 0.9378         
##              Prevalence : 0.5077         
##          Detection Rate : 0.5026         
##    Detection Prevalence : 0.5641         
##       Balanced Accuracy : 0.9324         
##                                          
##        'Positive' Class : 1              
## 

3.4 Naive Bayes

3.4.1 Desbalanceado

nbmodel1 <- naivebayes::naive_bayes(Target ~., data = train_data)

p <- predict(nbmodel1, test, type = 'class')
## Warning: predict.naive_bayes(): more features in the newdata are provided as
## there are probability tables in the object. Calculation is performed based on
## features to be found in the tables.
confusionMatrix(p, test$Target, positive='1', mode = 'everything')
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction    0    1
##          0 2553   18
##          1  391   86
##                                           
##                Accuracy : 0.8658          
##                  95% CI : (0.8532, 0.8777)
##     No Information Rate : 0.9659          
##     P-Value [Acc > NIR] : 1               
##                                           
##                   Kappa : 0.2543          
##                                           
##  Mcnemar's Test P-Value : <2e-16          
##                                           
##             Sensitivity : 0.82692         
##             Specificity : 0.86719         
##          Pos Pred Value : 0.18029         
##          Neg Pred Value : 0.99300         
##               Precision : 0.18029         
##                  Recall : 0.82692         
##                      F1 : 0.29604         
##              Prevalence : 0.03412         
##          Detection Rate : 0.02822         
##    Detection Prevalence : 0.15650         
##       Balanced Accuracy : 0.84706         
##                                           
##        'Positive' Class : 1               
## 

3.4.2 Undersampling

nbmodel2 <- naivebayes::naive_bayes(Target ~., data = train_data)

p <- predict(nbmodel2, test_data, type = 'class')
## Warning: predict.naive_bayes(): more features in the newdata are provided as
## there are probability tables in the object. Calculation is performed based on
## features to be found in the tables.
confusionMatrix(p, test_data$Target, positive='1', mode = 'everything')
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  0  1
##          0 82 16
##          1 14 83
##                                           
##                Accuracy : 0.8462          
##                  95% CI : (0.7877, 0.8937)
##     No Information Rate : 0.5077          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.6923          
##                                           
##  Mcnemar's Test P-Value : 0.8551          
##                                           
##             Sensitivity : 0.8384          
##             Specificity : 0.8542          
##          Pos Pred Value : 0.8557          
##          Neg Pred Value : 0.8367          
##               Precision : 0.8557          
##                  Recall : 0.8384          
##                      F1 : 0.8469          
##              Prevalence : 0.5077          
##          Detection Rate : 0.4256          
##    Detection Prevalence : 0.4974          
##       Balanced Accuracy : 0.8463          
##                                           
##        'Positive' Class : 1               
## 

3.5 Regressão Logística

3.5.1 Desbalanceado

modelorl1 <- glm(Target ~.,family=binomial(link='logit'),data=train)

p <- predict(modelorl1, test)
p <- ifelse(p > 0.51, 1, 0) |> as.factor()
confusionMatrix(p, as.factor(test$Target) , positive = '1', mode = 'everything')
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction    0    1
##          0 2941   89
##          1    3   15
##                                           
##                Accuracy : 0.9698          
##                  95% CI : (0.9631, 0.9756)
##     No Information Rate : 0.9659          
##     P-Value [Acc > NIR] : 0.1245          
##                                           
##                   Kappa : 0.2382          
##                                           
##  Mcnemar's Test P-Value : <2e-16          
##                                           
##             Sensitivity : 0.144231        
##             Specificity : 0.998981        
##          Pos Pred Value : 0.833333        
##          Neg Pred Value : 0.970627        
##               Precision : 0.833333        
##                  Recall : 0.144231        
##                      F1 : 0.245902        
##              Prevalence : 0.034121        
##          Detection Rate : 0.004921        
##    Detection Prevalence : 0.005906        
##       Balanced Accuracy : 0.571606        
##                                           
##        'Positive' Class : 1               
## 

3.5.2 Undesampling

modelorl2 <- glm(Target ~.,family=binomial(link='logit'),data=train_data)

p <- predict(modelorl2, test_data)
p <- ifelse(p > 0.51, 1, 0) |> as.factor()
confusionMatrix(p, as.factor(test_data$Target) , positive = '1', mode = 'everything')
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  0  1
##          0 85 26
##          1 11 73
##                                           
##                Accuracy : 0.8103          
##                  95% CI : (0.7481, 0.8627)
##     No Information Rate : 0.5077          
##     P-Value [Acc > NIR] : < 2e-16         
##                                           
##                   Kappa : 0.6213          
##                                           
##  Mcnemar's Test P-Value : 0.02136         
##                                           
##             Sensitivity : 0.7374          
##             Specificity : 0.8854          
##          Pos Pred Value : 0.8690          
##          Neg Pred Value : 0.7658          
##               Precision : 0.8690          
##                  Recall : 0.7374          
##                      F1 : 0.7978          
##              Prevalence : 0.5077          
##          Detection Rate : 0.3744          
##    Detection Prevalence : 0.4308          
##       Balanced Accuracy : 0.8114          
##                                           
##        'Positive' Class : 1               
## 

3.6 Considerações

Se formos considerar apenas acurácia, os modelos desbalanceados tiveram resultados um pouco superiores aos com undersampling. No entanto, as demais métricas como Kappa, TNI, F1-score e p-valor do Teste de Mcnemar mostram que faz sentido utilizar os modelos com undersampling. Assim, considerando as métricas avaliadas, o modelo escolhido é o da Árvore de Decisões com undersampling.

4 Conclusão

Este projeto apresentou de forma satisfatória um modelo de classificação de falhas em máquinas. Os modelos com undersampling mostraram-se de métricas superiores aos desbalanceados. Para um próximo projeto seria ideal testar modelos com oversampling também. A árvore de decisão teve uma performance boa em acurácia, apesar de tentar melhorar com um ajuste fino, não foi possível atingir tal resultado. O projeto em si não é complexo, mas mais uma vez o deploy foi importante no aprendizado. Aliás ele encontra-se aqui. Ficam pro futuro, usar modelos mais complexos, mostrar os ajustes de hiperparâmetros e melhorar o dashboard.