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!