Article image
Paulo Munhoz
Paulo Munhoz06/06/2025 09:49
Share
WEX - End to End EngineeringRecommended for youWEX - End to End Engineering

Estudar análise de dados é fácil

    Tudo começou após uma aula aqui na plataforma e eu pensei: "Ah vou fazer sozinho!"

    O que poderia não acontecer? É, não aconteceu nada em dois dias hahahahaha

    Então resolvi criar um código para gerar o dataset com informações ficticias e até mesmo difíceis de usar ( começar com tabela pronta é para os fracos e nada desse Titanic furado ai!)

    Dia 01:

    Saiu um código porco para gerar o CSV, mas usei a iA e arrumou tudo ( não posso sair por ai falando que sou mestre em Python mas dou meus pulos).

    Dia 02:

    Gereou o CSV e fiquei olhando para a tela do excel e alternando com o Power BI igual aquele meme do cachorro sentado no tabuleiro de monopoly... (não tava entendendo nada mas tô participando)

    Dia 03: Um perfil do LinkedIn comentou e fez umas postagens sobre o Streamlit e lembrei que eu ja tnha algum contato com a Lib... Bora!!!

    Dia 06 (isso mesmo você não leu errado):

    Depois de muito suor, sangue e lágrimas.... Mais um código bem porco... Lembrei da iA, eu sei onde quero chegar mas não sei como...

    Dia 07: "Dona iA pega esse código aqui e com base no que ele faz, me de umas dicas de o que fazer para gerar um dashboard em streamlit" PAH!!!! a mágica da tecnologia!!!

    Dia 10: Mais suor e lágrimas, nem Google, nem nenhuma das 3 iAs que conheço e nem nos fóruns da Lib e nem no StackOverflow, encontrei a resposta de porque a lib Plotly do python não funciona na nuvem da Streamlit community.

    Mas o código funciona na máquina local então chega de enrolar e encher você de história!

    Abaixo um dos código mais legais que eu já fiz!

    LEMBRANDO BEM, O CÓDIGO GERA UM DATASET ALEATÓRIO SE NÃO HOUVER NENHUM JÁ CRIADO NA PASTA, SE NÃO ELE USA O QUE ESTÁ LÁ

    import streamlit as st
    import pandas as pd
    import plotly.express as px
    import plotly.graph_objects as go
    import numpy as np
    import os
    from itertools import product
    
    
    # Configuração da página
    st.set_page_config(page_title="DASHBOARD DE ANÁLISE DE CRÉDITO", layout="wide")
    
    
    
    # Função para criar o dataset
    def create_dataset():
      np.random.seed(42)
      n = 15000  # Número de clientes
      data = {
          "ID_CLIENTE": range(1, n + 1),
          "IDADE": np.random.randint(18, 81, n),
          "RENDA_MENSAL": np.round(
              np.random.normal(2000, 20001, n).clip(min=1000, max=20000), 2
          ),
          "SCORE_CREDITO": np.random.randint(190, 999, n),
          "TEMPO_RESIDENCIA": np.random.randint(0, 41, n),
          "DIVIDA_ATUAL": np.round(np.random.exponential(1000, n).clip(max=50000), 2),
          "HISTORICO_INADIMPLENCIA": np.random.choice(["NÃO", "SIM"], n, p=[0.8, 0.2]),
          "EMPREGO": np.random.choice(
              ["CLT", "AUTÔNOMO", "DESEMPREGADO"], n, p=[0.7, 0.2, 0.1]
          ),
          "ESTADO_CIVIL": np.random.choice(
              ["SOLTEIRO", "CASADO", "DIVORCIADO"], n, p=[0.4, 0.5, 0.1]
          ),
          "TEMPO_EMPREGO": np.random.randint(0, 41, n),
          "VALOR_SOLICITADO": np.round(
              np.random.randint(5000, 300001, n).astype(float), 2
          ),
          "APROVADO": [
              "APROVADO" if s > 600 and r > 3000 else "REPROVADO"
              for s, r in zip(
                  np.random.randint(300, 851, n), np.random.normal(5000, 2000, n)
              )
          ],
      }
      df = pd.DataFrame(data)
      df.to_csv("base_credito_ficticia.csv", index=False)
      return df
    
    
    
    # Verificar se o arquivo já existe
    if not os.path.exists("base_credito_ficticia.csv"):
      df = create_dataset()
    else:
      df = pd.read_csv("base_credito_ficticia.csv")
    
    
    # Transformar textos em caixa alta
    for col in df.select_dtypes(include="object").columns:
      df[col] = df[col].str.upper()
    
    
    # Criar coluna numérica para APROVADO
    df["APROVADO_NUM"] = df["APROVADO"].map({"APROVADO": 1, "REPROVADO": 0})
    
    
    # Criar faixas etárias
    df["FAIXA_ETARIA"] = pd.cut(
      df["IDADE"],
      bins=[18, 30, 40, 50, 60, 80],
      labels=["18-30", "31-40", "41-50", "51-60", "61-80"],
    )
    
    
    # Criar faixas de score
    df["FAIXA_SCORE"] = pd.cut(
      df["SCORE_CREDITO"],
      bins=[0, 300, 500, 700, 850, 1000],
      labels=[
          "Muito Baixo (300-499)",
          "Baixo (500-699)",
          "Médio (700-849)",
          "Bom (850-999)",
          "Excelente (1000)",
      ],
    )
    
    
    # Criar razão valor solicitado / renda anual
    df["RAZAO_VALOR_RENDA"] = df["VALOR_SOLICITADO"] / (df["RENDA_MENSAL"] * 12)
    
    
    # Título do Dashboard
    st.title("📊 DASHBOARD DE ANÁLISE DE CRÉDITO")
    
    
    # Filtros interativos
    st.sidebar.header("🔍 FILTROS")
    estado_civil = st.sidebar.multiselect(
      "ESTADO CIVIL",
      options=df["ESTADO_CIVIL"].unique(),
      default=df["ESTADO_CIVIL"].unique(),
      help="Selecione os estados civis para análise",
    )
    emprego = st.sidebar.multiselect(
      "TIPO DE EMPREGO",
      options=df["EMPREGO"].unique(),
      default=df["EMPREGO"].unique(),
      help="Selecione os tipos de vínculo empregatício",
    )
    idade_range = st.sidebar.slider(
      "FAIXA ETÁRIA",
      min_value=int(df["IDADE"].min()),
      max_value=int(df["IDADE"].max()),
      value=(int(df["IDADE"].min()), int(df["IDADE"].max())),
      help="Selecione a faixa etária desejada",
    )
    score_range = st.sidebar.slider(
      "SCORE DE CRÉDITO",
      min_value=int(df["SCORE_CREDITO"].min()),
      max_value=int(df["SCORE_CREDITO"].max()),
      value=(int(df["SCORE_CREDITO"].min()), int(df["SCORE_CREDITO"].max())),
      help="Selecione o range de score desejado",
    )
    
    
    df_filtered = df[
      df["ESTADO_CIVIL"].isin(estado_civil)
      & df["EMPREGO"].isin(emprego)
      & (df["IDADE"].between(idade_range[0], idade_range[1]))
      & (df["SCORE_CREDITO"].between(score_range[0], score_range[1]))
    ]
    
    
    # Verificação de dados filtrados
    if df_filtered.empty:
      st.warning(
          "⚠️ Nenhum dado encontrado com os filtros atuais. Ajuste os filtros e tente novamente."
      )
      st.stop()
    
    
    # Seção de KPIs
    st.header("📈 VISÃO GERAL")
    col1, col2, col3, col4 = st.columns(4)
    col1.metric("👥 Total de Clientes", f"{len(df_filtered):,}".replace(",", "."))
    col2.metric(
      "✅ Taxa de Aprovação",
      f"{df_filtered['APROVADO_NUM'].mean():.1%}",
      help="Percentual de clientes aprovados no crédito",
    )
    col3.metric(
      "⚠️ Inadimplência",
      f"{df_filtered['HISTORICO_INADIMPLENCIA'].eq('SIM').mean():.1%}",
      help="Percentual de clientes com histórico de inadimplência",
    )
    col4.metric(
      "🏆 Score Médio",
      f"{df_filtered['SCORE_CREDITO'].mean():.0f}",
      help="Média do score de crédito dos clientes filtrados",
    )
    
    
    # Abas para diferentes análises
    tab1, tab2, tab3 = st.tabs(
      ["👥 Análise Demográfica", "💰 Análise Financeira", "📉 Risco de Crédito"]
    )
    
    
    with tab1:
      st.header("👥 Análise Demográfica")
    
    
      col1, col2 = st.columns(2)
    
    
      with col1:
          st.subheader("Distribuição por Faixa Etária")
          fig = px.pie(
              df_filtered,
              names="FAIXA_ETARIA",
              hole=0.3,
              color_discrete_sequence=px.colors.sequential.RdBu,
              labels={"FAIXA_ETARIA": "Faixa Etária"},
              title="Distribuição Percentual por Faixa Etária",
          )
          fig.update_traces(textposition="inside", textinfo="percent+label")
          st.plotly_chart(fig, use_container_width=True)
    
    
      with col2:
          st.subheader("Taxa de Aprovação por Estado Civil e Emprego")
          try:
              pivot = df_filtered.pivot_table(
                  index="ESTADO_CIVIL",
                  columns="EMPREGO",
                  values="APROVADO_NUM",
                  aggfunc="mean",
              )
              fig = px.imshow(
                  pivot,
                  text_auto=True,
                  aspect="auto",
                  color_continuous_scale="Blues",
                  labels=dict(
                      x="Tipo de Emprego", y="Estado Civil", color="Taxa de Aprovação"
                  ),
                  title="Taxa de Aprovação (%) por Estado Civil e Tipo de Emprego",
              )
              st.plotly_chart(fig, use_container_width=True)
          except Exception as e:
              st.warning(
                  "Não foi possível gerar o gráfico de calor com os dados filtrados."
              )
    
    
      st.subheader("Distribuição de Scores por Faixa Etária")
      fig = px.box(
          df_filtered,
          x="FAIXA_ETARIA",
          y="SCORE_CREDITO",
          color="APROVADO",
          color_discrete_map={"APROVADO": "green", "REPROVADO": "red"},
          labels={"FAIXA_ETARIA": "Faixa Etária", "SCORE_CREDITO": "Score de Crédito"},
          title="Distribuição de Scores de Crédito por Faixa Etária e Status de Aprovação",
      )
      fig.update_layout(boxmode="group")
      st.plotly_chart(fig, use_container_width=True)
    
    
    with tab2:
      st.header("💰 Análise Financeira")
    
    
      col1, col2 = st.columns(2)
    
    
      with col1:
          st.subheader("Renda vs. Valor Solicitado")
          fig = px.scatter(
              df_filtered,
              x="RENDA_MENSAL",
              y="VALOR_SOLICITADO",
              color="APROVADO",
              color_discrete_map={"APROVADO": "green", "REPROVADO": "red"},
              trendline="lowess",
              opacity=0.7,
              hover_data=["SCORE_CREDITO", "IDADE", "EMPREGO"],
              labels={
                  "RENDA_MENSAL": "Renda Mensal (R$)",
                  "VALOR_SOLICITADO": "Valor Solicitado (R$)",
                  "APROVADO": "Status",
              },
              title="Relação entre Renda Mensal e Valor Solicitado",
          )
          fig.update_layout(legend_title_text="Status de Aprovação")
          st.plotly_chart(fig, use_container_width=True)
    
    
      with col2:
          st.subheader("Razão Valor Solicitado/Renda Anual")
          fig = px.histogram(
              df_filtered,
              x="RAZAO_VALOR_RENDA",
              color="APROVADO",
              color_discrete_map={"APROVADO": "green", "REPROVADO": "red"},
              nbins=30,
              barmode="overlay",
              opacity=0.6,
              labels={
                  "RAZAO_VALOR_RENDA": "Razão (Valor Solicitado / Renda Anual)",
                  "count": "Número de Clientes",
              },
              title="Distribuição da Razão entre Valor Solicitado e Renda Anual",
          )
          fig.add_vline(
              x=5,
              line_dash="dash",
              line_color="red",
              annotation_text="Limite Recomendado (5x)",
              annotation_position="top",
          )
          fig.update_layout(legend_title_text="Status de Aprovação")
          st.plotly_chart(fig, use_container_width=True)
    
    
      st.subheader("Dívida Atual vs. Score de Crédito")
      fig = px.scatter(
          df_filtered,
          x="DIVIDA_ATUAL",
          y="SCORE_CREDITO",
          color="APROVADO",
          color_discrete_map={"APROVADO": "green", "REPROVADO": "red"},
          size="VALOR_SOLICITADO",
          hover_name="EMPREGO",
          opacity=0.7,
          labels={
              "DIVIDA_ATUAL": "Dívida Atual (R$)",
              "SCORE_CREDITO": "Score de Crédito",
              "VALOR_SOLICITADO": "Valor Solicitado (R$)",
              "APROVADO": "Status",
          },
          title="Relação entre Dívida Atual e Score de Crédito",
      )
      fig.update_layout(legend_title_text="Status de Aprovação")
      st.plotly_chart(fig, use_container_width=True)
    
    
    with tab3:
      st.header("📉 Análise de Risco")
    
    
      col1, col2 = st.columns(2)
    
    
      with col1:
          st.subheader("Inadimplência por Segmento")
    
    
          # Cria DataFrame agregado para garantir combinações válidas
          sunburst_df = (
              df_filtered.groupby(["FAIXA_ETARIA", "EMPREGO", "HISTORICO_INADIMPLENCIA"])
              .size()
              .reset_index(name="COUNT")
          )
    
    
          # Cria todas combinações possíveis para preencher missing paths
          all_combinations = list(
              product(
                  df_filtered["FAIXA_ETARIA"].unique(),
                  df_filtered["EMPREGO"].unique(),
                  df_filtered["HISTORICO_INADIMPLENCIA"].unique(),
              )
          )
    
    
          complete_df = pd.DataFrame(
              all_combinations,
              columns=["FAIXA_ETARIA", "EMPREGO", "HISTORICO_INADIMPLENCIA"],
          )
          complete_df = complete_df.merge(sunburst_df, how="left").fillna(0)
    
    
          if len(complete_df) > 0:
              fig = px.sunburst(
                  complete_df,
                  path=["FAIXA_ETARIA", "EMPREGO", "HISTORICO_INADIMPLENCIA"],
                  values="COUNT",
                  color="HISTORICO_INADIMPLENCIA",
                  color_discrete_map={"SIM": "#FF7F0E", "NÃO": "#1F77B4"},
                  branchvalues="total",
                  title="Distribuição Hierárquica da Inadimplência",
                  labels={"COUNT": "Número de Clientes"},
              )
              st.plotly_chart(fig, use_container_width=True)
          else:
              st.warning("Dados insuficientes para o gráfico sunburst.")
    
    
      with col2:
          st.subheader("Score de Crédito por Categoria")
          if not df_filtered.empty:
              fig = px.violin(
                  df_filtered,
                  x="FAIXA_SCORE",
                  y="SCORE_CREDITO",
                  color="APROVADO",
                  color_discrete_map={"APROVADO": "green", "REPROVADO": "red"},
                  box=True,
                  points="all",
                  labels={
                      "FAIXA_SCORE": "Categoria de Score",
                      "SCORE_CREDITO": "Score de Crédito",
                      "APROVADO": "Status",
                  },
                  title="Distribuição de Scores por Categoria e Status de Aprovação",
              )
              fig.update_layout(legend_title_text="Status de Aprovação")
              st.plotly_chart(fig, use_container_width=True)
    
    
      st.subheader("Matriz de Correlação entre Variáveis")
      numeric_cols = df_filtered.select_dtypes(include=["int64", "float64"]).columns
      if len(numeric_cols) > 0:
          corr = df_filtered[numeric_cols].corr()
          fig = px.imshow(
              corr,
              text_auto=True,
              aspect="auto",
              color_continuous_scale="RdBu",
              range_color=[-1, 1],
              title="Correlação entre Variáveis Numéricas",
          )
          st.plotly_chart(fig, use_container_width=True)
      else:
          st.warning("Nenhuma coluna numérica para calcular correlação.")
    
    
    # Rodapé
    st.sidebar.markdown("---")
    st.sidebar.markdown("**Dashboard de Análise de Crédito**")
    st.sidebar.markdown("Versão 3.0 - Julho 2024")
    st.sidebar.markdown(
      "Desenvolvido por [Paulo Munhoz](https://www.linkedin.com/in/paulomunhoz/)"
    )
    st.sidebar.markdown(
      "*Este dashboard foi criado para fins educacionais e de demonstração.*\n "
      "*Os dados são fictícios e não refletem informações reais.*"
    )
    
    
    

    Ah, também está o requeriments.txt:requeriments.txt

    altair==5.5.0
    altgraph==0.17.4
    attrs==25.3.0
    beautifulsoup4==4.13.3
    blinker==1.9.0
    bs4==0.0.2
    cachetools==5.5.2
    certifi==2025.4.26
    charset-normalizer==3.4.2
    click==8.2.1
    colorama==0.4.6
    contourpy==1.3.2
    cvzone==1.6.1
    cycler==0.12.1
    et_xmlfile==2.0.0
    fonttools==4.58.1
    frozendict==2.4.6
    gitdb==4.0.12
    GitPython==3.1.44
    idna==3.10
    Jinja2==3.1.6
    joblib==1.5.1
    jsonschema==4.24.0
    jsonschema-specifications==2025.4.1
    kagglehub==0.3.12
    kiwisolver==1.4.8
    MarkupSafe==3.0.2
    matplotlib==3.10.3
    multitasking==0.0.11
    mysql-connector-python==9.3.0
    narwhals==1.41.0
    numpy==2.2.6
    opencv-python==4.11.0.86
    openpyxl==3.1.5
    packaging==24.2
    pandas==2.2.3
    patsy==1.0.1
    peewee==3.17.9
    pefile==2023.2.7
    pillow==11.2.1
    platformdirs==4.3.7
    plotly==6.1.2
    protobuf==6.31.1
    pyarrow==20.0.0
    pydeck==0.9.1
    pyFirmata==1.1.0
    pyinstaller==6.12.0
    pyinstaller-hooks-contrib==2025.1
    pyparsing==3.2.3
    PyPDF2==3.0.1
    pyserial==3.5
    python-dateutil==2.9.0.post0
    pytimeparse2==1.7.1
    pytz==2025.2
    pywin32==310
    pywin32-ctypes==0.2.3
    PyYAML==6.0.2
    referencing==0.36.2
    requests==2.32.3
    rpds-py==0.25.1
    scikit-learn==1.6.1
    scipy==1.15.3
    seaborn==0.13.2
    setuptools==75.8.0
    simplejson==3.20.1
    six==1.17.0
    smmap==5.0.2
    soupsieve==2.6
    sqlite3-to-mysql==2.4.0
    statsmodels==0.14.4
    streamlit==1.45.1
    tabulate==0.9.0
    tenacity==9.1.2
    threadpoolctl==3.6.0
    toml==0.10.2
    tornado==6.5.1
    tqdm==4.67.1
    types-python-dateutil==2.9.0.20250516
    typing_extensions==4.14.0
    tzdata==2025.2
    Unidecode==1.4.0
    urllib3==2.4.0
    watchdog==6.0.0
    xlwings==0.33.15
    yfinance==0.2.55
    
    
    

    Valeu pessoal! digam o que acharam da minha "piração" no mundo dos dados!

    Me sigam por aqui e la no meu LinkedIn /pauloavm

    Abraços!

    Share
    Recommended for you
    TONNIE - Java and AI in Europe
    Microsoft - Azure Administrator Certification (AZ-104)
    WEX - End to End Engineering
    Comments (2)
    Paulo Munhoz
    Paulo Munhoz - 06/06/2025 17:02

    Dio Community, eu não lidei com a frustração... hahahaha eu não desisti ainda! só que a cabeça acelera e eu quero fazer 1000 coisas ao mesmo tempo... assim que a adrenalina baixar eu respiro e consigo resolver esse problema!

    DIO Community
    DIO Community - 06/06/2025 14:28

    Excelente, Paulo! Que artigo inspirador sobre sua "piração" no mundo dos dados e como a IA te ajudou a construir um dashboard de análise de crédito em Streamlit! É fascinante ver sua persistência em meio aos desafios, mostrando que o aprendizado é uma jornada de suor e lágrimas.

    Considerando que "o código funciona na máquina local então chega de enrolar e encher você de história!", qual você diria que é o maior aprendizado que você obteve ao lidar com a frustração de não conseguir fazer a biblioteca Plotly funcionar na nuvem da Streamlit Community, mesmo com a IA e o Stack Overflow?

    Recommended for youWEX - End to End Engineering