Gráficos

Projeção do número de casos no Brasil para a próxima semana

Projeção do número de óbitos no Brasil para a próxima semana

Número de Casos Novos Confirmados

Números de Óbitos Novos

Agradecimentos e Contribuições

  • Ívi M. de Carvalho, D. Sc.

(Para Programadores) Código para geração dos gráficos

NOTA: O resto desse relatório está focado no desenvolvimento dos gráficos mostrados acima. Somente é relevante caso tenha interesse em Python e Data Analysis

Neste relatório vamos desenvolver em Python uma projeção que pode ser atualizada em tempo real do número de casos de Covid-19 no Brasil. Devido à sua natureza com crescimento exponencial, podemos fazer projeções de curto/médio prazo de acordo com tal tendência. É importante salientar que não existe nenhuma exponencial pura. Em algum momento haverá um ponto de inflexão (quando o número de casos começa a diminuir) e esta exponencial se tornará um sigmóide (como já é o caso da China). Portanto, como não há como prever quando ocorrerá essa inflexão, as projeções somente são úteis para curto e médio prazo. Sem mais delongas, vamos começar! Primeiramente, todas as importações que serão utilizadas:

#collapse
# Todas as importações vem aqui
import numpy as np
import pandas as pd;
import matplotlib.pyplot as plt
import seaborn as sns;
from sklearn.linear_model import LinearRegression
from datetime import date

E também parâmetros:

#collapse
FIGSIZE = (8,4)

Em seguida, vamos importar a base de dados disponibilizada pelo repositório da John Hopkins University. Há duas bases relevantes para nosso caso, uma com o histórico do número de casos e outro com o histório do número de mortes (ambos obtido a partir dos relatórios diários da OMS).

#collapse
CASOS_URL = 'https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv'
MORTES_URL = 'https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_deaths_global.csv'

Ambos são urls diretas para arquivos .CSV, portanto podemos importá-los diretamente para a biblioteca Pandas do Python, sem precisar baixá-los:

#collapse
casos = pd.read_csv(CASOS_URL)
mortes = pd.read_csv(MORTES_URL)

Vamos visualizar o cabeçalho de cada um:

casos.head()
Province/State Country/Region Lat Long 1/22/20 1/23/20 1/24/20 1/25/20 1/26/20 1/27/20 1/28/20 1/29/20 1/30/20 1/31/20 2/1/20 2/2/20 2/3/20 2/4/20 2/5/20 2/6/20 2/7/20 2/8/20 2/9/20 2/10/20 2/11/20 2/12/20 2/13/20 2/14/20 2/15/20 2/16/20 2/17/20 2/18/20 2/19/20 2/20/20 2/21/20 2/22/20 2/23/20 2/24/20 2/25/20 2/26/20 ... 4/28/20 4/29/20 4/30/20 5/1/20 5/2/20 5/3/20 5/4/20 5/5/20 5/6/20 5/7/20 5/8/20 5/9/20 5/10/20 5/11/20 5/12/20 5/13/20 5/14/20 5/15/20 5/16/20 5/17/20 5/18/20 5/19/20 5/20/20 5/21/20 5/22/20 5/23/20 5/24/20 5/25/20 5/26/20 5/27/20 5/28/20 5/29/20 5/30/20 5/31/20 6/1/20 6/2/20 6/3/20 6/4/20 6/5/20 6/6/20
0 NaN Afghanistan 33.0000 65.0000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 ... 1828 1939 2171 2335 2469 2704 2894 3224 3392 3563 3778 4033 4402 4687 4963 5226 5639 6053 6402 6664 7072 7653 8145 8676 9216 9998 10582 11173 11831 12456 13036 13659 14525 15205 15750 16509 17267 18054 18969 19551
1 NaN Albania 41.1533 20.1683 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 750 766 773 782 789 795 803 820 832 842 850 856 868 872 876 880 898 916 933 946 948 949 964 969 981 989 998 1004 1029 1050 1076 1099 1122 1137 1143 1164 1184 1197 1212 1232
2 NaN Algeria 28.0339 1.6596 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 ... 3649 3848 4006 4154 4295 4474 4648 4838 4997 5182 5369 5558 5723 5891 6067 6253 6442 6629 6821 7019 7201 7377 7542 7728 7918 8113 8306 8503 8697 8857 8997 9134 9267 9394 9513 9626 9733 9831 9935 10050
3 NaN Andorra 42.5063 1.5218 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 743 743 745 745 747 748 750 751 751 752 752 754 755 755 758 760 761 761 761 761 761 761 762 762 762 762 762 763 763 763 763 764 764 764 765 844 851 852 852 852
4 NaN Angola -11.2027 17.8739 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 27 27 27 30 35 35 35 36 36 36 43 43 45 45 45 45 48 48 48 48 50 52 52 58 60 61 69 70 70 71 74 81 84 86 86 86 86 86 86 88

5 rows × 141 columns

mortes.head()
Province/State Country/Region Lat Long 1/22/20 1/23/20 1/24/20 1/25/20 1/26/20 1/27/20 1/28/20 1/29/20 1/30/20 1/31/20 2/1/20 2/2/20 2/3/20 2/4/20 2/5/20 2/6/20 2/7/20 2/8/20 2/9/20 2/10/20 2/11/20 2/12/20 2/13/20 2/14/20 2/15/20 2/16/20 2/17/20 2/18/20 2/19/20 2/20/20 2/21/20 2/22/20 2/23/20 2/24/20 2/25/20 2/26/20 ... 4/28/20 4/29/20 4/30/20 5/1/20 5/2/20 5/3/20 5/4/20 5/5/20 5/6/20 5/7/20 5/8/20 5/9/20 5/10/20 5/11/20 5/12/20 5/13/20 5/14/20 5/15/20 5/16/20 5/17/20 5/18/20 5/19/20 5/20/20 5/21/20 5/22/20 5/23/20 5/24/20 5/25/20 5/26/20 5/27/20 5/28/20 5/29/20 5/30/20 5/31/20 6/1/20 6/2/20 6/3/20 6/4/20 6/5/20 6/6/20
0 NaN Afghanistan 33.0000 65.0000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 58 60 64 68 72 85 90 95 104 106 109 115 120 122 127 132 136 153 168 169 173 178 187 193 205 216 218 219 220 227 235 246 249 257 265 270 294 300 309 327
1 NaN Albania 41.1533 20.1683 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 30 30 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 32 32 33 33 33 33 33 33 33 33 33 33 33 34
2 NaN Algeria 28.0339 1.6596 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 437 444 450 453 459 463 465 470 476 483 488 494 502 507 515 522 529 536 542 548 555 561 568 575 582 592 600 609 617 623 630 638 646 653 661 667 673 681 690 698
3 NaN Andorra 42.5063 1.5218 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 41 42 42 43 44 45 45 46 46 47 47 48 48 48 48 49 49 49 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51
4 NaN Angola -11.2027 17.8739 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4

5 rows × 141 columns

Como para este relatório temos interesse em apenas dados do Brasil, vamos atribuir duas novas variáveis à ambos:

def filter_country(df, country):
    # Filtrar pais
    df = df[df['Country/Region']==country]

    # Remover colunas iniciais, manter somente as datas
    df = df.iloc[:, 4:]

    # Transpor
    df = df.T

    # Redefinir coluna
    df.columns = [country]

    # Definir index para Datetime
    df.index = pd.to_datetime(df.index)

    return df

casos_brasil = filter_country(casos, 'Brazil')
mortes_brasil = filter_country(mortes, 'Brazil')

Vamos ver como ficaram os novos dataframes:

casos_brasil.tail()
Brazil
2020-06-02 555383
2020-06-03 584016
2020-06-04 614941
2020-06-05 645771
2020-06-06 672846
mortes_brasil.tail()
Brazil
2020-06-02 31199
2020-06-03 32548
2020-06-04 34021
2020-06-05 35026
2020-06-06 35930

Podemos facilmente também plotar tais dados para ver como estão:

casos_brasil.plot(title='Número de casos');
mortes_brasil.plot(title='Número de mortes');

Podemos conferir se o número de casos está se acelerando ou não plotando um gráfico de barras do número de casos por dia. Isso será importante para identificar a inflexão!

casos_novos_brasil = casos_brasil[1:]-casos_brasil[:-1].values
casos_novos_brasil.tail()
Brazil
2020-06-02 28936
2020-06-03 28633
2020-06-04 30925
2020-06-05 30830
2020-06-06 27075
mortes_novas_brasil = mortes_brasil[1:]-mortes_brasil[:-1].values
mortes_novas_brasil.tail()
Brazil
2020-06-02 1262
2020-06-03 1349
2020-06-04 1473
2020-06-05 1005
2020-06-06 904

Em seguida vamos visualizar ambos em um gráfico de barras:

# Funcao personalizada
def plot_bar_novos(df, title):
    df.index = df.index.strftime('%d/%m')#astype('str')
    ax = df.plot.bar(
        title=title, 
        figsize=FIGSIZE
    )
    _=plt.xticks(rotation=50)  
    return ax
primeiro_caso_filtro = casos_novos_brasil.values>0
ax=plot_bar_novos(casos_novos_brasil[primeiro_caso_filtro],
    title='Casos novos (sem acumular, a partir do primeiro caso)', 
    )
# Filtrar a partir do primeiro obito
primeiro_obito_filtro = mortes_novas_brasil.values>0

# Criar plot
ax=plot_bar_novos(mortes_novas_brasil[primeiro_obito_filtro],
               title='Óbitos novos (sem acumular, a partir do primeiro óbito)'
               )
ax.figure.savefig('obitos_novos.png', dpi=300)

Vamos agora dar início à modelagem das projeções. Primeiramente, vamos fazer alguns plots em cima do log dos dados:

casos_brasil['Brazil'].apply(np.log).plot(marker='o', linestyle='', title='Log do Número de casos');
mortes_brasil['Brazil'].apply(np.log).plot(marker='o', linestyle='', title='Log do Número de Mortes');
casos_novos_brasil.apply(np.log).plot(marker='o', linestyle='', title='Log do número de casos novos');
mortes_novas_brasil.apply(np.log).plot(marker='o', linestyle='', title='Log de novas mortes');

Todas as curvas acima não representam uma reta perfeita, o que são ótimas notícias: Significa que talvez não esteja mais seguindo uma tendência exponencial. De qualquer forma, vou usar como ponto de partida uma regressão linear, apesar de haverem ressalvas sobre o uso dela. Por simplificação, vou começar com uma projeção do número total de casos e de mortes. Primeiramente, vou juntar todos os dados em um único dataframe:

brasil = pd.DataFrame({
              'Confirmados Cumulativo': casos_brasil['Brazil'].values[1:],
              'Confirmados Novos': casos_novos_brasil['Brazil'].values,
              'Mortes Cumulativa': mortes_brasil['Brazil'].values[1:],
              'Mortes Novas': mortes_novas_brasil['Brazil'].values
              })

brasil.index = casos_novos_brasil.index
brasil.tail()
Confirmados Cumulativo Confirmados Novos Mortes Cumulativa Mortes Novas
2020-06-02 555383 28936 31199 1262
2020-06-03 584016 28633 32548 1349
2020-06-04 614941 30925 34021 1473
2020-06-05 645771 30830 35026 1005
2020-06-06 672846 27075 35930 904

Também podemos criar um dataframe com o log de todos os dados:

brasil_log = brasil.apply(np.log1p)
brasil_log.tail()
Confirmados Cumulativo Confirmados Novos Mortes Cumulativa Mortes Novas
2020-06-02 13.227415 10.272876 10.348173 7.141245
2020-06-03 13.277685 10.262350 10.390502 7.207860
2020-06-04 13.329283 10.339353 10.434763 7.295735
2020-06-05 13.378202 10.336276 10.463874 6.913737
2020-06-06 13.419273 10.206403 10.489356 6.807935

Vamos visualizar a distribuição de cada um e também scatter plots de cada um versus o outro:

pd.plotting.scatter_matrix(brasil_log, figsize = (14,8), diagonal = 'kde');

O plot acima fica um pouco enviesado pois há um grande acúmulo de zeros. Vamos filtrar as datas a partir do promeiro óbito:

primeiro_obito = brasil_log.index[brasil_log['Mortes Cumulativa']>0][0]
primeiro_obito_filtro = brasil_log.index>=primeiro_obito
primeiro_obito
Timestamp('2020-03-17 00:00:00')

Como é uma base bastante recente, vamos também pegar o primeiro caso:

primeiro_caso = brasil_log.index[brasil_log['Confirmados Cumulativo']>0][0]
primeiro_caso_filtro = brasil_log.index>=primeiro_caso
primeiro_caso
Timestamp('2020-02-26 00:00:00')

Vamos repetir o scatter matrix acima a partir dos filtros que definimos:

g = sns.pairplot(brasil[primeiro_obito_filtro], kind="reg")
g.fig.tight_layout()
g.fig.subplots_adjust(top=0.88)
g.fig.suptitle('Grade de gráficos, dados a partir do primeiro óbito', y=0.92);
g = sns.pairplot(brasil[primeiro_caso_filtro], kind="reg")
g.fig.suptitle('Scatter Matrix com filtro a partir do primeiro caso', y=1.08);
g = sns.pairplot(brasil_log[primeiro_obito_filtro], kind="reg")
g.fig.suptitle('Scatter Matrix do Log dos dados com filtro a partir do primeiro óbito', y=1.08);
g = sns.pairplot(brasil_log[primeiro_obito_filtro], kind="reg")
g.fig.suptitle('Scatter Matrix do Log dos dados com filtro a partir do primeiro óbito', y=1.08);

Os plots acima, talvez pareçam informação irrelevante, mas o fiz para ter uma ideia sobre a distribuição dos dados e se há alguma distribuição normal em algum caso, que justificaria o uso do desvio padrão (uma vez que o mesmo é em relação à distribuição normal). De qualquer forma, para manter as coisas simples inicialmente, vou manter meu plano inicial de criar uma projeção do número de casos incluindo o intervalo do desvio padrão. Então vamos começar pegando o as estatísticas filtrando a partir do primeiro caso:

std = brasil_log[primeiro_caso_filtro].std()

Visualizar um plot da tendência que vamos modelar:

brasil_log[primeiro_caso_filtro]['Confirmados Cumulativo'].plot()
<matplotlib.axes._subplots.AxesSubplot at 0x7f262dbfbb38>

Em seguida ajustar uma regressão linear em cima do log:

def fitar(x, y):
    lr = LinearRegression()
    lr.fit(x,y)
    return lr

x = np.arange(sum(primeiro_caso_filtro)).reshape(-1,1)
y = brasil_log[primeiro_caso_filtro]['Confirmados Cumulativo'].values
lr_casos = fitar(x, y)
def projetar(lr, x, y, plot=True):
    y_pred = lr.predict(x)
    if plot:
        plt.scatter(x, y)
        plt.plot(y_pred, 'r')
        plt.title("Projeção logarítmica")
        plt.show()

        plt.scatter(x, np.expm1(y))
        plt.plot(np.expm1(y_pred), 'r')
        plt.title("Projeção exponencial")
        plt.show()
    return y_pred

y_pred = projetar(lr_casos, x, y)

A projeção acima não ficou boa, vamos testar suavizar com um exponential moving average:

def generate_moving_average(data, mom=0.7):
    MOM = 0.7
    rolling_mean = [data[0]]

    for d in data[1:]:
        rolling_mean.append(rolling_mean[-1]*MOM + (1-MOM)*d)

    return np.array(rolling_mean)

def fitar_projetar(x, y, mom=None):
    if mom is not None:
        y_old = y
        y = generate_moving_average(y)

    lr = fitar(x, y)
    y_pred = projetar(lr, x, y, plot=not mom) # Plota se mom for nulo

    if mom is not None:
        y_pred_exp = np.expm1(y_pred)
        reversed_mv = [(rm-rm_*mom)/(1-mom) 
                       for rm, rm_ in zip(y_pred_exp[1:], y_pred_exp[:-1])]

        plt.scatter(x, np.expm1(y_old), label="Original")
        plt.scatter(x, np.expm1(y), label="Exp. Moving Average")
        plt.plot(y_pred_exp, 'r', label="Projeção do EMA")
        plt.plot(reversed_mv, label="EMA reverso para projeção real")
        plt.legend()
        plt.title(f"Projeção com Exp. Moving Average (momentum = {mom})")
        plt.show()
    return y_pred

y_pred = fitar_projetar(x, y, mom=0.5)

Também não ficou muito boa. Um dos motivos de estas funções não estarem se saindo bem é que o erro está sendo inferido na escala logarítmica, ou seja, enquanto estamos na escala logarítmica, o erro em termos absolutos é pequeno, no entanto, quando passamos à escala normal, o erro aumenta consideravelmente. Parra corrigir isso, precisamos ajustar uma função exponencial diretamente sem realizar a transformação logarítmica. Precisamos de uma função de custo que infira o erro diretamente da exponencial. Podemos fazer isso com o auxílio de otimizadores de redes neurais. Vamos também aproveitar para ajustar uma sigmóide. E aqui um detalhe: para quem é de machine learning, as boas práticas de seleção de modelos não se aplicam aqui (por exemplo, divisão treinamento e teste). Como os modelos a serem ajustados são bastante simples, não há a necessidade de reservar um conjunto de validação/teste.

# Creating a model
from keras.models import Sequential;
from keras.layers import Dense
from keras import backend as K
from keras.optimizers import Adam
from keras.activations import sigmoid, elu

def get_keras_model(lr=0.001, activation='exp'):
    if activation=='exp':
        def activ_func(x):
            return K.exp(x) - 1
    elif activation=='sigm':
        activ_func = sigmoid
    elif activation=='elu':
        activ_func = elu

    # Usage
    model = Sequential();
    model.add(Dense(1, input_dim=1, activation=activ_func))
    model.compile(optimizer=Adam(lr=lr), loss='mean_squared_error')
    return model
# Definir entrada e saida
x = np.arange(sum(primeiro_caso_filtro)).reshape(-1,1)
y = brasil[primeiro_caso_filtro]['Confirmados Cumulativo'].values
# Preparar dados
def preparar_dados(x, y):
    x_mean = x.mean()
    x_std = x.std()
    y_mean = y.mean()
    y_std = y.std()

    x_prep = (x-x_mean)/x_std
    y_prep = (y-y_mean)/y_std
    return x_prep, y_prep

x_prep, y_prep = preparar_dados(x, y)
#hide_output
model = get_keras_model(lr=0.1)
model.fit(x_prep, y_prep, epochs=100, verbose=0);
from sklearn.metrics import r2_score
def projetar_keras(model, x, y, days_ahead=0, plot=True, 
                   model_func="exponencial", 
                   tipo='caso', exibir_pontos=False,
                   anotar_dados_reais=True,
                   dados_reais_step=2):
    title = f'Projeção {model_func} de {tipo}s de Covid-19 no Brasil' + f' para os próximos {days_ahead} dias'
    hoje = date.today()
    hoje = hoje.strftime("%d/%m")
    x_mean = x.mean()
    x_std = x.std()
    y_mean = y.mean()
    y_std = y.std()

    n_days = len(x)
    x_proj = np.arange(n_days+days_ahead).reshape(-1,1)

    x_prep = (x_proj-x_mean)/x_std
    y_prep = (y-y_mean)/y_std

    y_pred = model.predict(x_prep)
    y_pred = y_pred*y_std + y_mean #Reverse y_pred back
    y_pred = y_pred.astype(int)
    y_pred = np.clip(y_pred, 0, None)
    y_pred = y_pred.squeeze()

    #print(y)
    #print(y_pred)

    r2 = r2_score(y, y_pred[:-days_ahead])

    if plot:
        fig = plt.figure(figsize=FIGSIZE)
        # Plotar projeção
        plt.plot(x_proj[-days_ahead-1:], y_pred[-days_ahead-1:], '-', 
                 color='red', label='Projeção')
        
        # Plotar função
        if exibir_pontos:
            plt.plot(x_proj[:-days_ahead], y_pred[:-days_ahead], '-', color='red', 
                     label=f'Ajuste {model_func} (R²={r2:.3f})', alpha=0.4)
        #plt.scatter(x, y, label='Dados reais', color='orange')
        plt.plot(y, '.', label='Dados reais')
        plt.title(title)
        for x_anot, y_anot in zip(x_proj[-days_ahead:], y_pred[-days_ahead:]):
            plt.annotate(y_anot, (x_anot, y_anot), ha='right', color='red')
        plt.annotate(f'({hoje}): {y[-1]}', (x[-1], y[-1]), ha='right',
                     color='black',
                     textcoords="offset points",
                     xytext=(-5,0))
        if anotar_dados_reais:
            for x_anot, y_anot in zip(x[:-1:dados_reais_step], y[:-1:dados_reais_step]):
                plt.annotate(y_anot, (x_anot, y_anot), ha='right', 
                            color='midnightblue',
                            textcoords="offset points",
                            xytext=(-5,3)
                            )
        
        plt.grid(color='black', linestyle='--', linewidth=0.17)
        plt.legend()
        plt.xlabel(f"Dias desde o primeiro {tipo} no Brasil")
        plt.ylabel(f'{tipo.capitalize()}s confirmados acumulados')
        plt.xticks(x_proj[::2])
        plt.show()
    return y_pred, fig
y_pred, fig = projetar_keras(model, x, y, days_ahead=7, 
                             exibir_pontos=True, 
                             anotar_dados_reais=False,
                             dados_reais_step=3)

Por curiosidade, vamos testar ajustar um sigmóide:

#hide_output
model = get_keras_model(lr=1, activation='sigm')
model.fit(x_prep, y_prep, epochs=100, verbose=0);
y_pred, fig = projetar_keras(model, x, y, days_ahead=5, model_func='sigmóide', exibir_pontos=True)

Bem distante do esperado :) Vamos testar também o ELU:

#hide_output
model = get_keras_model(lr=0.1, activation='elu')
model.fit(x_prep, y_prep, epochs=100, verbose=0);
y_pred, fig = projetar_keras(model, x, y, days_ahead=7, model_func="ELU (Exp. Linear Unit)", exibir_pontos=True)

Um pouco melhor, mas neste caso com tendência linear. Vamos também replicar estes dois últimos para o número de óbitos.

# Definir entrada e saida
x = np.arange(sum(primeiro_obito_filtro)).reshape(-1,1)
y = brasil[primeiro_obito_filtro]['Mortes Cumulativa'].values

x_prep, y_prep = preparar_dados(x, y)
#hide_output
model = get_keras_model(lr=0.1, activation='exp')
model.fit(x_prep, y_prep, epochs=100, verbose=0);
y_pred, fig = projetar_keras(model, x, y, days_ahead=7, model_func="exponencial", 
                             tipo='obito',
                             exibir_pontos=True, 
                             anotar_dados_reais=False,
                             dados_reais_step=3)
#hide_output
model = get_keras_model(lr=0.1, activation='elu')
model.fit(x_prep, y_prep, epochs=100, verbose=0);
y_pred, fig = projetar_keras(model, x, y, 
                             days_ahead=7, 
                             model_func="ELU (Exp. Linear Unit)", 
                             tipo='obito', 
                             exibir_pontos=True)