Alura > Cursos de Data Science > Cursos de Data Science > Conteúdos de Data Science > Primeiras aulas do curso IA aumentada: prevendo atrasos de voos

IA aumentada: prevendo atrasos de voos

Explorando os dados - Apresentação

Olá! Eu sou o Bruno Raphaell, instrutor aqui na Alura, e vou te acompanhar neste curso de IA aumentada!

Audiodescrição: Bruno se descreve como um homem de cabelos cacheados pretos e curtos, pele negra, com a barba preta curta. Ele veste uma camisa preta, usa óculos com armação preta arredondada, e está sentado em uma cadeira preta em frente a uma parede branca iluminada em gradiente de verde, rosa e roxo, com uma estante de madeira com enfeites da Alura.

O que vamos aprender?

Você já teve algum voo atrasado ou perdeu alguma conexão devido a atraso? Esses atrasos costumam ocorrer por diversas razões, como condições climáticas, a necessidade de um avião se deslocar de um local para outro, ou imprevistos no momento do embarque dos passageiros que dificultam o embarque na hora prevista.

Os atrasos podem gerar custos tanto para as empresas quanto para os passageiros.

Diante desses cenários, fazemos parte de uma equipe de cientistas de dados que foi contratada por um aeroporto para realizar a otimização aeroportuária. Nosso objetivo é definir onde cada avião irá estacionar e, assim, construir planos de estacionamento mais adequados, rápidos e otimizados.

Para isso, sugerimos a criação de um modelo preditivo capaz de prever os atrasos nos voos e, assim, ajustar o plano de estacionamento com agilidade. Com essa solução, esperamos reduzir o tempo de espera dos passageiros e otimizar as operações do aeroporto.

Nossa primeira missão, portanto, consiste em desenvolver um modelo de machine learning, mais especificamente, um modelo de regressão. Este modelo será capaz de prever os atrasos nos voos com base em suas características. Essas previsões serão importantes, porque os outros processos de otimização utilizarão essa informação como entrada.

Para desenvolver esse modelo de machine learning, dispomos de um conjunto de dados com informações relevantes sobre cada voo. Porém, é importante ressaltar um detalhe: neste curso, não construiremos os planos de estacionamento otimizados. Vamos dar nosso primeiro passo, que é justamente construir o modelo de regressão.

Pré-requisitos

Para aproveitar ao máximo esse projeto, é importante que você tenha conhecimento na linguagem de programação Python e conheça as bibliotecas Pandas, NumPy e Seaborn.

Vamos desenvolver esse projeto juntos?

Explorando os dados - Entendendo o conjunto de dados

Podemos solucionar, ou mesmo otimizar, essa etapa de atraso de voos conhecendo-os. Sendo assim, podemos construir um modelo de machine learning que seja capaz de prever esse atraso no voo, e a saída desse modelo de machine learning poderá ser utilizada como entrada para outros processos de otimização para melhorar ainda mais a operação desse aeroporto.

Como vamos inserir esses dados em um modelo de machine learning e ele precisa de dados, é ideal conhecermos esses dados. Então, nesta aula, vamos justamente conhecer os dados com os quais trabalhamos!

Entendendo o conjunto de dados

Importação dos dados

Começaremos no Google Colab. A primeira coisa que vamos fazer será renomear o notebook como modelo-atraso-voo.ipynb para identificá-lo melhor. Em seguida, vamos importar os dados.

Clicaremos no quarto ícone na aba mais à esquerda e depois em "Fazer upload para o armazenamento da sessão", correspondente ao primeiro ícone na aba "Arquivos". Vamos procurar o conjunto de dados flights, conjunto de dados de voo.

Após clicar em "Abrir", surgirá um aviso de que ele vai ser salvo em outro lugar. Podemos clicar em "OK". Feito isso, aguardamos os dados serem carregados para o ambiente do Google Colab.

Leitura dos dados

Uma vez carregado o conjunto de dados, precisamos ler esses dados. Para isso, é necessária uma biblioteca muito interessante e muito utilizada em Data Science: a Pandas. Como fazemos essa leitura?

Primeiro, devemos importar a biblioteca com o seguinte comando:

import pandas as pd

Após importar a biblioteca, vamos executar a célula com "Shift + Enter" para criar outra célula abaixo. Agora temos que ler os dados. Esses dados estão em um formato chamado CSV. Como ler um CSV no Pandas?

Para isso, utilizamos o comando pd.read_csv(), para o qual passamos o caminho do arquivo. O arquivo está solto na pasta, então vamos simplesmente copiar o nome do arquivo e colar entre aspas simples e entre parênteses.

pd.read_csv('flights.csv')

Vamos verificar se assim funciona. Ao executar, é retornada a seguinte tabela:

#flight_idairlineaircraft_typeschengenoriginarrival_timedeparture_timedayyearis_holidaydelay
026MMAirbus A320non-schengenTCY8.88507110.88507102010False70.205981
110YEAirbus A320non-schengenTCY8.93999611.93999602010False38.484609
23BZEmbraer E175schengenTZF18.63538422.63538402010False2.388305
328BZAirbus A330non-schengenEMM15.96796317.96796302010False19.138491
415BZAirbus A330non-schengenFJB16.57189419.57189402010False15.016271

Nosso conjunto de dados está lido e trazido para o Google Colab.

Atribuição a uma variável

Agora vamos atribuir o que escrevemos na célula anterior a uma variável específica chamada dados. Para conferir que deu certo, vamos visualizar as 5 primeiras linhas usando o comando head().

dados = pd.read_csv('flights.csv')
dados.head()

Vamos executar novamente com "Shift + Enter". Teremos o mesmo conjunto de dados apresentado acima, e já percebemos que conseguimos tirar algumas informações. Temos uma coluna chamada flight_id (identificador do voo); a airline (companhia aérea); o tipo da aeronave, que é aircraft_type; schengen, que indica se o voo é do espaço Schengen ou não.

O termo "Schengen" está relacionado com o Acordo de Schengen, um acordo europeu que tem implicações em termos de logística aeroportuária, mas não está diretamente relacionado com as operações aeroportuárias em si.

O aeroporto de origem (origin) está em siglas. Por exemplo: o aeroporto de Congonhas recebe a sigla CGH. Temos o arrival_time (hora de chegada), departure_time (hora de saída), o dia (day), e o ano que é correspondente a esse dia (year). Por exemplo: temos o dia 0 em 2010, então é 1º de janeiro de 2010.

Por fim, temos a coluna is_holiday, se é um feriado ou não, e a coluna delay, o atraso em minutos.

Importante notar que a hora em arrival_time está em formato decimal. Por exemplo: como seria 8.88 em horas? Poderíamos fazer um cálculo simples. Seria 0.88 multiplicado por 60 para saber a quantidade em minutos. Teríamos, basicamente, 8 horas e 52 minutos.

O mesmo processo se repete para departure_time. Multiplicamos os números após a vírgula por 60 para saber a quantidade de minutos. Teremos, assim, o tempo tanto em horas quanto em minutos.

Visualizando as últimas linhas

Já conseguimos visualizar as linhas e sabemos o que é cada uma das colunas. O que podemos fazer agora é visualizar as últimas linhas. Verificar, por exemplo, quantas amostras temos no nosso conjunto de dados. Para isso, podemos usar o comando dados.tail(). Assim, visualizamos as últimas 5 amostras.

dados.tail()
#flight_idairlineaircraft_typeschengenoriginarrival_timedeparture_timedayyearis_holidaydelay
711703BZEmbraer E175schengenTZF18.63538422.6353843642022True25.677277
7117117BZAirbus A330non-schengenCNU16.71872221.7187223642022True52.624348
711727MMBoeing 787schengenTZF8.56494913.5649493642022True56.167080
711735BZAirbus A320schengenZQO9.34409712.3440973642022True56.758844
7117429BZBoeing 737schengenZQO8.59120811.5912083642022True41.401648

Percebemos que vai até a linha 71.174. Como começa em 0, consequentemente, temos 71.175 amostras no nosso conjunto de dados. Outra coisa que podemos fazer é visualizar a quantidade de colunas.

Visualizando a quantidade de colunas

Para evitar contar manualmente, utilizamos o método shape, então digitamos dados.shape em uma nova célula.

dados.shape

Retorno da célula:

(71175, 11)

Como retorno, temos exatamente o que acabamos de executar: 71.175 linhas e 11 colunas ao todo. Já conseguimos ter essas duas informações, mas não conseguimos ter informações e estatísticas mais descritivas. Por exemplo: qual o valor médio das colunas numéricas? Qual o valor máximo? Qual a mediana? Não temos essas informações só com o que executamos.

Para obter essas informações, vamos executar o método describe(), ou seja, dados.describe() em uma nova célula.

dados.describe()

Executando com "Shift + Enter", inicialmente, serão dadas somente informações das colunas numéricas:

#flight_idarrival_timedeparture_timedayyeardelay
count71175.00000071175.00000071175.00000071175.00000071175.00000071175.000000
mean15.46513513.28315916.480222182.0000002016.00000012.548378
std8.6496464.0233804.143705105.3667693.74168423.125349
min1.0000007.06559410.0655940.0000002010.000000-41.028033
25%8.0000008.93999612.66865591.0000002013.000000-4.412876
50%15.00000014.25891116.376052182.0000002016.0000009.740454
75%23.00000016.90969020.041281273.0000002019.00000027.650853
max30.00000019.34123523.341235364.0000002022.000000125.632352

Percebemos que não há 11 colunas, justamente porque estão retiradas as colunas categóricas. Temos flight_id, arrival_time, departure_time, day, year e delay. Não faz tanto sentido analisar a coluna flight_id, pois é um identificador. Então, vamos partir para o arrival_time.

Em arrival_time, temos: a média (mean); o desvio padrão (std); o valor mínimo (min); o primeiro quartil (25%); a mediana (50%), que é o valor que está no meio do conjunto de dados, nesse caso, 14.25; o terceiro quartil (75%), que é 16.9; e o valor máximo (max) que temos de arrival_time, ou seja, o maior valor que um avião chegou nesse aeroporto foi às 19 horas e 34 minutos. Multiplicamos por 60 para saber a quantidade exata de minutos.

Em compensação, o valor máximo do departure_time, isto é, a hora de saída, é 23.34. Multiplicamos novamente por 60 para saber a quantidade exata de minutos. Temos o valor médio 16 para o departure_time e o valor médio 13 para o arrival_time, por exemplo, e temos essas mesmas estatísticas para as outras colunas.

Analisando a coluna delay

Como nossa variável alvo, isto é, o problema de negócio com os dados que tentamos resolver é relacionado ao atraso, precisamos analisar a coluna delay. A média do atraso é 12 minutos, o desvio padrão é 23, o valor mínimo é -41, ou seja, houve voos que adiantaram, e o valor máximo é de 125 minutos, ou seja, houve aviões que atrasaram até 2 horas e 5 minutos, o que é bastante tempo. Como fazer para visualizar estatísticas descritivas para variáveis categóricas?

Podemos executar o mesmo método. Vamos copiar dados.describe(), colar em uma nova célula abaixo, e adicionar o parâmetro include que será igual a 'O'.

dados.describe(include='O')

Serão dadas as quatro colunas que são categóricas:

#airlineaircraft_typeschengenorigin
count71175711757117571175
unique36210
topBZAirbus A320schengenTZF
freq47598307784256914162

Primeiramente, temos a coluna airline, que é a companhia aérea. São três companhias aéreas diferentes, e a que mais aparece (top) é a BZ, com a frequência (freq) de 47598. Podemos replicar essas análises para as outras três colunas. Assim, conseguimos ter uma análise mais descritiva de nossas colunas categóricas.

Extraindo mais informações

Já conseguimos extrair bastante informação com os métodos que executamos, mas há outro método que fornece informações bastante relevantes: o método info(). Vamos executar em uma nova célula dados.info().

dados.info()

Executando esse método, temos a seguinte saída:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 71175 entries, 0 to 71174
Data columns (total 11 columns):
 #   Column          Non-Null Count  Dtype  
---  ------          --------------  -----  
 0   flight_id       71175 non-null  int64  
 1   airline         71175 non-null  object 
 2   aircraft_type   71175 non-null  object 
 3   schengen        71175 non-null  object 
 4   origin          71175 non-null  object 
 5   arrival_time    71175 non-null  float64
 6   departure_time  71175 non-null  float64
 7   day             71175 non-null  int64  
 8   year            71175 non-null  int64  
 9   is_holiday      71175 non-null  bool   
 10  delay           71175 non-null  float64
dtypes: bool(1), float64(3), int64(3), object(4)
memory usage: 5.5+ MB

Temos o RangeIndex, que indica a quantidade de linhas do conjunto de dados, que varia de 0 a 71.174; bem como o número de colunas, totalizando 11. Nesse caso, temos duas informações muito relevantes: as colunas Non-Null Count, que é a quantidade de valores não nulos, e Dtype, que é o tipo da coluna.

Percebemos que todas as colunas têm 71.175 valores não nulos. Isso indica que não há dados nulos no nosso conjunto de dados, o que significa que não precisamos nos preocupar com esse tipo de tratamento.

Outra coisa que percebemos é o tipo dos dados. Todos os tipos estão correspondentes com as informações, então também não precisamos nos preocupar com a conversão do tipo da coluna para a tipagem correta, pois já estão na tipagem correta.

Conclusão

Conseguimos tirar essas duas informações que são muito relevantes. Porém, construímos uma análise estatística basicamente com números. Para visualizar as informações de forma mais abrangente, o ideal construir gráficos!

Explorando os dados - Visualizações gráficas dos dados

Construímos nossas análises estatísticas dos dados, mas para termos uma análise ainda mais abrangente, o ideal é construir análises gráficas. É isso que vamos fazer neste vídeo!

Visualizações gráficas dos dados

Começaremos no ambiente de desenvolvimento do Google Colab, a partir do momento que deixamos no vídeo passado. Deixamos uma célula de markdown pré-pronta, com o título "Visualizações gráficas dos dados", para deixar nosso notebook mais organizado, e também preparamos uma célula de código para digitarmos.

Como vamos trabalhar com gráficos, o ideal é trazer bibliotecas que trabalhem com gráficos em Python. Vamos trabalhar com duas: Matplotlib e Seaborn. Então, vamos importá-las para o nosso ambiente. Para isso, fazemos import matplotlib.pyplot as plt, que é o apelido dessa biblioteca na comunidade, e import seaborn as sns, que também é o apelido da biblioteca. Vamos executar a célula com ""Shift + Enter"".

import matplotlib.pyplot as plt
import seaborn as sns

Agora as bibliotecas estão no nosso ambiente de desenvolvimento. Na próxima célula, vamos colar um código pré-pronto. O ideal é que você pause o vídeo, copie o código para o seu notebook, e depois retorne para explicarmos linha a linha.

average_delay = dados.groupby('airline')['delay'].mean().reset_index()
sns.barplot(x='airline', y='delay', data=average_delay)
plt.title('Companhias aéreas vs atrasos médios')
plt.xlabel('Companhias aéreas')
plt.ylabel('Atraso médio em minutos')
plt.show()

sns.countplot(data=dados, x='airline')
plt.title('Número de voos por companhia aérea')
plt.xlabel('Companhia aérea')
plt.ylabel('Número de voos')
plt.show()

Primeiro gráfico

Na primeira linha, onde temos average_delay, nós agrupamos pela companhia aérea, porque queremos construir um gráfico de atrasos médios por companhia aérea. Como queremos saber a média do atraso, usamos ['delay'].mean().

Para ficar mais evidente o que fizemos, vamos criar uma nova célula acima, colar a primeira linha do código e imprimi-la digitando average_delay na segunda linha da célula.

average_delay = dados.groupby('airline')['delay'].mean().reset_index()
average_delay

Ao executar essa célula com ""Shift + Enter"", percebemos que é retornado um DataFrame.

#airlinedelay
0BZ3.077595
1MM40.498007
2YE25.772248

Qual a necessidade do reset_index()? Se não o colocássemos, seria retornada uma series, e queremos trabalhar com esse conjunto de dados como um DataFrame. Por isso, usamos o .reset_index(). Assim, temos um DataFrame e conseguimos trabalhar de forma mais fácil, referenciando as colunas airline e delay.

Podemos excluir a nova célula criada e retornar para a anterior.

Na linha seguinte, temos o método sns.barplot(), onde no eixo x adicionamos airline, no eixo y, delay, e em data, adicionamos o DataFrame average_delay que mostramos anteriormente.

Em seguida, adicionamos o título "Companhias aéreas vs atrasos médios", nomeando o eixo x como "Companhias aéreas" e o eixo y como "Atraso médios em minutos". Na última linha do primeiro gráfico, imprimimos com plt.show().

Segundo gráfico

Outro gráfico que queremos construir é o número de voos por companhia aérea. Para isso, usamos o comando sns.countplot(), que vai fazer uma contagem dos valores da coluna airline, com os dados que trabalhamos, que é o DataFrame airline que importamos. O título é "Número de voos por companhia aérea", com o eixo x sendo "Companhia aérea" e o eixo y "Número de voos".

Ao executar a célula com ""Shift + Enter"", obtemos os seguintes gráficos:

Gráfico de barras verticais intitulado 'Companhias aéreas vs atrasos médios', com o eixo x rotulado como 'Companhias aéreas' e o eixo y como 'Atraso médio em minutos'. O eixo x plota as companhias 'BZ', 'MM', e 'YE', representadas, respectivamente, por uma barra azul, outra laranja e outra verde. No eixo y, são plotados os minutos de 0 a 40 em intervalos de 5. A barra azul está entre 0 e 5, a barra laranja em aproximadamente 40, e a barra verde em aproximadamente 25.Gráfico de barras verticais intitulado 'Número de voos por companhia aérea', com o eixo x rotulado como 'Companhia aérea' e o eixo y como 'Número de voos'. O eixo x plota as companhias 'MM', 'YE', e 'BZ', representadas, respectivamente, por uma barra azul, outra laranja e outra verde. No eixo y, são plotados os voos de 0 a 40000 em intervalos de 10000. A barra azul está em aproximadamente 10000, a barra laranja entre 10000 e 20000, e a barra verde acima de 40000.

No primeiro gráfico, percebemos que a companhia aérea com mais atraso médio é a MM, seguida por YE e por último a BZ. Já no segundo gráfico, mesmo a BZ tendo o menor atraso, é a companhia aérea que mais tem voos, com mais de 40.000 voos, seguida por YE e por último MM. Ou seja, a MM é a que tem menos voos e atrasa mais.

Terceiro e quarto gráficos

Agora, vamos construir um gráfico do tipo de voo, ou seja, se é Schengen ou não. Vamos copiar o código da célula anterior, mas em vez de agrupar por airline, agruparemos por schengen, e ajustaremos os títulos e eixos conforme o contexto.

Para começar, vamos selecionar airline dentro de groupby() e usar o atalho "Ctrl + Shift + L" para selecionar todas as ocorrências de airline na célula e substituir por schengen. Assim, fizemos a alteração necessária tanto em barplot() quanto em countplot().

Quanto aos títulos, no lugar de "Companhias aéreas vs atrasos médios" teremos "Tipo do voo vs atrasos médios", com "Tipo do voo" no eixo x e "Atraso médio em minutos" no eixo y. Da mesma forma, no lugar de "Número de voos por companhia aérea" teremos "Número de voos por tipo do voo", com "Tipo do voo" no eixo x e "Número de voos" no eixo y.

average_delay = dados.groupby('schengen')['delay'].mean().reset_index()
sns.barplot(x='schengen', y='delay', data=average_delay)
plt.title('Tipo do voo vs atrasos médios')
plt.xlabel('Tipo do voo')
plt.ylabel('Atraso médio em minutos')
plt.show()

sns.countplot(data=dados, x='schengen')
plt.title('Número de voos por tipo do voo')
plt.xlabel('Tipo do voo')
plt.ylabel('Número de voos')
plt.show()

Executando a célula com "Shift + Enter", temos os gráficos abaixo:

Gráfico de barras verticais intitulado 'Tipo do voo vs atrasos médios', com o eixo x rotulado como 'Tipo do voo' e o eixo y como 'Atraso médio em minutos'. O eixo x plota os tipos 'non-schengen' e 'schengen', representados, respectivamente, por uma barra azul e outra laranja. No eixo y, são plotados os minutos de 0 a 16 em intervalos de 2. A barra azul está entre 15 e 16, enquanto a barra laranja está entre 10 e 12.Gráfico de barras verticais intitulado 'Número de voos por tipo do voo', com o eixo x rotulado como 'Tipo do voo' e o eixo y como 'Número de voos'. O eixo x plota os tipos 'non-schengen' e 'schengen', representados, respectivamente, por uma barra azul e outra laranja. No eixo y, são plotados os voos de 0 a 40000 em intervalos de 5000. A barra azul está próxima de 30000, enquanto a barra laranja está acima de 40000.

No primeiro gráfico, percebemos que os voos fora do espaço Schengen tendem a atrasar mais, em média 15 minutos, enquanto os que são do espaço Schengen atrasam em média 10 minutos.

Entretanto, mesmo o tipo do voo sendo non-schengen, eles aparecem em menos quantidade no conjunto de dados, e ainda assim são os que atrasam mais. Enquanto isso, os voos do tipo schengen aparecem em muita quantidade; em compensação, atrasam menos.

Quinto gráfico

A seguir, vamos analisar se os atrasos em feriados são maiores ou menores do que em dias não feriados. Para isso, criaremos uma nova célula e ajustaremos o código anterior para a coluna is_holiday, alterando títulos e eixos para refletir a análise de feriado versus atraso médio.

average_delay = dados.groupby('is_holiday')['delay'].mean().reset_index()
sns.barplot(x='is_holiday', y='delay', data=average_delay)
plt.title('Feriado vs atrasos médios')
plt.xlabel('É feriado?')
plt.ylabel('Atraso médio em minutos')
plt.show()

Ao executar a célula com "Shift + Enter", observamos que, durante os feriados, os voos tendem a atrasar mais.

Gráfico de barras verticais intitulado 'Feriado vs atrasos médios', com o eixo x rotulado como 'É feriado?' e o eixo y como 'Atraso médio em minutos'. O eixo x plota os valores 'False' e 'True', representados, respectivamente, por uma barra azul e outra laranja. No eixo y, são plotados os minutos de 0 a 40 em intervalos de 5. A barra azul em aproximadamente 10, enquanto a barra laranja está acima de 40.

Sexto gráfico

Outra análise que podemos é sobre o tipo de aeronave. Pegamos o segundo gráfico que construímos, um countplot(), e adicionamos em uma nova célula. Queremos saber a contagem de amostras por tipo de aeronave. Alteramos o eixo x para aircraft_type e ajustamos os títulos e eixos. Para organizar o gráfico, adicionamos uma ordenação decrescente e rotacionamos os nomes das aeronaves em 70 graus com plt.xticks(rotation=70).

order = dados['aircraft_type'].value_counts().index
sns.countplot(data=dados, x='aircraft_type', order = order)
plt.title('Número de voos por tipo da aeronave')
plt.xticks(rotation=70)
plt.xlabel('Tipo da aeronave')
plt.ylabel('Número de voos')
plt.show()
Gráfico de barras verticais intitulado 'Número de voos por tipo de aeronave', com o eixo x rotulado como 'Tipo da aeronave' e o eixo y como 'Número de voos'. O eixo x plota os tipos 'Airbus A320', 'Airbus A330', 'Boeing 787', 'Embraer E175', 'Boeing 737' e 'Boeing 777', representados, respectivamente, por barras em azul, laranja, verde, vermelho, lilás e marrom. No eixo y, são plotados os voos de 0 a 30000 em intervalos de 5000. As barras estão com os respectivos valores, da esquerda para a direita: 30000, 16000, 10000, 7000, 5000 e 3000.

Conclusão

Ao final, conseguimos construir gráficos descritivos das nossas informações e extrair informações relevantes do nosso conjunto de dados. Resta visualizar a distribuição das variáveis, incluindo a variável alvo, para analisarmos como essa variável se comporta!

Sobre o curso IA aumentada: prevendo atrasos de voos

O curso IA aumentada: prevendo atrasos de voos possui 128 minutos de vídeos, em um total de 43 atividades. Gostou? Conheça nossos outros cursos de Data Science em Data Science, ou leia nossos artigos de Data Science.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Data Science acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas