Pular para o conteúdo principal

Médias móveis no pandas

Aprenda a identificar tendências e interpretar séries temporais com a ajuda de médias móveis (rolling).
· 8 min leer

Explore com IA

Introdução

A média móvel, também chamada de média rolante, é usada para analisar dados de séries temporais calculando médias de diferentes subconjuntos do conjunto completo. Como envolve tirar a média do conjunto ao longo do tempo, ela também é chamada de moving mean (MM) ou rolling mean.

Existem várias formas de calcular a média rolante; uma delas é tomar um subconjunto fixo de uma série completa de números. A primeira média móvel é calculada fazendo a média do primeiro subconjunto fixo; em seguida, esse subconjunto é alterado avançando para o próximo bloco fixo (incluindo o valor seguinte e excluindo o valor anterior da série).

A média móvel é usada principalmente com séries temporais para capturar flutuações de curto prazo enquanto foca nas tendências de longo prazo.

Alguns exemplos de séries temporais: preços de ações, registros meteorológicos, qualidade do ar, produto interno bruto, emprego etc.

Em geral, a média móvel suaviza os dados.

A média móvel é base para muitos algoritmos, como o modelo Autoregressive Integrated Moving Average (ARIMA), que usa médias móveis para prever séries temporais.

Existem vários tipos de médias móveis:

  • Média móvel simples (SMA): A Simple Moving Average (SMA) usa uma janela deslizante para calcular a média em um número definido de períodos. É uma média com pesos iguais dos n dados anteriores.

    Para entender melhor a SMA, vamos a um exemplo: uma sequência de n valores:

    sequence of n values

    então a média rolante com pesos iguais para n pontos de dados será essencialmente a média dos M dados anteriores, onde M é o tamanho da janela deslizante:

    simple moving average calculation 1

    Da mesma forma, para calcular os valores seguintes da média rolante, um novo valor é adicionado à soma e o valor do período anterior é removido; como você já tem a média dos períodos anteriores, não é necessário somar tudo a cada vez:

    simple moving average calculation 2
  • Média móvel cumulativa (CMA): Diferente da média móvel simples, que descarta a observação mais antiga quando uma nova é adicionada, a cumulative moving average considera todas as observações anteriores. A CMA não é a melhor técnica para analisar tendências e suavizar dados. O motivo é que ela faz a média de todos os dados anteriores até o ponto atual; assim, uma média com pesos iguais da sequência de n valores:
    sequence of n values 2
    até o instante atual é dada por:
    cumulative moving average 1
    Da mesma forma, a atualização da média cumulativa para cada novo valor pode ser calculada usando a fórmula abaixo:
    cumulative moving average 2
  • Média móvel exponencial (EMA): Diferente de SMA e CMA, a exponential moving average dá mais peso aos valores mais recentes e, por isso, pode ser um modelo melhor para capturar o movimento da tendência de forma mais rápida. A reação da EMA é diretamente proporcional ao padrão dos dados.

Como a EMA dá um peso maior aos dados recentes do que aos antigos, ela responde mais rapidamente às últimas mudanças do que a SMA, o que torna seus resultados mais oportunos; por isso, a EMA costuma ser preferida em relação a outras técnicas.

Chega de teoria, certo? Vamos partir para a implementação prática da média móvel.

Implementando média móvel em dados de séries temporais

Média móvel simples (SMA)

Primeiro, vamos criar uma série temporal de exemplo e implementar a SMA usando apenas Python.

Suponha que a demanda por um produto foi observada por 12 meses (1 ano) e você precisa calcular as médias móveis com janelas de 3 e 4 meses.

Importar módulos

import pandas as pd
import numpy as np
 
product = {'month' : [1,2,3,4,5,6,7,8,9,10,11,12],'demand':[290,260,288,300,310,303,329,340,316,330,308,310]}
df = pd.DataFrame(product)
df.head()
 

Execute e edite o código deste tutorial online

Executar código
 monthdemand
01290
12260
23288
34300
45310

Vamos calcular a SMA para uma janela de 3, o que significa considerar três valores por vez para calcular a média móvel; a cada novo valor, o mais antigo é descartado.

Para implementar, você vai usar a função iloc do pandas. Como a coluna demand é a que interessa, você fixa sua posição na função iloc, enquanto a linha será a variável i, que vai iterar até o final do dataframe.

for i in range(0,df.shape[0]-2):
    df.loc[df.index[i+2],'SMA_3'] = np.round(((df.iloc[i,1]+ df.iloc[i+1,1] +df.iloc[i+2,1])/3),1)
 monthdemandSMA_3
01290NaN
12260NaN
23288279.3
34300282.7
45310299.3

Para validar, vamos usar também a função nativa rolling do pandas e ver se bate com nossa média móvel simples feita em Python.

df['pandas_SMA_3'] = df.iloc[:,1].rolling(window=3).mean()
df.head()
 monthdemandSMA_3pandas_SMA_3
01290NaNNaN
12260NaNNaN
23288279.3279.333333
34300282.7282.666667
45310299.3299.333333

Legal! Como você pode ver, as médias móveis personalizada e do pandas batem, o que confirma que sua implementação da SMA está correta.

Vamos também calcular rapidamente a média móvel simples para um window_size de 4.

for i in range(0,df.shape[0]-3):
    df.loc[df.index[i+3],'SMA_4'] = np.round(((df.iloc[i,1]+ df.iloc[i+1,1] +df.iloc[i+2,1]+df.iloc[i+3,1])/4),1)
df.head()
 monthdemandSMA_3pandas_SMA_3SMA_4
01290NaNNaNNaN
12260NaNNaNNaN
23288279.3279.333333NaN
34300282.7282.666667284.5
45310299.3299.333333289.5
df['pandas_SMA_4'] = df.iloc[:,1].rolling(window=4).mean()
df.head()
 monthdemandSMA_3pandas_SMA_3SMA_4pandas_SMA_4
01290NaNNaNNaNNaN
12260NaNNaNNaNNaN
23288279.3279.333333NaNNaN
34300282.7282.666667284.5284.5
45310299.3299.333333289.5289.5

Agora, vamos plotar os dados das médias móveis que você calculou.

import matplotlib.pyplot as plt
%matplotlib inline
plt.figure(figsize=[15,10])
plt.grid(True)
plt.plot(df['demand'],label='data')
plt.plot(df['SMA_3'],label='SMA 3 Months')
plt.plot(df['SMA_4'],label='SMA 4 Months')
plt.legend(loc=2)
<matplotlib.legend.Legend at 0x11fe15080>
calculated moving averages line graph

Média móvel cumulativa

Acho que agora estamos prontos para usar um dataset real.

Para a média móvel cumulativa, vamos usar um air quality dataset que pode ser baixado neste link.

df = pd.read_csv("AirQualityUCI/AirQualityUCI.csv", sep = ";", decimal = ",")
df = df.iloc[ : , 0:14]
df.head()
 DateTimeCO(GT)PT08.S1(CO)NMHC(GT)C6H6(GT)PT08.S2(NMHC)NOx(GT)PT08.S3(NOx)NO2(GT)PT08.S4(NO2)PT08.S5(O3)TRH
010/03/200418.00.002.61360.0150.011.91046.0166.01056.0113.01692.01268.013.648.9
110/03/200419.00.002.01292.0112.09.4955.0103.01174.092.01559.0972.013.347.7
210/03/200420.00.002.21402.088.09.0939.0131.01140.0114.01555.01074.011.954.0
310/03/200421.00.002.21376.080.09.2948.0172.01092.0122.01584.01203.011.060.0
410/03/200422.00.001.61272.051.06.5836.0131.01205.0116.01490.01110.011.259.6

Pré-processamento é uma etapa essencial ao trabalhar com dados. Para dados numéricos, uma das verificações mais comuns é por valores NaN (nulos). Se houver NaN, você pode substituí-los por 0, pela média, por valores anteriores ou posteriores, ou até removê-los. Embora substituir geralmente seja melhor do que descartar, como este dataset tem poucos valores nulos e estão no fim da série, removê-los não afetará a continuidade.

df.isna().sum()
Date             114
Time             114
CO(GT)           114
PT08.S1(CO)      114
NMHC(GT)         114
C6H6(GT)         114
PT08.S2(NMHC)    114
NOx(GT)          114
PT08.S3(NOx)     114
NO2(GT)          114
PT08.S4(NO2)     114
PT08.S5(O3)      114
T                114
RH               114
dtype: int64

Pelo resultado acima, dá para notar cerca de 114 valores NaN em todas as colunas; porém, eles estão todos no final da série temporal, então vamos descartá-los.

df.dropna(inplace=True)
df.isna().sum()
Date             0
Time             0
CO(GT)           0
PT08.S1(CO)      0
NMHC(GT)         0
C6H6(GT)         0
PT08.S2(NMHC)    0
NOx(GT)          0
PT08.S3(NOx)     0
NO2(GT)          0
PT08.S4(NO2)     0
PT08.S5(O3)      0
T                0
RH               0
dtype: int64

Você vai aplicar a média móvel cumulativa na coluna de temperatura (T), então vamos separá-la do conjunto completo.

df_T = pd.DataFrame(df.iloc[:,-2])
df_T.head()
 T
013.6
113.3
211.9
311.0
411.2

Agora, vamos usar o método expanding do pandas para encontrar a média cumulativa dos dados acima. Como vimos na introdução, diferente da média móvel simples, a média móvel cumulativa considera todos os valores anteriores ao calcular a média.

df_T['CMA_4'] = df_T.expanding(min_periods=4).mean()
df_T.head(10)
 TCMA_4
013.6NaN
113.3NaN
211.9NaN
311.012.450000
411.212.200000
511.212.033333
611.311.928571
710.711.775000
810.711.655556
910.311.520000

Séries temporais são plotadas em função do tempo, então vamos combinar as colunas de data e hora e convertê-las em um objeto datetime. Para isso, vamos usar o módulo datetime do Python (Fonte: Time Series Tutorial).

import datetime

df['DateTime'] = (df.Date) + ' ' + (df.Time)
df.DateTime = df.DateTime.apply(lambda x: datetime.datetime.strptime(x, '%d/%m/%Y %H.%M.%S'))

Vamos alterar o índice do dataframe de temperatura para datetime.

df_T.index = df.DateTime

Agora vamos plotar a temperatura real e a média móvel cumulativa ao longo do tempo.

plt.figure(figsize=[15,10])
plt.grid(True)
plt.plot(df_T['T'],label='temperature')
plt.plot(df_T['CMA_4'],label='CMA_4')
plt.legend(loc=2)
<matplotlib.legend.Legend at 0x1210a2d30>
time series data graph

Média móvel exponencial

df_T['EMA'] = df_T.iloc[:,0].ewm(span=40,adjust=False).mean()
df_T.head()
 TCMA_4EMA
DateTime   
2004-03-10 18:00:0013.6NaN13.600000
2004-03-10 19:00:0013.3NaN13.585366
2004-03-10 20:00:0011.9NaN13.503153
2004-03-10 21:00:0011.012.4513.381048
2004-03-10 22:00:0011.212.2013.274655
plt.figure(figsize=[15,10])
plt.grid(True)
plt.plot(df_T['T'],label='temperature')
plt.plot(df_T['CMA_4'],label='CMA_4')
plt.plot(df_T['EMA'],label='EMA')
plt.legend(loc=2)
<matplotlib.legend.Legend at 0x14b2a41d0>
time series data graph 2

Uau! Como dá para observar no gráfico acima, a Exponential Moving Average (EMA) capta muito bem o padrão dos dados, enquanto a Cumulative Moving Average (CMA) fica bem atrás.

Vá além!

Parabéns por concluir o tutorial.

Este tutorial foi um ótimo ponto de partida para calcular médias móveis e interpretar suas séries temporais.

Tente escrever o código de média móvel cumulativa e exponencial em Python sem usar a biblioteca pandas. Isso vai dar uma visão bem mais profunda de como são calculadas e em que aspectos diferem uma da outra.

Ainda há muito para experimentar. Tente calcular a autocorrelação parcial entre os dados de entrada e a média móvel e busque alguma relação entre os dois.

Se quiser aprender mais sobre DataFrames no pandas, faça o curso interativo pandas Foundations da DataCamp.

Referências:

Fique à vontade para deixar suas perguntas sobre este tutorial na seção de comentários abaixo.

Temas

Relacionado

data-frames-in-python-banner_cgzjxy.jpeg

Tutorial

Pandas Tutorial: DataFrames em Python