Como Construir um Sistema de Predição de Doenças Cardíacas com Python e Streamlit

Neste tutorial, vamos construir um sistema completo de análise e predição de doenças cardíacas usando Python, Streamlit e Machine Learning. Este projeto é perfeito para portfólios e aprendizado de ciência de dados aplicada à saúde.

📋 Pré-requisitos

  • Python 3.8+
  • Conhecimento básico de Python
  • Noções de pandas e machine learning

🚀 Passo 1: Configuração do Ambiente

Primeiro, vamos instalar as dependências necessárias:

bash

pip install streamlit pandas numpy matplotlib seaborn scikit-learn

🗂️ Passo 2: Estrutura do Projeto

Crie um arquivo heart_attack.py com a seguinte estrutura:

python

import streamlit as st
import pandas as pdimport numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
# Configuração da página
st.set_page_config(page_title="Predição Cardíaca", page_icon="", layout="wide")

📊 Passo 3: Carregamento e Cache de Dados

Vamos implementar uma função eficiente para carregar os dados:

python

@st.cache_data
def load_data():
    """
    Carrega o dataset de doenças cardíacas com cache para performance
    """
    try:
        data = pd.read_csv('./data/heart.csv')
        st.success("✅ Dados carregados com sucesso!")
        return data
    except FileNotFoundError:
        st.error("❌ Arquivo não encontrado. Verifique o caminho do dataset.")
        return None

def mostrar_info_dataset(df):
    """
    Mostra informações básicas sobre o dataset
    """
    st.subheader("📁 Informações do Dataset")
    
    col1, col2, col3 = st.columns(3)
    
    with col1:
        st.metric("Total de Pacientes", len(df))
    
    with col2:
        st.metric("Variáveis", len(df.columns))
    
    with col3:
        taxa_doenca = (df['output'].mean() * 100)
        st.metric("Taxa de Doença Cardíaca", f"{taxa_doenca:.1f}%")
    
    # Expander com detalhes técnicos
    with st.expander("🔍 Detalhes Técnicos do Dataset"):
        st.write("**Variáveis incluídas:**")
        st.write("- `age`: Idade do paciente")
        st.write("- `sex`: Sexo (1 = masculino, 0 = feminino)")
        st.write("- `cp`: Tipo de dor no peito")
        st.write("- `trtbps`: Pressão arterial em repouso")
        st.write("- `chol`: Colesterol sérico")
        st.write("- `fbs`: Açúcar no sangue em jejum")
        st.write("- `restecg`: Resultados eletrocardiográficos em repouso")
        st.write("- `thalachh`: Frequência cardíaca máxima alcançada")
        st.write("- `exng`: Angina induzida por exercício")
        st.write("- `oldpeak`: Depressão ST induzida por exercício")
        st.write("- `slp`: Inclinação do segmento ST")
        st.write("- `caa`: Número de vasos principais coloridos")
        st.write("- `thall`: Talassemia")
        st.write("- `output`: Diagnóstico (0 = sem doença, 1 = com doença)")

🎨 Passo 4: Estilização com CSS

Vamos criar um design atrativo para nossa aplicação:

python

def aplicar_estilos():
    """
    Aplica estilos CSS customizados à aplicação
    """
    custom_css = """
    <style>
    .main-header {
        font-size: 3rem;
        color: #FF4B4B;
        text-align: center;
        margin-bottom: 2rem;
        font-weight: bold;
    }
    .section-header {
        font-size: 1.8rem;
        color: #FF4B4B;
        border-bottom: 2px solid #FF4B4B;
        padding-bottom: 0.5rem;
        margin-top: 2rem;
    }
    .metric-card {
        background-color: #262730;
        padding: 1rem;
        border-radius: 10px;
        border-left: 4px solid #FF4B4B;
    }
    .insight-box {
        background-color: #0E1117;
        padding: 1rem;
        border-radius: 10px;
        border: 1px solid #FF4B4B;
        margin: 1rem 0;
    }
    </style>
    """
    st.markdown(custom_css, unsafe_allow_html=True)

📈 Passo 5: Análise Exploratória Interativa

Vamos criar visualizações interativas para entender os dados:

python

def criar_analise_exploratoria(df):
    """
    Cria seção de análise exploratória com visualizações
    """
    st.markdown("<div class='section-header'>📊 Análise Exploratória</div>", 
                unsafe_allow_html=True)
    
    # Abas para organizar as análises
    tab1, tab2, tab3, tab4 = st.tabs([
        "📋 Estatísticas", 
        "📈 Distribuições", 
        "🔗 Correlações", 
        "🎯 Insights"
    ])
    
    with tab1:
        criar_estatisticas_descritivas(df)
    
    with tab2:
        criar_visualizacoes_distribuicao(df)
    
    with tab3:
        criar_analise_correlacao(df)
    
    with tab4:
        criar_insights_avancados(df)

def criar_estatisticas_descritivas(df):
    """
    Cria visualizações de estatísticas descritivas
    """
    st.subheader("Estatísticas Descritivas")
    
    # Métricas principais em cards
    col1, col2, col3, col4 = st.columns(4)
    
    with col1:
        with st.container():
            st.markdown("<div class='metric-card'>", unsafe_allow_html=True)
            st.metric("Média Idade", f"{df['age'].mean():.1f} anos")
            st.markdown("</div>", unsafe_allow_html=True)
    
    with col2:
        with st.container():
            st.markdown("<div class='metric-card'>", unsafe_allow_html=True)
            st.metric("Pressão Arterial Média", f"{df['trtbps'].mean():.1f} mmHg")
            st.markdown("</div>", unsafe_allow_html=True)
    
    with col3:
        with st.container():
            st.markdown("<div class='metric-card'>", unsafe_allow_html=True)
            st.metric("Colesterol Médio", f"{df['chol'].mean():.1f} mg/dl")
            st.markdown("</div>", unsafe_allow_html=True)
    
    with col4:
        with st.container():
            st.markdown("<div class='metric-card'>", unsafe_allow_html=True)
            st.metric("Freq. Cardíaca Máx.", f"{df['thalachh'].mean():.1f} bpm")
            st.markdown("</div>", unsafe_allow_html=True)
    
    # Tabela de estatísticas
    st.dataframe(df.describe(), use_container_width=True)

def criar_visualizacoes_distribuicao(df):
    """
    Cria visualizações de distribuição dos dados
    """
    st.subheader("Distribuições das Variáveis")
    
    # Seleção interativa de variável
    variavel = st.selectbox(
        "Selecione a variável para visualizar:",
        ['age', 'trtbps', 'chol', 'thalachh', 'oldpeak']
    )
    
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
    
    # Histograma
    sns.histplot(data=df, x=variavel, hue='output', ax=ax1, kde=True)
    ax1.set_title(f'Distribuição de {variavel} por Diagnóstico')
    
    # Boxplot
    sns.boxplot(data=df, x='output', y=variavel, ax=ax2)
    ax2.set_title(f'Boxplot de {variavel} por Diagnóstico')
    ax2.set_xticklabels(['Sem Doença', 'Com Doença'])
    
    st.pyplot(fig)
    
    # Insight automático
    with st.container():
        st.markdown("<div class='insight-box'>", unsafe_allow_html=True)
        st.write("💡 **Insight:** Esta visualização mostra como a variável selecionada se distribui entre pacientes com e sem doença cardíaca.")
        st.markdown("</div>", unsafe_allow_html=True)

🤖 Passo 6: Modelo de Machine Learning Robusto

Vamos implementar um modelo com validação e métricas:

python

class ModeloCardiaco:
    def __init__(self):
        self.modelo = None
        self.X_train = None
        self.X_test = None
        self.y_train = None
        self.y_test = None
    
    def preparar_dados(self, df):
        """
        Prepara os dados para treinamento
        """
        X = df.drop('output', axis=1)
        y = df['output']
        
        self.X_train, self.X_test, self.y_train, self.y_test = train_test_split(
            X, y, test_size=0.2, random_state=42, stratify=y
        )
        
        return X, y
    
    def treinar_modelo(self, n_estimators=100):
        """
        Treina o modelo Random Forest
        """
        self.modelo = RandomForestClassifier(
            n_estimators=n_estimators,
            random_state=42,
            max_depth=10,
            min_samples_split=5
        )
        
        self.modelo.fit(self.X_train, self.y_train)
        
        return self.modelo
    
    def avaliar_modelo(self):
        """
        Avalia o modelo e retorna métricas
        """
        if self.modelo is None:
            raise ValueError("Modelo não foi treinado ainda")
        
        y_pred = self.modelo.predict(self.X_test)
        y_pred_proba = self.modelo.predict_proba(self.X_test)[:, 1]
        
        # Cálculo de métricas
        acuracia = accuracy_score(self.y_test, y_pred)
        
        # Validação cruzada
        cv_scores = cross_val_score(self.modelo, self.X_train, self.y_train, cv=5)
        
        return {
            'acuracia': acuracia,
            'cv_mean': cv_scores.mean(),
            'cv_std': cv_scores.std(),
            'matriz_confusao': confusion_matrix(self.y_test, y_pred),
            'relatorio_classificacao': classification_report(self.y_test, y_pred)
        }
    
    def prever(self, dados):
        """
        Faz predição para novos dados
        """
        if self.modelo is None:
            raise ValueError("Modelo não foi treinado ainda")
        
        probabilidade = self.modelo.predict_proba(dados)[0][1]
        return probabilidade

def criar_secao_modelo(df):
    """
    Cria a seção completa do modelo de machine learning
    """
    st.markdown("<div class='section-header'>🤖 Modelo de Predição</div>", 
                unsafe_allow_html=True)
    
    # Treinar modelo
    if st.button("🚀 Treinar Modelo", type="primary"):
        with st.spinner("Treinando modelo..."):
            modelo_cardiaco = ModeloCardiaco()
            X, y = modelo_cardiaco.preparar_dados(df)
            modelo_cardiaco.treinar_modelo()
            metricas = modelo_cardiaco.avaliar_modelo()
            
            # Mostrar resultados
            col1, col2 = st.columns(2)
            
            with col1:
                st.metric("Acurácia", f"{metricas['acuracia']:.2%}")
                st.metric("Validação Cruzada", f"{metricas['cv_mean']:.2%}{metricas['cv_std']:.2%})")
            
            with col2:
                st.write("**Matriz de Confusão:**")
                st.dataframe(metricas['matriz_confusao'])
            
            # Salvar modelo na session state
            st.session_state.modelo = modelo_cardiaco
            st.session_state.metricas = metricas
    
    # Mostrar métricas se modelo já foi treinado
    if 'metricas' in st.session_state:
        st.success("✅ Modelo treinado e avaliado com sucesso!")
        
        with st.expander("📊 Ver Relatório Detalhado"):
            st.text(st.session_state.metricas['relatorio_classificacao'])
    
    return st.session_state.get('modelo', None)

🎯 Passo 7: Interface de Predição

Vamos criar um formulário interativo para predições:

python

def criar_formulario_predicao(modelo):
    """
    Cria formulário para entrada de dados e predição
    """
    st.markdown("<div class='section-header'>🎯 Fazer Predição</div>", 
                unsafe_allow_html=True)
    
    with st.form("formulario_predicao"):
        st.write("**Informações do Paciente:**")
        
        col1, col2 = st.columns(2)
        
        with col1:
            idade = st.slider("Idade", 20, 80, 50)
            sexo = st.selectbox("Sexo", ["Masculino", "Feminino"])
            tipo_dor = st.selectbox(
                "Tipo de Dor no Peito", 
                ["0 - Típica", "1 - Atípica", "2 - Não anginosa", "3 - Assintomática"]
            )
            pressao_arterial = st.slider("Pressão Arterial (mmHg)", 90, 200, 120)
            colesterol = st.slider("Colesterol (mg/dl)", 100, 400, 200)
        
        with col2:
            glicemia = st.selectbox("Glicemia > 120 mg/dl", ["Não", "Sim"])
            freq_card_max = st.slider("Freq. Cardíaca Máxima", 60, 220, 150)
            depressao_st = st.slider("Depressão ST", 0.0, 6.0, 1.0, 0.1)
            vasos_principais = st.selectbox("Vasos Principais", ["0", "1", "2", "3"])
            talassemia = st.selectbox("Talassemia", ["0", "1", "2", "3"])
        
        # Botão de predição
        submitted = st.form_submit_button("🔍 Calcular Risco", type="primary")
        
        if submitted and modelo is not None:
            # Processar entradas
            dados_processados = processar_entradas(
                idade, sexo, tipo_dor, pressao_arterial, colesterol,
                glicemia, freq_card_max, depressao_st, vasos_principais, talassemia
            )
            
            # Fazer predição
            probabilidade = modelo.prever(dados_processados)
            
            # Mostrar resultado
            mostrar_resultado(probabilidade)

def processar_entradas(idade, sexo, tipo_dor, pressao, colesterol, 
                      glicemia, freq_card, depressao_st, vasos, talassemia):
    """
    Processa as entradas do usuário para o formato do modelo
    """
    sexo_num = 1 if sexo == "Masculino" else 0
    tipo_dor_num = int(tipo_dor.split(" - ")[0])
    glicemia_num = 1 if glicemia == "Sim" else 0
    
    dados = pd.DataFrame({
        'age': [idade],
        'sex': [sexo_num],
        'cp': [tipo_dor_num],
        'trtbps': [pressao],
        'chol': [colesterol],
        'fbs': [glicemia_num],
        'restecg': [0],  # Valor padrão
        'thalachh': [freq_card],
        'exng': [0],     # Valor padrão
        'oldpeak': [depressao_st],
        'slp': [1],      # Valor padrão
        'caa': [int(vasos)],
        'thall': [int(talassemia)]
    })
    
    return dados

def mostrar_resultado(probabilidade):
    """
    Mostra o resultado da predição de forma visual
    """
    st.markdown("---")
    st.subheader("📊 Resultado da Predição")
    
    # Barra de progresso visual
    st.write(f"**Probabilidade de Doença Cardíaca:** {probabilidade:.1%}")
    st.progress(float(probabilidade))
    
    # Interpretação do resultado
    if probabilidade < 0.3:
        st.success("🎉 **Baixo Risco** - Probabilidade baixa de doença cardíaca")
    elif probabilidade < 0.7:
        st.warning("⚠️ **Risco Moderado** - Recomendada avaliação médica")
    else:
        st.error("🚨 **Alto Risco** - Procure avaliação médica urgente")
    
    # Disclaimer médico
    with st.expander("⚠️ Aviso Importante"):
        st.warning("""
        Este sistema é uma ferramenta educacional e **NÃO substitui** o diagnóstico médico profissional. 
        Consulte sempre um médico para avaliações de saúde.
        
        **Limitações:**
        - Baseado em dados históricos limitados
        - Não considera histórico familiar
        - Não substitui exames clínicos
        - Precisão do modelo: ~80-85%
        """)

🔧 Passo 8: Função Principal

Finalmente, vamos integrar tudo na função principal:

python

def main():
    """
    Função principal da aplicação
    """
    # Aplicar estilos
    aplicar_estilos()
    
    # Cabeçalho
    st.markdown("<h1 class='main-header'>❤️ Predição de Doenças Cardíacas</h1>", 
                unsafe_allow_html=True)
    
    st.write("Sistema inteligente para análise e predição de riscos cardíacos usando Machine Learning")
    
    # Carregar dados
    df = load_data()
    
    if df is not None:
        # Mostrar informações do dataset
        mostrar_info_dataset(df)
        
        # Análise exploratória
        criar_analise_exploratoria(df)
        
        # Seção do modelo
        modelo = criar_secao_modelo(df)
        
        # Formulário de predição (após modelo treinado)
        if modelo is not None:
            criar_formulario_predicao(modelo)
        else:
            st.info("💡 Treine o modelo primeiro para habilitar as predições")
    
    # Rodapé
    st.markdown("---")
    st.markdown(
        "Desenvolvido com ❤️ usando Streamlit | "
        "[![Repo](https://img.shields.io/badge/GitHub-Repositório-blue)](https://github.com)"
    )

if __name__ == "__main__":
    main()

Como Rodar o Algoritimo em um Ambiente Virtual Usando Python

1 Vamos criar a seguinte estrutura de arquivos:

heart_prediction_app/
├── heart_attack_improved.py
├── requirements.txt
└── data/
 └── heart.csv

2 requirements.txt

streamlit==1.28.0
pandas==2.0.3
numpy==1.24.3
matplotlib==3.7.2
seaborn==0.12.2
scikit-learn==1.3.0

3 Criar o ambiente virtual com python

# 1. Entre no Diretório heart_prediction_app e rode:
python -m venv heart_env

# 2. Ativar ambiente (Linux/Mac)
source heart_env/bin/activate

# 3. Ativar ambiente (Windows)
heart_env\Scripts\activate

# 4. Instalar dependências
pip install -r requirements.txt

# 4.1 Se houver erro no requirements.txt, instale uma dependência de cada vez.
pip install streamlit pandas numpy matplotlib seaborn scikit-learn

# 5. Executar aplicação
streamlit run heart_attack_improved.py

🎉 Conclusão

Parabéns! Você construiu um sistema completo de predição de doenças cardíacas com:

  • Análise exploratória interativa
  • Modelo de ML com validação robusta
  • Interface moderna e responsiva
  • Formulário de predição intuitivo
  • Visualizações profissionais

🚀 Próximos Passos

  1. Melhorias técnicas:
    • Adicionar mais algoritmos de ML
    • Implementar feature engineering
    • Adicionar persistência do modelo
  2. Funcionalidades:
    • Exportar relatórios
    • Histórico de predições
    • Múltiplos datasets
  3. Deploy:
    • Docker

Este tutorial mostra como construir uma aplicação de data science completa. Ideal para aprender Streamlit, ML aplicado e desenvolvimento de dashboards interativos!

projeto completo no github

https://github.com/jonatan777/heart_prediction_app