Muitas das transformações dos dados utilizadas neste notebook foram aprendidas neste kernel
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import classification_report,confusion_matrix
2. Importando DataSet disponível no Kaggle
Para os exemplos seguintes funcionarem você deve baixar o detaset no link acima e salvar na mesma pasta do projeto com o nome de: BrFlights2.csv
df = pd.read_csv('BrFlights2.csv', encoding='latin1')
3. Normalizando e traduzindo nomes das colunas e criando tabela para checagem dos tipos de variáveis
df.columns = ['Flights', 'Airline', 'Flight_Type','Departure_Estimate','Departure_Real','Arrival_Estimate','Arrival_Real','Flight_Situation','Code_Justification','Origin_Airport','Origin_City','Origin_State','Origin_Country','Destination_Airport','Destination_City','Destination_State','Destination_Country','Destination_Long','Destination_Lat','Origin_Long','Origin_Lat']
tab_info=pd.DataFrame(df.dtypes).T.rename(index={0:'column type'})
tab_info=tab_info.append(pd.DataFrame(df.isnull().sum()).T.rename(index={0:'null values'}))
tab_info=tab_info.append(pd.DataFrame(df.isnull().sum()/df.shape[0]*100).T.
rename(index={0:'null values (%)'}))
tab_info
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
Flights | Airline | Flight_Type | Departure_Estimate | Departure_Real | Arrival_Estimate | Arrival_Real | Flight_Situation | Code_Justification | Origin_Airport | ... | Origin_State | Origin_Country | Destination_Airport | Destination_City | Destination_State | Destination_Country | Destination_Long | Destination_Lat | Origin_Long | Origin_Lat | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
column type | object | object | object | object | object | object | object | object | object | object | ... | object | object | object | object | object | object | float64 | float64 | float64 | float64 |
null values | 0 | 0 | 0 | 0 | 289196 | 0 | 289196 | 0 | 1510212 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
null values (%) | 0 | 0 | 0 | 0 | 11.3744 | 0 | 11.3744 | 0 | 59.3983 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
3 rows × 21 columns
...
Constatamos que Departure_Real e Arrival_Real tem cerca de 11% de valores nulos.
Deletaremos esse valores no passo seguinte (Ignorando a coluna Code_Justification e dropando as ,'tuplas' com algum valor nulo)
....
df_time = df[['Flights', 'Airline', 'Flight_Type', 'Departure_Estimate',
'Departure_Real', 'Arrival_Estimate', 'Arrival_Real',
'Flight_Situation', 'Origin_Airport',
'Origin_City', 'Origin_State', 'Origin_Country', 'Destination_Airport',
'Destination_City', 'Destination_State', 'Destination_Country',
'Destination_Long', 'Destination_Lat', 'Origin_Long', 'Origin_Lat']]
df_time.dropna(how='any',inplace=True)
df_time['Departure_Estimate'] = pd.to_datetime(df_time['Departure_Estimate'])
df_time['Departure_Real'] = pd.to_datetime(df_time['Departure_Real'])
df_time['Arrival_Estimate'] = pd.to_datetime(df_time['Arrival_Estimate'])
df_time['Arrival_Real'] = pd.to_datetime(df_time['Arrival_Real'])
df_time['Departure_Delays'] =df_time.Departure_Real - df_time.Departure_Estimate
df_time['Arrival_Delays'] = df_time.Arrival_Real - df_time.Arrival_Estimate
df_time['Departure_Delays'] = df_time['Departure_Delays'].apply(lambda x : round(x.total_seconds()/60))
df_time['Arrival_Delays'] = df_time['Arrival_Delays'].apply(lambda x : round(x.total_seconds()/60))
Estudaremos os atrasos nas Chegadas dos voos
df_time['ArrivalStatus'] = ""
df_time.loc[df_time.Arrival_Delays > 0 , 'ArrivalStatus'] = "Atrasado"
df_time.loc[df_time.Arrival_Delays < 0 , 'ArrivalStatus'] = "Adiantado"
df_time.loc[df_time.Arrival_Delays == 0 , 'ArrivalStatus'] = "Pontual"
df_time.head(10)
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
Flights | Airline | Flight_Type | Departure_Estimate | Departure_Real | Arrival_Estimate | Arrival_Real | Flight_Situation | Origin_Airport | Origin_City | ... | Destination_City | Destination_State | Destination_Country | Destination_Long | Destination_Lat | Origin_Long | Origin_Lat | Departure_Delays | Arrival_Delays | ArrivalStatus | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | AAL - 203 | AMERICAN AIRLINES INC | Internacional | 2016-01-30 08:58:00+00:00 | 2016-01-30 08:58:00+00:00 | 2016-01-30 10:35:00+00:00 | 2016-01-30 10:35:00+00:00 | Realizado | Afonso Pena | Sao Jose Dos Pinhais | ... | Porto Alegre | RS | Brasil | -51.175381 | -29.993473 | -49.172481 | -25.532713 | 0 | 0 | Pontual |
1 | AAL - 203 | AMERICAN AIRLINES INC | Internacional | 2016-01-13 12:13:00+00:00 | 2016-01-13 12:13:00+00:00 | 2016-01-13 21:30:00+00:00 | 2016-01-13 21:30:00+00:00 | Realizado | Salgado Filho | Porto Alegre | ... | Miami | N/I | Estados Unidos | -80.287046 | 25.795865 | -51.175381 | -29.993473 | 0 | 0 | Pontual |
2 | AAL - 203 | AMERICAN AIRLINES INC | Internacional | 2016-01-29 12:13:00+00:00 | 2016-01-29 12:13:00+00:00 | 2016-01-29 21:30:00+00:00 | 2016-01-29 21:30:00+00:00 | Realizado | Salgado Filho | Porto Alegre | ... | Miami | N/I | Estados Unidos | -80.287046 | 25.795865 | -51.175381 | -29.993473 | 0 | 0 | Pontual |
3 | AAL - 203 | AMERICAN AIRLINES INC | Internacional | 2016-01-19 12:13:00+00:00 | 2016-01-18 12:03:00+00:00 | 2016-01-19 21:30:00+00:00 | 2016-01-18 20:41:00+00:00 | Realizado | Salgado Filho | Porto Alegre | ... | Miami | N/I | Estados Unidos | -80.287046 | 25.795865 | -51.175381 | -29.993473 | -1450 | -1489 | Adiantado |
4 | AAL - 203 | AMERICAN AIRLINES INC | Internacional | 2016-01-30 12:13:00+00:00 | 2016-01-30 12:13:00+00:00 | 2016-01-30 21:30:00+00:00 | 2016-01-30 21:30:00+00:00 | Realizado | Salgado Filho | Porto Alegre | ... | Miami | N/I | Estados Unidos | -80.287046 | 25.795865 | -51.175381 | -29.993473 | 0 | 0 | Pontual |
5 | AAL - 203 | AMERICAN AIRLINES INC | Internacional | 2016-01-03 23:05:00+00:00 | 2016-01-03 23:05:00+00:00 | 2016-01-04 07:50:00+00:00 | 2016-01-04 07:50:00+00:00 | Realizado | Miami | Miami | ... | Sao Jose Dos Pinhais | PR | Brasil | -49.172481 | -25.532713 | -80.287046 | 25.795865 | 0 | 0 | Pontual |
6 | AAL - 203 | AMERICAN AIRLINES INC | Internacional | 2016-01-05 23:05:00+00:00 | 2016-01-05 23:35:00+00:00 | 2016-01-06 07:50:00+00:00 | 2016-01-06 08:35:00+00:00 | Realizado | Miami | Miami | ... | Sao Jose Dos Pinhais | PR | Brasil | -49.172481 | -25.532713 | -80.287046 | 25.795865 | 30 | 45 | Atrasado |
7 | AAL - 203 | AMERICAN AIRLINES INC | Internacional | 2016-01-18 12:13:00+00:00 | 2016-01-18 13:09:00+00:00 | 2016-01-18 21:30:00+00:00 | 2016-01-18 22:24:00+00:00 | Realizado | Salgado Filho | Porto Alegre | ... | Miami | N/I | Estados Unidos | -80.287046 | 25.795865 | -51.175381 | -29.993473 | 56 | 54 | Atrasado |
8 | AAL - 203 | AMERICAN AIRLINES INC | Internacional | 2016-01-22 23:05:00+00:00 | 2016-01-22 23:05:00+00:00 | 2016-01-23 07:50:00+00:00 | 2016-01-23 07:50:00+00:00 | Realizado | Miami | Miami | ... | Sao Jose Dos Pinhais | PR | Brasil | -49.172481 | -25.532713 | -80.287046 | 25.795865 | 0 | 0 | Pontual |
9 | AAL - 203 | AMERICAN AIRLINES INC | Internacional | 2016-01-15 23:05:00+00:00 | 2016-01-15 23:55:00+00:00 | 2016-01-16 07:50:00+00:00 | 2016-01-16 08:28:00+00:00 | Realizado | Miami | Miami | ... | Sao Jose Dos Pinhais | PR | Brasil | -49.172481 | -25.532713 | -80.287046 | 25.795865 | 50 | 38 | Atrasado |
10 rows × 23 columns
Pronto, já temos nosso Dataset importado num dataframe com todas as informações que precisamos... Na verdade veremos futuramente se precisaremos mesmo de todas essas informações...
Seria interessante excluir colunas como Departure_Real e Departure_Delay pois essas informações tornam obvio saber se o voo atrasou ou não... Discutiremos isso mais pra frente
Nosso Próximo Passo agora vai ser extratificar o dataframe em outros dois dataframes um pra treinamento da rede neural e um para validação da nossa rede, cada um destes dataframes vai ser ainda separado em duas Colunas uma com todas as informações e outra somente com a nossa classe (ArrivalStatus)
Extratificar significa que separaremos os dados na proporção que eles tem de valores da nossa classe objetivo, por exemplo, se temos 50% de Voos Pontuais, 40% de atrasos e 10% de adiantamentos os nosso dois dataframes gerados terão essas proporções.
df_time.shape
(2253323, 23)
train, test = train_test_split(df_time, test_size=0.3, stratify=df_time['ArrivalStatus'])
atrasados = train.groupby('ArrivalStatus').get_group('Atrasado').count()[0]
adiantados = train.groupby('ArrivalStatus').get_group('Adiantado').count()[0]
pontuais = train.groupby('ArrivalStatus').get_group('Pontual').count()[0]
total = atrasados + adiantados + pontuais
colunas = ['Situation','%']
train_info = [[atrasados/total*100],[adiantados/total*100],[pontuais/total*100]]
pd.DataFrame(train_info, index=['Atrasados','Adiantados','Pontuais'], columns = ['Percent'])
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
Percent | |
---|---|
Atrasados | 15.508969 |
Adiantados | 18.276501 |
Pontuais | 66.214530 |
atrasados = test.groupby('ArrivalStatus').get_group('Atrasado').count()[0]
adiantados = test.groupby('ArrivalStatus').get_group('Adiantado').count()[0]
pontuais = test.groupby('ArrivalStatus').get_group('Pontual').count()[0]
total = atrasados + adiantados + pontuais
colunas = ['Situation','%']
test_info = [[atrasados/total*100],[adiantados/total*100],[pontuais/total*100]]
pd.DataFrame(test_info, index=['Atrasados','Adiantados','Pontuais'], columns = ['Percent'])
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
Percent | |
---|---|
Atrasados | 15.508945 |
Adiantados | 18.276560 |
Pontuais | 66.214495 |
Dessa forma temos em ambos os DataFrames aproximadamente 17% de Voos Adiantados, 16% de Voos Atrasados e 68% de Voos Pontuais
train.shape
(1577326, 23)
X_train = train.iloc[:,:22]
Y_train = train.iloc[:,22:]
X_test = test.iloc[:,:22]
Y_test = test.iloc[:,22:]
X_train.shape
(1577326, 22)
Y_train.shape
(1577326, 1)
X_test.shape
(675997, 22)
Y_test.shape
(675997, 1)
O Algoritmo que utilizaremos só aceita variáveis numéricas na origem, dessa forma, o jeito mais fácil, só pra testar o modelo, foi excluir todas,é claro que isso colocou a eficiência da nossa rede lá embaixo... O que precisamos fazer agora é:
É difícil prever a diferença que cada variável faria, mas podemos começar definindo as mais óbvias como data e cia aérea por exemplo...
Definidas quais variáveis usaremos devemos ver a melhor técnica para transformá-la em conteúdo que pode ser processado pela RN (ou seja, números)
Conheco duas, uma é basicamente pegar os caracteres e transformar em números, porém para esta técnica os dados devem possuir algum valor semântico quando transformados em números (funciona para data e hora por exemplo)
A outra técnica é para dados que não tem valor semântico quando transformados em algarismos (como nomes, por exemplo). Essa técnica é chamada de Dummies, que é bassicamente montar uma tabela onde as colunas teriam os nomes das Cias Aéreas, por exemplo, as linhas seriam os voos e a célula (LinhaXColuna) seria preenchida com 1 se o voo pertence àquela cia aérea e com 0 caso não pertença. Isso impacta diretamente no problema seguinte:
Depois de sabermos exatamente o tamanho da nossa entrada (quantas colunas terão nas nossas tabelas X) precisamos definir quantas camadas e quantos neurônios em cada camadas teremos.
Existem alguns estudos sobre o assunto, mas é um ponto que não é uma unânimidade na área. Podemos tomar como base este artigo
#Mes
#X_Train_mes = pd.DataFrame(X_train[['Departure_Estimate']])
#X_Train_mes['Departure_Estimate'] = X_Train_mes['Departure_Estimate'].dt.month
#look_up = {1: 'Jan', 2: 'Feb', 3: 'Mar', 4: 'Apr', 5: 'May', 6: 'Jun', 7: 'Jul', 8: 'Aug', 9: 'Sep', 10: 'Oct', 11: 'Nov', 12: 'Dec'}
#X_Train_mes['Departure_Estimate'] = X_Train_mes['Departure_Estimate'].apply(lambda x: look_up[x])
#X_Train_mes = pd.get_dummies(X_Train_mes)
#X_Train_mes.head()
#Pegando os valores relevantes
New_X_train = X_train[['Destination_Long', 'Destination_Lat', 'Origin_Long', 'Origin_Lat','Departure_Delays']]
#Transformando Valores relevantes em dados calculáveis
############## Treino
#Linha aérea
Dummies_X_train = X_train[['Airline']]
Dummies_X_train = pd.get_dummies(Dummies_X_train)
#Dia da Semana
X_data = X_train[['Departure_Estimate']]
X_data['Dia_Semana'] = X_data['Departure_Estimate'].dt.weekday_name
#Mes
X_Train_mes = pd.DataFrame(X_train[['Departure_Estimate']])
X_Train_mes['Departure_Estimate'] = X_Train_mes['Departure_Estimate'].dt.month
look_up = {1: 'Jan', 2: 'Feb', 3: 'Mar', 4: 'Apr', 5: 'May', 6: 'Jun', 7: 'Jul', 8: 'Aug', 9: 'Sep', 10: 'Oct', 11: 'Nov', 12: 'Dec'}
X_Train_mes['Departure_Estimate'] = X_Train_mes['Departure_Estimate'].apply(lambda x: look_up[x])
X_Train_mes = pd.get_dummies(X_Train_mes)
#Hora
X_hora = pd.DataFrame(X_data['Departure_Estimate'].dt.hour)
X_data = X_data['Dia_Semana']
X_hora.columns = ['Hora']
X_hora = X_hora['Hora'].apply(str)
X_hora = pd.get_dummies(X_hora)
Dummies_X_Data = pd.get_dummies(X_data)
X_train = pd.concat([New_X_train, Dummies_X_train, Dummies_X_Data, X_hora,X_Train_mes], axis=1)
############## Teste
New_X_test = X_test[['Destination_Long', 'Destination_Lat', 'Origin_Long', 'Origin_Lat','Departure_Delays']]
Dummies_X_test = X_test[['Airline']]
Dummies_X_test = pd.get_dummies(Dummies_X_test)
#Dia da Semana
X_datat = X_test[['Departure_Estimate']]
X_datat['Dia_Semana'] = X_datat['Departure_Estimate'].dt.weekday_name
#Mes
X_test_mes = pd.DataFrame(X_test[['Departure_Estimate']])
X_test_mes['Departure_Estimate'] = X_test_mes['Departure_Estimate'].dt.month
#look_up = {1: 'Jan', 2: 'Feb', 3: 'Mar', 4: 'Apr', 5: 'May', 6: 'Jun', 7: 'Jul', 8: 'Aug', 9: 'Sep', 10: 'Oct', 11: 'Nov', 12: 'Dec'}
X_test_mes['Departure_Estimate'] = X_test_mes['Departure_Estimate'].apply(lambda x: look_up[x])
X_test_mes = pd.get_dummies(X_test_mes)
#Hora
X_horat = pd.DataFrame(X_datat['Departure_Estimate'].dt.hour)
X_datat = X_datat['Dia_Semana']
X_horat.columns = ['Hora']
X_horat = X_horat['Hora'].apply(str)
X_horat = pd.get_dummies(X_horat)
Dummies_X_Datat = pd.get_dummies(X_datat)
X_test = pd.concat([New_X_test, Dummies_X_test, Dummies_X_Datat, X_horat,X_test_mes], axis=1)
X_test_mes.head()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
Departure_Estimate_Apr | Departure_Estimate_Aug | Departure_Estimate_Dec | Departure_Estimate_Feb | Departure_Estimate_Jan | Departure_Estimate_Jul | Departure_Estimate_Jun | Departure_Estimate_Mar | Departure_Estimate_May | Departure_Estimate_Nov | Departure_Estimate_Oct | Departure_Estimate_Sep | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
1687115 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
1783207 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2154287 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2074967 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
535615 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
#X_test_mes['mes'] = pd.DataFrame(X_datat['Departure_Estimate'].dt.month)
#X_test_mes.head()
#X_test_mes['Departure_Estimate'] = X_test_mes.apply(lambda x: look_up[x])
X_train.shape
(1577326, 103)
X_test.shape
(675997, 103)
X_train.dropna(how='any',inplace=True)
X_train.shape
(1577326, 103)
X_test.dropna(how='any',inplace=True)
X_test.shape
(675997, 103)
X_train.head()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
Destination_Long | Destination_Lat | Origin_Long | Origin_Lat | Departure_Delays | Airline_AEROLINEAS ARGENTINAS | Airline_AEROMEXICO | Airline_AIR CANADA | Airline_AIR CHINA | Airline_AIR EUROPA S/A | ... | Departure_Estimate_Dec | Departure_Estimate_Feb | Departure_Estimate_Jan | Departure_Estimate_Jul | Departure_Estimate_Jun | Departure_Estimate_Mar | Departure_Estimate_May | Departure_Estimate_Nov | Departure_Estimate_Oct | Departure_Estimate_Sep | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1584578 | -53.700874 | -29.707958 | -51.175381 | -29.993473 | 46 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
485150 | -56.117269 | -15.653079 | -47.917235 | -15.869737 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
1050608 | -48.545966 | -27.670118 | -46.656584 | -23.627325 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
90584 | -87.907321 | 41.974162 | -46.478126 | -23.434553 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
204307 | -47.917235 | -15.869737 | -51.175381 | -29.993473 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
5 rows × 103 columns
X_test.head()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
Destination_Long | Destination_Lat | Origin_Long | Origin_Lat | Departure_Delays | Airline_AEROLINEAS ARGENTINAS | Airline_AEROMEXICO | Airline_AIR CANADA | Airline_AIR CHINA | Airline_AIR EUROPA S/A | ... | Departure_Estimate_Dec | Departure_Estimate_Feb | Departure_Estimate_Jan | Departure_Estimate_Jul | Departure_Estimate_Jun | Departure_Estimate_Mar | Departure_Estimate_May | Departure_Estimate_Nov | Departure_Estimate_Oct | Departure_Estimate_Sep | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1687115 | -46.656584 | -23.627325 | -43.965396 | -19.634099 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
1783207 | -43.249423 | -22.813410 | -46.656584 | -23.627325 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2154287 | -47.917235 | -15.869737 | -34.950614 | -7.147060 | 31 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2074967 | -38.331241 | -12.911098 | -38.533097 | -3.777156 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
535615 | -57.514181 | -25.241513 | -46.478126 | -23.434553 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
5 rows × 103 columns
#Definir arquitetura:
mlp = MLPClassifier(activation='relu', alpha=0.0001, batch_size='auto', beta_1=0.9,
beta_2=0.999, early_stopping=False, epsilon=1e-08,
hidden_layer_sizes=(9, 7, 5), learning_rate='constant',
learning_rate_init=0.001, max_iter=200, momentum=0.9,
nesterovs_momentum=True, power_t=0.5, random_state=None,
shuffle=True, solver='adam', tol=0.0001, validation_fraction=0.1,
verbose=False, warm_start=False)
#Magic....
mlp.fit(X_train,Y_train)
#Nostradamus mode on...
predictions = mlp.predict(X_test)
#Plotando nosso resultado...
print(classification_report(Y_test,predictions))
precision recall f1-score support
Adiantado 0.93 0.91 0.92 123549
Atrasado 0.95 0.89 0.92 104840
Pontual 0.98 1.00 0.99 447608
accuracy 0.97 675997
macro avg 0.95 0.93 0.94 675997
weighted avg 0.97 0.97 0.97 675997