Redescobrindo o Método de Monte Carlo: Uma Ferramenta Poderosa para Previsões Estocásticas

Este post está no linkedin também: https://www.linkedin.com/pulse/redescobrindo-o-m%2525C3%2525A9todo-de-monte-carlo-uma-ferramenta-poderosa-lelis-hteuf%3FtrackingId=iq2iSan5STG5DASm2wZRfA%253D%253D/?trackingId=iq2iSan5STG5DASm2wZRfA%3D%3D

No último mês, encontrei-me revisitando o método de Monte Carlo, uma técnica que não utilizava profissionalmente desde 2010. Meu trabalho de conclusão de curso em 2007 já explorava o potencial do Monte Carlo em simulações, particularmente no processo de fabricação de blocos de concreto em uma fábrica de pré-moldados. Essa experiência inicial despertou meu interesse duradouro por métodos de simulação. Ao longo dos anos, mergulhei em métodos de simulação e aprendizado de máquina mais sofisticados. Essa jornada me permitiu fazer comparações detalhadas e reconhecer a eficácia do Monte Carlo em várias aplicações. Uma das suas maiores vantagens é a capacidade de realizar previsões estocásticas com eficiência, sem exigir recursos computacionais avançados. Isso o torna uma ferramenta estatística valiosa, especialmente para empresas com orçamentos limitados, mas que buscam alavancar seus negócios através de análises preditivas. Neste artigo, quero ilustrar a aplicabilidade do Método de Monte Carlo usando um caso prático. Vamos explorar um conjunto de dados de usabilidade de uma página web, focando especificamente na quantidade de acessos durante um determinado período. As colunas do nosso dataframe são: Row, Day, Day.Of.Week, Date, Page.Loads, Unique.Visits, First.Time.Visits e Returning.Visits. Método de Monte Carlo não é apenas um remanescente do passado. Ele se mantém relevante e extremamente útil na era moderna do big data e da inteligência artificial. Seu baixo custo computacional e precisão em previsões estocásticas fazem dele uma opção atraente para muitas aplicações empresariais.

# Vamos fazer as importações das bibliotecas que iremos utilizar

import pandas as pd
from IPython.display import HTML
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from datetime import datetime, timedelta

Tratamento de Dados

Primeiro vamos subir os dados no notebook

# Substitua 'caminho_do_seu_arquivo' pelo caminho real onde o arquivo 'data.csv' está armazenado em seu computador
caminho_do_arquivo = 'data.csv'

# Carregando o arquivo CSV em um DataFrame
data_df = pd.read_csv(caminho_do_arquivo)

# Selecionando apenas as 5 primeiras linhas do DataFrame
primeiras_linhas_df = data_df.head()

# Estilizando a tabela com HTML e CSS
style = """
<style>
    table {
        border-collapse: collapse;
        width: 100%;
    }
    th, td {
        border: 1px solid black;
        text-align: left;
        padding: 8px;
    }
    th {
        background-color: #f2f2f2;
    }
</style>
"""

# Exibindo a tabela estilizada com apenas as 5 primeiras linhas
html = style + primeiras_linhas_df.to_html(index=False)
display(HTML(html))

image

Para converter a coluna Date em um timestamp e as outras colunas Page.Loads, Unique.Visits, First.Time.Visits, e Returning.Visits para inteiros (substituindo vírgulas por nada, pois são usadas como delimitadores de milhares), você pode utilizar o Pandas para fazer essas transformações.

# Convertendo a coluna 'Date' para datetime
data_df['Date'] = pd.to_datetime(data_df['Date'])

# Convertendo as colunas de visitas para inteiros
for coluna in ['Page.Loads', 'Unique.Visits', 'First.Time.Visits', 'Returning.Visits']:
    # Removendo vírgulas e convertendo para inteiro
    data_df[coluna] = data_df[coluna].str.replace(',', '').astype(int)

# Estilizando a tabela com HTML e CSS
style = """
<style>
    table {
        border-collapse: collapse;
        width: 100%;
    }
    th, td {
        border: 1px solid black;
        text-align: left;
        padding: 8px;
    }
    th {
        background-color: #f2f2f2;
    }
</style>
"""

# Exibindo as 5 primeiras linhas da tabela estilizada
html = style + data_df.head().to_html(index=False)
display(HTML(html))

image2

Simulação de Monte Carlo

Agora que temos os dados tratados, vamos fazer uma simulação para a coluna de PageLoads, que é o número de acessos diários da página em questão. Mas antes vamos fazer uma análise gráfica para identificar a distribuição por dia da semana e vermos o padrão de acessos:

# Agrupando os dados por dia da semana e calculando a média de Page.Loads
grouped_data = data_df.groupby('Day.Of.Week')['Page.Loads'].mean()

# Criando um gráfico de barras
plt.figure(figsize=(10, 6))
sns.barplot(x=grouped_data.index, y=grouped_data.values)

# Adicionando títulos e rótulos
plt.title('Média de Acessos Diários por Dia da Semana')
plt.xlabel('Dia da Semana')
plt.ylabel('Média de Acessos')

# Exibindo o gráfico
plt.show()

image3

Vamos identificar como está a série temporal dos acessos:

# Ordenando os dados pela data
data_df.sort_values('Date', inplace=True)

# Criando uma série temporal de acessos por data
plt.figure(figsize=(15, 6))
plt.plot(data_df['Date'], data_df['Page.Loads'], marker='o', linestyle='-')

# Adicionando títulos e rótulos
plt.title('Série Temporal de Acessos por Data')
plt.xlabel('Data')
plt.ylabel('Acessos (Page Loads)')

# Melhorando a formatação do eixo x
plt.xticks(rotation=45)
plt.tight_layout()

# Exibindo o gráfico
plt.show()

image4

Esta série temporal demonstra um comportamento bem usual ao longo do ano, que sugere uma sazonalidade interessante. Vamos trabalhar agora a distribuição padrão, média modas e medianas com a biblioteca skimpy e o matplotlib:

# Criando um histograma para a coluna Page.Loads
plt.figure(figsize=(10, 6))
plt.hist(data_df['Page.Loads'], bins=20, color='blue', edgecolor='black')

# Adicionando títulos e rótulos
plt.title('Histograma de Acessos (Page Loads)')
plt.xlabel('Acessos')
plt.ylabel('Frequência')

# Exibindo o histograma
plt.show()

# Calculando estatísticas descritivas
media = round(data_df['Page.Loads'].mean(), 0)
moda = round(data_df['Page.Loads'].mode()[0], 0)  # Moda pode ter mais de um valor, pegamos o primeiro
mediana = round(data_df['Page.Loads'].median(), 0)
variancia = round(data_df['Page.Loads'].var(), 0)
desvio_padrao = round(data_df['Page.Loads'].std(), 0)

# Exibindo as estatísticas calculadas com arredondamento
print("Média de Acessos: ", media)
print("Moda de Acessos: ", moda)
print("Mediana de Acessos: ", mediana)
print("Variância de Acessos: ", variancia)
print("Desvio Padrão de Acessos: ", desvio_padrao)

image5

Média de Acessos:  4117.0
Moda de Acessos:  2948
Mediana de Acessos:  4106.0
Variância de Acessos:  1825141.0
Desvio Padrão de Acessos:  1351.0
  1. Média e Mediana: A média de acessos é 4117.0 e a mediana é 4106.0. Estes valores são muito próximos, sugerindo que a distribuição dos dados pode ser simétrica em torno do valor central. Em uma distribuição perfeitamente simétrica, a média e a mediana seriam idênticas. A proximidade desses dois valores indica que não há uma inclinação extrema para a esquerda ou direita na distribuição dos dados.
  2. Moda: A moda de acessos, que é o valor mais frequente, é 2948. O fato da moda ser significativamente menor do que a média e a mediana pode indicar uma distribuição levemente assimétrica ou a presença de um pico secundário (bimodalidade), onde um grupo específico de dias teve um número de acessos consideravelmente mais baixo que a média.
  3. Variância e Desvio Padrão: A variância é 1825141.0 e o desvio padrão é 1351.0. O desvio padrão, que é a raiz quadrada da variância, dá uma ideia da dispersão dos dados em torno da média. Um desvio padrão maior indica uma maior variação nos dados. Neste caso, um desvio padrão de 1351.0, comparado à média de 4117.0, sugere uma variação moderada dos dados. Isso significa que, embora a maioria dos dados esteja próxima à média, ainda existem variações significativas nos acessos diários.
  4. Análise Geral: Levando em conta todas essas medidas, parece que os dados de acessos possuem uma distribuição relativamente simétrica com alguma variação. A diferença entre a moda e as outras medidas centrais pode indicar alguma irregularidade na distribuição dos dados, como dias com acessos atipicamente baixos ou a presença de mais de um pico na distribuição dos acessos diários.

Para realizar uma simulação de Monte Carlo com 1000 iterações, ajustando a previsão de acessos à média de cada dia da semana, você precisa seguir estes passos:

# Calculando a média de acessos por dia da semana
media_por_dia = data_df.groupby('Day.Of.Week')['Page.Loads'].mean()

# Preparando uma lista para coletar os resultados da simulação
resultados = []

# Realizando a simulação de Monte Carlo
for i in range(1000):
    simulacao = media_por_dia.apply(lambda x: np.random.normal(loc=x, scale=desvio_padrao)).tolist()
    resultados.append(simulacao)

# Convertendo a lista de resultados em um DataFrame
resultados_simulacao = pd.DataFrame(resultados, columns=media_por_dia.index)

# Estilizando a tabela com HTML e CSS
style = """
<style>
    table {
        border-collapse: collapse;
        width: 100%;
    }
    th, td {
        border: 1px solid black;
        text-align: left;
        padding: 8px;
    }
    th {
        background-color: #f2f2f2;
    }
</style>
"""

# Exibindo a tabela estilizada com a descrição dos resultados da simulação
html = style + resultados_simulacao.describe().to_html()
display(HTML(html))

image6

# Criando o gráfico
plt.figure(figsize=(12, 6))

# Plotando cada simulação
for index, row in resultados_simulacao.iterrows():
    plt.plot(row.index, row, color='lightblue', linewidth=0.5)

# Adicionando títulos e rótulos
plt.title('Simulações de Monte Carlo para Acessos Diários')
plt.xlabel('Dia da Semana')
plt.ylabel('Acessos')

# Exibindo o gráfico
plt.show()

image7

# Calculando a média das simulações para cada dia da semana (numerados de 1 a 7)
media_simulacoes = resultados_simulacao.mean()

# Preparando as datas para o próximo mês
hoje = datetime.today()
ano = hoje.year if hoje.month < 12 else hoje.year + 1
mes_proximo = hoje.month + 1 if hoje.month < 12 else 1
primeiro_dia_proximo_mes = pd.Timestamp(ano, mes_proximo, 1)
ultimo_dia_proximo_mes = pd.Timestamp(ano, mes_proximo + 1, 1) - timedelta(days=1) if mes_proximo < 12 else pd.Timestamp(ano + 1, 1, 1) - timedelta(days=1)
datas_proximo_mes = pd.date_range(primeiro_dia_proximo_mes, ultimo_dia_proximo_mes)

# Previsão para o próximo mês
previsao = pd.DataFrame(index=datas_proximo_mes, columns=['Previsão de Acessos'])

# Mapeando os nomes dos dias da semana para números (1=Domingo, 2=Segunda, etc.)
dia_da_semana_map = {
    'Sunday': 1,
    'Monday': 2,
    'Tuesday': 3,
    'Wednesday': 4,
    'Thursday': 5,
    'Friday': 6,
    'Saturday': 7
}

# Aplicando as médias das simulações aos dias da semana
for data in datas_proximo_mes:
    nome_dia = data.day_name()
    numero_dia = dia_da_semana_map[nome_dia]
    previsao.at[data, 'Previsão de Acessos'] = round(media_simulacoes.get(numero_dia, np.nan))

# Estilizando a tabela com HTML e CSS
style = """
<style>
    table {
        border-collapse: collapse;
        width: 100%;
    }
    th, td {
        border: 1px solid black;
        text-align: left;
        padding: 8px;
    }
    th {
        background-color: #f2f2f2;
    }
</style>
"""

# Exibindo a tabela estilizada com as previsões
html = style + previsao.to_html()
display(HTML(html))

image8

# Criando um gráfico de série temporal das previsões
plt.figure(figsize=(12, 6))
plt.plot(previsao.index, previsao['Previsão de Acessos'], marker='o', linestyle='-')

# Adicionando títulos e rótulos
plt.title('Previsão de Acessos para o Mês Futuro')
plt.xlabel('Data')
plt.ylabel('Previsão de Acessos')

# Melhorando a formatação do eixo x
plt.xticks(rotation=45)
plt.grid(True)
plt.tight_layout()

# Exibindo o gráfico
plt.show()

image9

Previsões Informadas pela Sazonalidade Semanal

Ao concluirmos nossa jornada de análise e simulação, é evidente que o método de Monte Carlo, combinado com o reconhecimento da sazonalidade semanal, oferece insights valiosos para a previsão do comportamento de acessos em uma página web. As simulações realizadas não apenas capturaram a essência da variação diária, mas também destacaram como as tendências semanais podem influenciar o volume de acessos. Através da análise detalhada, observamos padrões claros que refletem o comportamento do usuário ao longo da semana. Esses padrões foram então utilizados para alimentar nossas simulações de Monte Carlo, garantindo que cada previsão refletisse não apenas números brutos, mas também a nuance do comportamento humano e da rotina semanal. O resultado foi uma série de previsões para o mês futuro, onde cada dia foi analisado individualmente, considerando a média histórica e a sazonalidade. Isso nos permitiu não apenas entender melhor o padrão geral de acessos, mas também fornecer uma previsão detalhada que leva em conta as variações diárias e semanais. Em suma, este estudo reafirma a importância de olhar além dos números puros, incorporando a dinâmica do comportamento do usuário e os padrões sazonais em nossas análises. As simulações de Monte Carlo, com sua flexibilidade e capacidade de modelar incertezas, provaram ser uma ferramenta inestimável nesta tarefa. Espera-se que as previsões geradas sirvam como um guia confiável para planejamento e otimização estratégica no contexto de tráfego da página web.