Alura > Cursos de Data Science > Cursos de Machine Learning > Conteúdos de Machine Learning > Primeiras aulas do curso Séries temporais: detectando anomalias e realizando previsões

Séries temporais: detectando anomalias e realizando previsões

Entendendo o problema - Apresentação

Olá, tudo bem? Me chamo João Miranda, sou instrutor aqui na Alura e irei te acompanhar ao longo deste curso.

Audiodescrição: João se delcara um homem branco, com cabelo e barba curtos. Veste uma camisa azul lisa. Ao fundo, há uma parede com uma iluminação que vai do verde ao azul.

Você já teve contato com dados que variam ao longo do tempo e não soube como analisá-los?

O que vamos aprender?

Neste curso, construiremos um projeto prático de séries temporais utilizando dados da quantidade de clientes em uma rede de restaurantes ao longo do tempo.

A partir desses dados, extrair insights para aprimorar o planejamento dessa rede de restaurantes, detectando pontos de anomalia e fazendo previsões para períodos posteriores, para otimizar a alocação de pessoas atendentes.

Não realizaremos comparações nem avaliações de desempenho de modelos. O foco será na exploração de dados e na análise de séries temporais.

Pré-requisitos

Para acompanhar o conteúdo, é recomendado que você tenha conhecimento na linguagem de programação Python e na biblioteca Pandas.

Vamos começar?

Entendendo o problema - Fazendo a leitura dos dados

A rede de restaurantes está enfrentando dificuldades no planejamento do negócio. Em alguns dias, a quantidade de clientes é muito baixa, resultando em pessoas atendentes ociosas. Em outros dias, a quantidade de clientes é muito alta, tornando insuficiente a quantidade de pessoas atendentes para atender toda a demanda.

Como cientistas de dados, podemos ajudar essa rede de restaurantes a planejar melhor o negócio e alocar a quantidade ideal de pessoas atendentes para a quantidade de clientes ao longo dos dias.

Essa rede de restaurantes coleta informações sobre a quantidade de clientes que visitam os restaurantes ao longo dos dias e armazena essas informações em um arquivo CSV. Esse arquivo estará disponível na atividade "Preparando Ambiente".

É importante fazer o download dessa base de dados na atividade "Preparando Ambiente" antes de continuar com este vídeo.

Vamos utilizar o Google Colab e a linguagem Python para analisar esses dados e extrair insights para ajudar essa rede de restaurantes a planejar melhor o negócio.

Upload do arquivo CSV

O primeiro passo será fazer o upload do arquivo CSV para o ambiente do Google Colab.

Para fazer isso, abrimos o menu lateral esquerdo "Arquivos", clicamos no primeiro botão na parte superior denominado "Fazer Upload para Armazenamento da Sessão" e selecionamos o arquivo disponível na atividade "Preparando Ambiente", que é o clientes_restaurantes.csv.

Clicamos duas vezes neste arquivo dentro do nosso computador. Após fazer o upload do arquivo, selecionamos a opção dos três pontos ao lado do nome do arquivo e escolhemos a opção "Copiar Caminho". Feito isso, podemos fechar o menu lateral de arquivos no botão "X" e começar a leitura desse arquivo.

Para ler o arquivo, vamos utilizar a biblioteca pandas.

Importando a biblioteca

Importamos essa biblioteca na primeira célula, utilizando o código import pandas as pd.

import pandas as pd

Executamos o código com o atalho "Ctrl + Enter".

Fazendo a leitura do arquivo

Agora, para ler o arquivo, vamos utilizar a função read.csv() da biblioteca pandas e armazenar isso em uma variável chamada dados.

Digitamos dados = pd.read_csv(''), e colamos o caminho do arquivo que copiamos no menu de arquivos. Pressionamos "Ctrl + V" e colamos o caminho '/content/clientes_restaurantes.csv'.

dados = pd.read_csv('/content/clientes_restaurantes.csv')

Executamos essa célula com "Ctrl + Enter". Agora, já fizemos a leitura da base de dados e armazenamos na variável dados.

Visualizando os dados

Para visualizar esses dados, vamos para a próxima célula, escrevemos dados e executamos com "Ctrl + Enter".

dados

Obtemos uma visualização do DataFrame contendo todos os dados da quantidade de clientes que visitam os restaurantes.

#dataChimi & ChurriAssa Frão
01/1/201665.0139.0
11/2/201624.085.0
21/3/201624.081.0
31/4/201623.032.0
41/5/20162.043.0
............
4734/18/201730.018.0
4744/19/201720.018.0
4754/20/201722.046.0
4764/21/201738.038.0
4774/22/201797.059.0

A primeira informação é o índice do DataFrame que varia de 0 até 477, ou seja, temos 478 linhas na base de dados.

Na primeira coluna do DataFrame, temos informações de data que variam de 1/1/2016 até 4/22/2017. A data está no formato americano, onde o primeiro número é o mês, o segundo é o dia e o último é o ano. Nas próximas colunas, temos "Chimi & Churri" e "Assa Frão", que provavelmente são os nomes de cada um dos restaurantes da rede.

As informações nestas colunas são valores numéricos que mostram a quantidade de clientes que visitam esses restaurantes em cada um dos dias.

Esse tipo de informação é conhecido como uma série temporal, uma informação numérica que varia ao longo do tempo.

Já conseguimos fazer a leitura da base de dados e agora podemos começar a explorar as informações desse DataFrame.

Explorando os dados do DataFrame

Para começar a explorar esses dados, vamos identificar o formato, o tipo de dado de cada uma das colunas e também vamos identificar se existem dados nulos no DataFrame.

Para fazer essa análise, utilizamos o método info() da biblioteca pandas. Digitamos na próxima célula, dados.info().

dados.info()

Pressionamos "Ctrl + Enter" para executar essa célula.

<class 'pandas.core.frame.DataFrame'>

RangeIndex: 478 entries, 0 to 477

Data columns (total 3 columns):

Column Non-Null Count Dtype


0 data 478 non-null object

1 Chimi & Churri 476 non-null float64

2 Assa Frão 477 non-null float64

dtypes: float64(2), object(1)

memory usage: 11.3+ KB

Obtemos várias informações interessantes sobre a base de dados. Temos 478 registros e um total de 3 colunas. Abaixo do info(), temos para cada uma das colunas a quantidade de dados não nulos e também o Dtype, que é o tipo de dado daquelas colunas.

A coluna "data" tem 478 dados não nulos e, como sabemos que tem 478 registros na base de dados, essa coluna não tem nenhum dado nulo. No entanto, o Dtype, o tipo daquela coluna, está marcado como object, o que significa que essa coluna está identificada como um texto.

A biblioteca pandas não sabe que isso é uma data. Vamos precisar fazer uma transformação para alocar essa coluna de data, como o formato DateTime. A partir disso, a biblioteca pandas vai identificar que isso é uma data e vamos conseguir trabalhar melhor com esse tipo de informação.

Continuaremos analisando os dados.

As colunas da quantidade de clientes nos restaurantes, "Chimi & Churri" e "Assa Frão", têm 476 e 477 dados não nulos, respectivamente. Como a quantidade de linhas é 478, existem dados nulos que precisaremos tratar (1 no Assa Frão e 2 no Chimi & Churri).

É importante fazer esse tratamento de dados nulos porque estamos trabalhando com séries temporais e, caso tenha alguma data que não tenha uma informação, que é um dado nulo, perdemos a sensação de continuidade. Teremos clientes em um dia e no outro não temos nenhuma informação.

O Dtype de ambas as colunas está marcado como float64, ou seja, já está identificado como uma informação numérica.

Transformando a coluna data em datetime

O primeiro passo será fazer o tratamento, a modificação da coluna "data" para o formato datetime, para conseguirmos trabalhar melhor com esse tipo de informação.

Para fazer isso, utilizamos uma função, que é o to_datetime)_, que vai fazer a transformação daquela coluna, que está como texto, para esse formato de data. Digitamos na próxima célula: dados['data'] = pd.to_datetime(dados['data']).

Selecionamos a coluna em dados['data'] e armazenamos o conteúdo dessa transformação na mesma coluna, usando uma substituição com pd.to_datetime(), passando os mesmos dados.

dados['data'] = pd.to_datetime(dados['data'])

Dessa maneira, conseguimos transformar esses dados, que estão em formato de texto, para o formato de data. Além disso, modificamos essa informação para colocá-la no índice do dataframe. Isso vai facilitar, em momentos futuros, a utilização desse formato de data.

Na mesma célula, digitamos dados.set_index('data', inplace = True). Com dados.set_index() transformamos o índice do dataframe na informação de data. Dentro passamos o nome da coluna (data) e inplace = True para fazer a substituição no próprio conjunto de dados.

dados['data'] = pd.to_datetime(dados['data'])
dados.set_index('data', inplace = True)

Executamos essa célula.

Para visualizar a nova informação, na próxima célula, digitamos dados e executamos o código com "Ctrl + Enter".

dataChimi & ChurriAssa Frão
2016-01-0165.0139.0
2016-01-0224.085.0
2016-01-0324.081.0
2016-01-0423.032.0
2016-01-052.043.0
.........
2017-04-1830.018.0
2017-04-1920.018.0
2017-04-2022.046.0
2017-04-2138.038.0
2017-04-2297.059.0

Agora, a data está sendo entendida, de fato, como uma data, no formato dateTime. Observe que a ordem das informações foram alteradas: 2016-01-01, começa com o ano, mês e depois dia. E também está no índice do dataframe.

Conclusão e Próximos passos

Já conseguimos fazer a leitura da base de dados e identificar a quantidade de dados nulos em cada uma das colunas. Vamos precisar fazer um tratamento desses dados, mas isso ficará para o próximo vídeo!

Entendendo o problema - Encontrando e tratando dados nulos

Conseguimos ler a nossa base de dados, que contém a quantidade de clientes que frequentam a rede de restaurantes. Na exploração inicial, percebemos a presença de dados nulos no DataFrame. Esses dados nulos podem prejudicar as análises futuras. Eles interferem na extração de estatísticas, como média e mediana, e também fazem com que percamos o senso de continuidade da série temporal.

Encontrando dados nulos

O que podemos fazer para detectar quais são esses dados nulos e não ter nenhum problema em análises posteriores? Uma filtragem na base de dados utilizando o método isna() para detectar quais são esses dados nulos.

Na primeira célula, começamos com o restaurante Chimi & Churri. Digitamos: dados['Chimi & Churri']. E depois que filtrarmos a coluna, vamos fazer a filtragem das linhas. Abrimos colchetes novamente, [dados['Chimi & Churri'].isna()].

dados['Chimi & Churri'][dados['Chimi & Churri'].isna()]

Executamos essa célula com "Ctrl + Enter".

data

2016-04-05 NaN

2016-09-17 NaN

Name: Chimi & Churri, dtype: float64

Obteremos dois valores nulos representados por NaN e estão na data 2016-04-05 e 2016-09-17.

Visualizando os dados nulos no gráfico

Seria interessante se conseguíssemos visualizar isso a partir de um gráfico. Vamos construir um gráfico da série temporal mostrando a quantidade de clientes ao longo do tempo do Chimi & Churri e vamos colocar retas verticais em vermelho para dar esse destaque nas datas onde ocorreram dados nulos.

Para construir essas retas verticais, importamos a biblioteca matplotlib e utilizar a função axvline().

Na próxima célula, importamos a primeira biblioteca com import matplotlib.pyplot as plt, executando o código com "Ctrl + Enter".

import matplotlib.pyplot as plt

Na próxima célula, vamos construir primeiro o gráfico de linhas da série temporal. Digitamos: dados['Chimi & Churri'].plot(figsize = (20,3)), sendo (20,3) o tamanho da figura. Na mesma célula, inserimos as retas verticais. Vai ser uma reta vertical para cada uma das datas. Então, construiremos dois gráficos de reta vertical.

O primeiro deles vai ser plt.axvline(x=, color='red'). Precisamos passar x= para identificar em qual data ocorreu o dado nulo. Vamos fazer toda essa filtragem, passando toda a filtragem que fizemos anteriormente para esse x= e, ao final, passamos qual é a data. Digitamos .index[0] para passar a primeira data. Também teremos uma cor para essa reta vertical com color = 'red'.

Nessa mesma célula, inserimos a próxima data. Copiamos todo esse código, colamos na próxima linha e substituímos index[0] por index[1]. Inserimos um ponto e vírgula ao final do código, pois, se não o fizermos, a biblioteca matplotlib mostra uma informação textual no nosso gráfico. Como não desejamos essa informação, adicionamos o ponto e vírgula.

dados['Chimi & Churri'].plot(figsize = (20,3))
plt.axvline(x = dados['Chimi & Churri'][dados['Chimi & Churri'].isna()].index[0], color = 'red')
plt.axvline(x = dados['Chimi & Churri'][dados['Chimi & Churri'].isna()].index[1], color = 'red');

Executamos o código com "Ctrl + Enter".

Gráfico de linhas azul representando flutuações de dados ao longo do tempo, com o eixo x mostrando datas de Jan 2016 até Abr 2017, e o eixo y variando de 0 a 120. Duas linhas verticais vermelhas marcam pontos específicos no gráfico.

Conseguimos obter qual é a quantidade de clientes ao longo do tempo para o restaurante Chimi & Churri. No mês de abril e no mês de setembro, temos duas retas verticais mostrando onde estão os dados nulos dessa série temporal.

Faremos o mesmo para o restaurante Assa Frão.

Copiamos o código da filtragem dos dados nulos, colamos na próxima célula vazia e substituímos as informações de Chimi & Churri para Assa Frão.

dados['Assa Frão'][dados['Assa Frão'].isna()]

Executamos o código com "Ctrl + Enter".

data

2016-11-24 NaN

Name: Assa Frão, dtype: float64

Conseguimos obter apenas um dado nulo que está na data 2016-11-24.

Vamos fazer a mesma coisa e visualizar isso em um gráfico. Copiamos todo o código que construímos para o gráfico do Chimi & Churri. Vamos colar na próxima célula vazia e substituir as informações de Chimi & Churri para Assa Frão.

Então, digitamos Assa Frão dentro dos colchetes, e no axvline(). Posteriormente ajustamos em x = dados['Assa Frão'][dados['Assa Frão'].isna()]. No index colocamos index[0], porque**** só temos apenas uma data de dado nulo. No final, inserimos o ponto e vírgula.

dados['Assa Frão'].plot(figsize = (20,3))
plt.axvline(x = dados['Assa Frão'][dados['Assa Frão'].isna()].index[0], color = 'red');

Executamos o código com "Ctrl + Enter".

Gráfico de linha azul mostrando flutuações de dados ao longo do tempo de janeiro de 2016 a abril de 2017, com a linha do eixo y variando de 0 a 140 e uma linha vertical vermelha destacando um ponto específico em janeiro de 2017.

Obtemos outro gráfico da série temporal para o Assa Frão e conseguimos perceber que, em novembro de 2016, temos uma reta vertical mostrando qual é o dado nulo nessa série temporal.

Identificamos quais são os dados nulos e em quais datas eles ocorrem, mas agora precisamos tratá-los para que não prejudiquem nossas análises futuras. Quais tipos de tratamento podemos realizar?

Tratando os dados nulos

A abordagem mais indicada seria substituir esses valores pelos dados originais daquela data. Para isso, a rede de restaurantes precisa recuperar a fonte de informação original em alguma base de dados que possuem. No entanto, no caso, não temos mais essa fonte de informação. Portanto, embora essa seja a melhor opção, utilizaremos outras estratégias para preencher os dados nulos.

Uma das estratégias possíveis é substituir o valor nulo pela média da coluna. Esse tratamento é mais adequado quando o DataFrame não depende da data. No entanto, como estamos trabalhando com séries temporais, existem tratamentos mais indicados, pois o padrão dos dados muda ao longo do tempo.

Vamos substituir os valores nulos por valores que correspondem ao padrão de um período. Por exemplo, podemos substituir o valor nulo pelo valor do dia anterior ou pelo valor do dia posterior. Isso faz sentido porque, nestes dias próximos, a série temporal tende a apresentar um padrão semelhante.

No entanto, utilizaremos uma estratégia ainda mais indicada: a interpolação entre o dia anterior e o dia posterior.

Interpolação dos dados nulos

Essa interpolação calcula um ponto médio entre o valor da data anterior e o valor da data posterior.

Para realizar essa interpolação, usaremos o método interpolate(). Na próxima célula, digitamos dados = dados.interpolate().

dados = dados.interpolate()

Essa função detecta automaticamente os dados nulos e realiza a substituição com base na lógica de calcular o ponto médio entre a data anterior e a data posterior. Ela preencherá apenas os valores nulos na base de dados. Ao executar essa célula, toda a base será atualizada, mas apenas os dados nulos serão preenchidos.

Para verificar se o tratamento foi realizado corretamente, filtraremos os dados para as datas nulas. Identificamos que as datas com dados nulos são 5 de abril (04-05), 17 de setembro (09-17) e 24 de novembro (11-24). Vamos analisar o dia anterior e o dia posterior a cada uma dessas datas. Na próxima célula, digitamos dados.loc['2016-04-04':'2016-04-06'].

dados.loc['2016-04-04':'2016-04-06']

Estamos selecionando um intervalo de dias que inclui a data anterior ao dia 5 de abril e a data posterior.

dataChimi & ChurriAssa Frão
2016-04-0432.012.0
2016-04-0517.034.0
2016-04-062.040.0

Ao executar esse código, visualizamos que o restaurante Chimi & Churri agora tem um valor para o dia 5 de abril, que estava nulo. Esse valor é o ponto médio entre 32 e 2. Portanto, a soma de 32 mais 2 é 34, e dividida por 2 resulta em 17.

Vamos aplicar a mesma filtragem para detectar os outros dados nulos. Copiamos e colamos o código nas duas células seguintes, e para a próxima data, que é 17 de setembro (09-17), usamos dados.loc['2016-09-16':'2016-09-18'].

dados.loc['2016-09-16':'2016-09-18']

Obtemos:

dataChimi & ChurriAssa Frão
2016-09-1623.051.0
2016-09-1737.560.0
2016-09-1852.057.0

Ao executar essa linha, visualizamos que o valor para 17 de setembro foi preenchido com a média entre 23 e 52, que são os valores dos dias 16 e 18 de setembro. O valor resultante é 37,5.

Como trabalhamos com a quantidade de clientes que frequentam o restaurante, o valor decimal não faz sentido, e precisaremos ajustar esse valor para um número inteiro.

Por fim, filtramos a última data nula, que é 24 de novembro. Usamos dados.loc['2016-11-23':'2016-11-25'].

dados.loc['2016-11-23':'2016-11-25']

Executamos com "Ctrl + Enter".

dataChimi & ChurriAssa Frão
2016-11-235.038.0
2016-11-2417.070.0
2016-11-2572.0102.0

Verificamos que, no restaurante Assa Frão, o valor para 24 de novembro é 70, que corresponde à média entre 102 e 38.

Próximo passo

Com o tratamento dos dados nulos concluído, podemos agora prosseguir com as análises. Continuaremos essas análises no próximo vídeo!

Sobre o curso Séries temporais: detectando anomalias e realizando previsões

O curso Séries temporais: detectando anomalias e realizando previsões possui 147 minutos de vídeos, em um total de 50 atividades. Gostou? Conheça nossos outros cursos de Machine Learning 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 Machine Learning acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas