<jupyter_start><jupyter_text>Módulo 5  01. Machine Learning e Saúde  Desafio 01: Se você não conhece o Kaggle, explore um pouco os datasets, competições e discussões para verificar o quão rico é esta plataforma.  Desafio 02: Leia a descrição do problema proposta pelo Sírio Libanês, tentando entender qual é o real problema. Antes de continuar para as próximas aulas, debata no Discord suas conclusões sobre o projeto. *  02. Desenvolvimento no Kaggle<jupyter_code>import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))
import pandas as pd
dados = pd.read_excel('/kaggle/input/covid19/Kaggle_Sirio_Libanes_ICU_Prediction.xlsx')
dados.head()
dados.describe()<jupyter_output><empty_output><jupyter_text>**Observação:** Algumas colunas, no mundo real, podem estar **anonimizadas**, por fatores éticos humanos (elas podem descobrir quais pessoas estão sendo usadas para a base de dados, de acordo com tal característica nos dados), ou até mesmo por conta das próprias empresas não quererem que você saiba o que é aquela variável.<jupyter_code>dados['ICU']
dados['ICU'].value_counts()<jupyter_output><empty_output><jupyter_text>- Aprendizado Supervisionado: quando você quer prever o valor de alguma *label*, dado que você sabe qual é esta variável;- Aprendizado Não-Supervisionado: quando você quer prever o valor de alguma *label*, dado que você **não** sabe qual é esta variável. O que acontece na realidade é que, a partir das outras características eu tento agrupar em determinados grupos **sem saber o que realmente é aquele dado.** Ou seja, estamos aprendendo o **conceito** e não a palavra em si.  Desafio 01 -pesquisar aprendizado supervisionado, não supervisionado, por reforço e mais (forma).  Desafio 02 - Pesquisar sobre classificação, regressão e outros problemas que podem ser solucionados com Machine Learning.  03. Modelos de Machine Learning<jupyter_code>x_columns = dados.describe().columns

dados_limpos = dados.dropna()
y = dados_limpos['ICU']
x = dados_limpos[x_columns].drop('ICU', axis=1)
from sklearn.linear_model import LogisticRegression<jupyter_output><empty_output><jupyter_text>Uma explicação introdutória da Regressão LogísticaO modelo de Regressão Logística é utilizado para descrever uma variável quantitativa como algo classificatório (binário). O método utiliza a probabililidade de ocorrer tal valor (quantitativo), sendo assim, se for uma probabilidade menor que 50%, então ele classifica como "não possível" (0), já se for acima, como "possível" (1).A função que ela utiliza é a **sigmoid**, com a fórmula abaixo:Dado uma função linear *t*, temos: Então:Vai um exemplo abaixo:**Observação:** o gráfico e as fórmulas são tiradas do Wikipedia.<jupyter_code>modelo = LogisticRegression()
modelo.fit(x, y)
modelo.predict([x.iloc[4]])
y.iloc[4]
sum(modelo.predict(x) == y)
sum(modelo.predict(x) != y)
accuracy = sum(modelo.predict(x) == y) / len(y) * 100
accuracy<jupyter_output><empty_output><jupyter_text>Desafio 03 - Qual o motivo de jogar fora o ICU do dado X? O que acontece se estiver no x? Pq seria bom/ruim? .  Desafio 04 - Pesquisar sobre ética em data science/ciência de dados e inteligência artificial .  04. Métricas e Avaliações<jupyter_code>y.value_counts()
from sklearn.dummy import DummyClassifier<jupyter_output><empty_output><jupyter_text>DummyClassifierEsse modelo na verdade chuta o valor de acordo com a **proporcionalidade**. Sendo assim, ele é uma **base** de um algoritmo ingênuo, estimando pela maioria, ou seja, pela moda.Então, se fosse para chutar algum valor no exemplo acima, o modelo chutaria **0** todos os valores.<jupyter_code>modelo = DummyClassifier("most_frequent")
modelo.fit(x, y)
sum(modelo.predict(x) == y) / len(y) * 100
from sklearn.metrics import accuracy_score<jupyter_output><empty_output><jupyter_text>AcuráciaBasicamente, é a taxa de acerto do que foi predito em relação ao que foi testado.<jupyter_code>accuracy_score(y, modelo.predict(x))<jupyter_output><empty_output><jupyter_text>Repare que o resultado foi o mesmo da célula passada.<jupyter_code>import numpy as np

np.random.seed(73246)
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, stratify=y)
print(f'Nº de pacientes para treinar (input -> x): {len(x_train)}')
print(f'Nº de pacientes para treinar (o que é tal amostra -> y): {len(y_train)}')
print(f'Nº de pacientes para testar (input -> x): {len(x_test)}')
print(f'Nº de pacientes para testar (o que é tal amostra -> y): {len(y_test)}')
modelo = LogisticRegression()
modelo.fit(x_train, y_train)

y_prediction = modelo.predict(x_test)
accuracy_score(y_test, y_prediction)<jupyter_output>/opt/conda/lib/python3.7/site-packages/sklearn/linear_model/_logistic.py:764: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  extra_warning_msg=_LOGISTIC_SOLVER_CONVERGENCE_MSG)<jupyter_text>**Observação 1:** se rodarmos nosso modelo novamente, muito provavelmente chegaremos em acurácias distintas. Isso acontece justamente porque o modelo matemático é sim o mesmo, porém os valores que vamos pegando do dataframe, de forma aleatória, é **diferente**! Para definir um único "modo" aleatório, precisamos definir o argumento ***random_state***, no *train_test_split*.**Observação 2:** o train_test_split utiliza o numpy por trás dos panos, ou seja, se definirmos a seed antes, com o numpy, não precisamos mexer no *random_state*.**Observação 3:** o *train_test_split* divide o treino e o teste de forma aleatória, e por padrão, coloca uma proporção de 75% para treino e 25% para teste. Entretanto, ninguém garante que os valores que estão dentro do treino são proporcionais aos que estão dentro do teste, por exemplo, 50% de 0 e 50% de 1 no treino, assim como 50% de 0 e 50% de 1 no teste. Para definir essas porcentagens internas, precisamos definir o argumento ***stratify*** (estratificar), no train_test_split.**Observação 4:** se colocarmos ***stratify*** = y, estamos dizendo: "estratifique de acordo com a proporcionalidade dos valores que existem no y"<jupyter_code>y_train.value_counts(normalize=True)
y_test.value_counts(normalize=True)<jupyter_output><empty_output><jupyter_text>Desafio 05 - Decida quanto você vai escolher para treino e quanto para teste. Quanto as pessoas utilizam e qual o motivo? > In a draft copy currently being written by <NAME>, he discusses about the amount of data in train-test dataset. My understanding from the book, The traditional and most common value is 70-30 or 75-25. If you have 10k or 30k samples, it is fine to go with 70-30 split. But when dealing with Big-data, for example if you have 1 million samples, it is not recommended to have 30k samples as test data, so in that case, 90-10 is actually okay. Because 10k test samples can pretty much provide an intuition about the model.> in brief: for less samples, go with recommended 70-30 split, for much higher samples, go with number of samples 90-10.link: https://datascience.stackexchange.com/questions/15804/which-train-test-split-performs-better-5050-or-6040  Desafio 06 - Olhe a documentação da LogisticRegression... o que tem de parâmetro em comum com o train_test_split? O que isso significa pra nós? . O parâmetro ***random_state*** é comum às duas funções, entretanto, fazem coisas distintas.O r*andom_state* da *LogisticRegression()* é um código para padronizar a aleatoriedade, assim como no train_test_split, porém esse é definido de acordo com um outro argumento da *LogisticRegression()*, ***o solver***. Este argumento basicamente otimiza o modelo de acordo com algumas condições, são estas em inglês (documentação):- For small datasets, ‘*liblinear*’ is a good choice, whereas ‘*sag*’ and ‘*saga*’ are faster for large ones.- For multiclass problems, only ‘*newton-cg*’, ‘*sag*’, ‘saga’ and ‘*lbfgs*’ handle multinomial loss; ‘*liblinear*’ is limited to one-versus-rest schemes.- ‘*newton-cg*’, ‘*lbfgs*’, ‘sag’ and ‘saga’ handle L2 or no penalty- ‘*liblinear*’ and ‘*saga*’ also handle L1 penalty- ‘*saga*’ also supports ‘*elasticnet*’ penalty- ‘*liblinear*’ does not support setting penalty='*none*'Já o do *train_test_split* é o que foi relatado anteriormente.  05. Compreendendo o problema real<jupyter_code>dados = pd.read_excel('/kaggle/input/covid19/Kaggle_Sirio_Libanes_ICU_Prediction.xlsx')
dados.head()<jupyter_output><empty_output><jupyter_text>Antes de colocarmos o nosso modelo de ML em prática, deveríamos ter entendido melhor nossa base de dados. Se repararmos, a coluna "*PATIENT_VISIT_IDENTIFIER*" é o ID da pessoa que está sendo avaliada. Entretanto, existem vários ID's iguais, por quê?A resposta está [aqui](https://www.kaggle.com/S%C3%ADrio-Libanes/covid19), isso simplesmente acontece pois a avaliação do sangue do paciente foi feita em momentos distintos. Neste link, eles usam janelas separadas de 2 em 2 horas ("*WINDOW*"), até a categoria "*ABOVE_12*".Na teoria, não há a necessidade de saber o que acontece depois que a pessoa foi para a UTI, e sim o que houve **antes**, pois queremos predizer a partir de características anteriores a ida para este lugar. Além disso, se a pessoa foi para a UTI direto (antes da categoria "0-2"), nem precisamos avaliar a situação do paciente, só irá atrapalhar o nosso modelo! Ou seja, podemos descartar tal pessoa.Também perceba que existem valores faltantes no dataframe, e a resposta mais imediata para estes é preenchê-los de acordo com o momento anterior ou posterior a aquele valor vazio, pois não haveriam oscilações tão drásticas de um momento para o outro, no exame de sangue.Sendo assim, precisamos fazer 2 coisas antes de modelarmos os dados:- Preencher os valores contínuos faltantes com os números anteriores ou posteriores na coluna;- Retirar os pacientes que, teoricamente, foram direto para a UTI ("*WINDOW*" = "0-2").<jupyter_code># Selecionando as features que são contínuas
features_continuas = dados.iloc[:, 13:-2].columns
features_continuas
def preenche_tabela(df):
    features_continuas_colunas = df.iloc[:, 13:-2].columns
    features_continuas = df.groupby('PATIENT_VISIT_IDENTIFIER', as_index=False)[features_continuas_colunas].fillna(method='bfill').fillna(method='ffill')
    features_categoricas = df.iloc[:, :13]
    saida = df.iloc[:, -2:]
    dados_finais = pd.concat([features_categoricas, features_continuas, saida], ignore_index=True, axis=1)
    dados_finais.columns = df.columns
    return dados_finais
# Preenchendo os valores faltantes
dados_limpos = preenche_tabela(dados)
dados_limpos.head()
dados_limpos.describe()
# Retirando todos os valores faltantes que sobraram
dados_limpos = dados_limpos.dropna()
# Removendo os dados em que a "WINDOW" é igual a "0-2" e o "ICU" igual a 1 (direto pra UTI)
a_remover = dados_limpos.query('WINDOW == "0-2" and ICU == 1')['PATIENT_VISIT_IDENTIFIER'].values
dados_limpos = dados_limpos.query('PATIENT_VISIT_IDENTIFIER not in @a_remover')
display(dados_limpos.describe())<jupyter_output><empty_output><jupyter_text>Agora, para finalizar, eu vou querer apenas **um momento** do paciente, poderia ser qualquer um para falar a verdade. Sendo assim, apenas porque é o primeiro em que aparece, vou escolher todos os pacientes na janela "0-2". Para isso, temos que selecionar os pacientes no momento "0-2" que realmente foram para a UTI e os que não foram.<jupyter_code>def prepare_window(rows):
    if np.any(rows['ICU']):
        rows.loc[rows['WINDOW'] == '0-2', 'ICU'] = 1
    return rows.loc[rows['WINDOW'] == '0-2']
dados_limpos = dados_limpos.groupby('PATIENT_VISIT_IDENTIFIER').apply(prepare_window)
dados_limpos.head()
# Dropando as colunas não importantes para o modelo
dados_limpos = dados_limpos.drop(['PATIENT_VISIT_IDENTIFIER', 'WINDOW'], axis=1).reset_index(drop=True)
dados_limpos.head()
x_columns = dados_limpos.describe().columns

y = dados_limpos['ICU']
x = dados_limpos[x_columns].drop('ICU', axis=1)
np.random.seed(73246)
x_train, x_test, y_train, y_test = train_test_split(x, y, stratify=y)
modelo = DummyClassifier()
modelo.fit(x_train, y_train)

y_prediction = modelo.predict(x_test)
accuracy_score(y_test, y_prediction)
modelo = LogisticRegression()
modelo.fit(x_train, y_train)

y_prediction = modelo.predict(x_test)
accuracy_score(y_test, y_prediction)<jupyter_output>/opt/conda/lib/python3.7/site-packages/sklearn/linear_model/_logistic.py:764: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  extra_warning_msg=_LOGISTIC_SOLVER_CONVERGENCE_MSG)