Como pegar a base de salários que usamos em aula e criar um dashboard interativo com: filtros na barra lateral, métricas, gráficos (barras, box, histograma, scatter) e mapa (choropleth) — tudo num app web simples.
Resumo mental: Jupyter/Colab = exploração e narrativa. Streamlit = aplicação interativa “de verdade” (um mini-sistema) com atualização automática quando o usuário mexe nos filtros.
✅ Recomendado: rodar localmente (Linux/Windows/macOS).
⚠️ Colab: pode funcionar com “túneis”, mas é mais chato/instável para iniciantes.
- Thonny: muito simples, ótimo para iniciantes (leve e direto).
- Spyder: estilo “Matlab”, excelente para ciência de dados (um pouco mais pesado, mas bem didático).
- Geany ou Kate: super leves (editor + terminal) se você já está confortável.
Se quiser “funciona em qualquer lugar” e ainda leve, eu sugiro Thonny. Para data science com conforto, Spyder também é uma boa.
Como criar um ambiente isolado para não bagunçar o Python do sistema.
mkdir dashboard-salarios cd dashboard-salarios python3 -m venv .venv source .venv/bin/activate python -m pip install --upgrade pip pip install streamlit pandas plotly
mkdir dashboard-salarios cd dashboard-salarios py -m venv .venv .venv\Scripts\Activate.ps1 python -m pip install --upgrade pip pip install streamlit pandas plotly
Se o PowerShell bloquear scripts, use Prompt (cmd): .venv\Scripts\activate.bat
Por que isso importa? Cada projeto tem suas dependências. Com virtualenv, você evita conflitos e consegue repetir o setup em outra máquina.
Uma organização simples e limpa:
Baixe o arquivo salarios.csv e coloque na pasta dados/ do seu projeto:
Estrutura do dataset: 100 registros com dados reais de salários por senioridade, país e ano. Perfeito para testar todos os filtros e gráficos do dashboard.
📂 Como usar este dataset
- Clique no botão verde "Baixar salarios.csv"
- Crie a pasta
dados/na raiz do seu projeto - Mova o arquivo para
dados/salarios.csv - Execute o dashboard:
streamlit run app.py
O dataset contém 100 linhas com variação realista de salários (20k a 150k USD) distribuídos entre 2020-2024, 6 níveis de senioridade e 15 países diferentes.
dashboard-salarios/
.venv/
app.py
dados/
salarios.csv
requirements.txt
Dica didática: comece com app.py apenas. Depois divida em módulos se crescer.
Como instalar tudo de uma vez em outra máquina.
pip freeze > requirements.txt
pip install -r requirements.txt
Observação: pip freeze captura tudo. Para aula está ótimo; em produção, liste manualmente as principais.
Com upload próprio, filtros avançados, métricas comparativas e exemplo agro.
import streamlit as st
import pandas as pd
import plotly.express as px
import numpy as np
import io
# ==========================================
# GERADOR DE DADOS SINTÉTICOS (Fallback)
# ==========================================
def gerar_dados_salarios(n=1000):
np.random.seed(42)
senioridades = ['Júnior', 'Pleno', 'Sênior', 'Especialista']
paises = ['Brazil', 'United States', 'Canada', 'Germany', 'Japan', 'Australia']
empregos = ['CLT', 'PJ', 'Estágio', 'Tempo integral']
remoto = ['Sim', 'Não', 'Híbrido']
data = {
'salario_em_usd': np.concatenate([
np.random.normal(25000, 5000, 200),
np.random.normal(45000, 8000, 300),
np.random.normal(75000, 12000, 350),
np.random.normal(110000, 20000, 150)
]).astype(int),
'senioridade': np.repeat(senioridades, [200, 300, 350, 150]),
'ano': np.random.choice([2020, 2021, 2022, 2023, 2024], n),
'pais': np.random.choice(paises, n),
'tipo_emprego': np.random.choice(empregos, n),
'remoto': np.random.choice(remoto, n)
}
return pd.DataFrame(data)
# =========================
# Config e leitura de dados
# =========================
st.set_page_config(page_title="Dashboard de Salários", layout="wide")
st.title("🔴 AULA — Dashboard de Salários (Streamlit)")
st.write("Base de salários + filtros + gráficos interativos (Plotly).")
# --- GESTÃO DE CACHE E ARQUIVO ---
st.sidebar.header("📂 Gestão de Dados")
uploaded_file = st.sidebar.file_uploader("Ou faça upload da sua própria base CSV", type=["csv"])
if st.sidebar.button("♻️ Limpar Cache / Resetar"):
st.cache_data.clear()
st.rerun()
@st.cache_data
def carregar_dados(u_file):
if u_file is not None:
return pd.read_csv(u_file)
try:
df_local = pd.read_csv("dados/salarios.csv")
for c in df_local.select_dtypes(include="object").columns:
df_local[c] = df_local[c].astype(str).str.strip()
return df_local
except FileNotFoundError:
return None
df_bruto = carregar_dados(uploaded_file)
if df_bruto is None:
df = gerar_dados_salarios()
st.info("⚠️ Arquivo não encontrado localmente. Usando dados sintéticos gerados automaticamente.")
else:
df = df_bruto
st.info("Usando base de exemplo ou upload com sucesso!")
# Detecta coluna de salário
def detectar_coluna_salario(dataframe):
candidatos = ["salario_em_usd", "salary_in_usd", "usd", "salary_usd", "salario_usd"]
for c in candidatos:
if c in dataframe.columns: return c
return dataframe.select_dtypes(include=[np.number]).columns[0]
SAL = detectar_coluna_salario(df)
# Mapeamento de Colunas (Seu original)
COL_SEN = "senioridade"
COL_ANO = "ano"
COL_PAIS = "pais"
COL_EMP = "tipo_emprego"
COL_REM = "remoto"
# ==========================================
# Sidebar: filtros (Sua lógica de Multiselect e Quantis)
# ==========================================
st.sidebar.header("🎛️ Filtros")
# Salário máximo (Quantil 99 original)
sal_max_default = int(df[SAL].quantile(0.99))
sal_max = st.sidebar.slider("Salário máximo (USD)", 0, int(df[SAL].max()), sal_max_default, step=1000)
# Faixa salarial
sal_range = st.sidebar.slider(
"Faixa salarial (USD)",
int(df[SAL].min()),
int(df[SAL].max()),
(int(df[SAL].quantile(0.1)), int(df[SAL].quantile(0.9)))
)
d = df[(df[SAL] >= sal_range[0]) & (df[SAL] <= sal_range[1]) & (df[SAL] <= sal_max)].copy()
def filtro_select(col, label):
if col in d.columns:
opts = ["Todos"] + sorted(d[col].dropna().unique().tolist())
escolha = st.sidebar.selectbox(label, opts, index=0)
if escolha != "Todos":
return d[d[col] == escolha].copy()
return d
def filtro_multiselect(col, label, top=40):
if col in d.columns:
top_vals = d[col].value_counts().head(top).index.tolist()
opts = ["Todos"] + top_vals
escolha = st.sidebar.multiselect(label, opts, default=["Todos"])
if "Todos" not in escolha and len(escolha) > 0:
return d[d[col].isin(escolha)].copy()
return d
d = filtro_select(COL_ANO, "Ano")
d = filtro_select(COL_EMP, "Tipo de emprego")
d = filtro_select(COL_REM, "Remoto")
d = filtro_multiselect(COL_PAIS, "País (Top 40)", top=40)
d = filtro_select(COL_SEN, "Senioridade")
# ==========================================
# Métricas (Seu original com Comparativas)
# ==========================================
st.subheader("📌 Métricas rápidas")
c1, c2, c3, c4 = st.columns(4)
c1.metric("Registros", f"{len(d):,}")
c2.metric("Média (USD)", f"${d[SAL].mean():,.0f}" if len(d) else "-")
c3.metric("Mediana (USD)", f"${d[SAL].median():,.0f}" if len(d) else "-")
c4.metric("P90 (USD)", f"${d[SAL].quantile(0.90):,.0f}" if len(d) else "-")
st.markdown("---")
col1, col2 = st.columns(2)
col1.metric("Média GERAL (Sem filtro)", f"${df[SAL].mean():,.0f}")
col2.metric("Média FILTRADA", f"${d[SAL].mean():,.0f}",
delta=f"{d[SAL].mean() - df[SAL].mean():+,.0f}" if len(d) else None)
# ==========================================
# Exportação (Seu original com Kaleido)
# ==========================================
st.subheader("📥 Exportar dados")
col_export1, col_export2 = st.columns(2)
with col_export1:
csv_bytes = d.to_csv(index=False).encode("utf-8")
st.download_button("📄 Baixar CSV filtrado", data=csv_bytes, file_name="salarios_filtrado.csv", mime="text/csv")
with col_export2:
if st.button("📸 Salvar gráfico de barras como PNG"):
try:
import kaleido
# A variável fig_bar precisa ser definida antes
st.info("O gráfico será salvo localmente no servidor se o Kaleido estiver instalado.")
except ImportError:
st.warning("Instale kaleido: pip install kaleido")
# ==========================================
# Gráficos (Todos os seus originais)
# ==========================================
st.subheader("📊 Gráficos")
# 1) Barras
if COL_SEN in d.columns and len(d) > 0:
grp = d.groupby(COL_SEN)[SAL].mean().reset_index(name="media_salario")
fig_bar = px.bar(grp, x=COL_SEN, y="media_salario", text="media_salario", title="Média salarial por senioridade")
fig_bar.update_traces(texttemplate="$%{text:,.0f}", textposition="outside")
st.plotly_chart(fig_bar, use_container_width=True)
# 2) Boxplot
if COL_SEN in d.columns and len(d) > 0:
fig_box = px.box(d, x=COL_SEN, y=SAL, title="Distribuição salarial (Boxplot)")
st.plotly_chart(fig_box, use_container_width=True)
# 3) Histograma
if len(d) > 0:
st.plotly_chart(px.histogram(d, x=SAL, nbins=40, title="Histograma de Salários"), use_container_width=True)
# 4) Scatter
if COL_ANO in d.columns and len(d) > 0:
st.plotly_chart(px.scatter(d, x=COL_ANO, y=SAL, color=COL_SEN if COL_SEN in d.columns else None, title="Evolução Temporal"), use_container_width=True)
# 5) Mapa (Choropleth com sua regra de mín. 10 registros)
if COL_PAIS in d.columns and len(d) > 0:
mapa = d.groupby(COL_PAIS)[SAL].agg(media_salario="mean", qtd="size").reset_index()
mapa = mapa[mapa["qtd"] >= 10].copy()
if not mapa.empty:
fig_map = px.choropleth(mapa, locations=COL_PAIS, locationmode="country names", color="media_salario",
hover_data={"qtd": True, "media_salario": ":,.0f"}, title="Mapa Salarial")
st.plotly_chart(fig_map, use_container_width=True)
Nota didática: o Streamlit reexecuta o script ao mudar um filtro.
Por isso usamos @st.cache_data na leitura do CSV (fica rápido).
# dentro da pasta do projeto, com o venv ativo: streamlit run app.py
Ao rodar, abre em http://localhost:8501.
Você terá um “mini-sistema” web com:
- Filtros na lateral para explorar hipóteses.
- Métricas que mudam conforme o recorte.
- Gráficos interativos (zoom, hover, salvar imagem).
- Download do recorte filtrado em CSV (para relatório/atividade).
Atividade: formular 3 perguntas (hipóteses), testar no dashboard e justificar com prints/valores.
- Comece simples: 1 gráfico + 2 filtros. Só depois adicione mapa/scatter.
- Evite KeyError: sempre confira
df.columnse padronize nomes. - Outliers: use slider de salário máximo (p99) para não “achatar” o gráfico.
- País: se vier como sigla, converta para ISO-3 ou nomes antes do mapa.
Importante: Se o dataset tiver colunas com nomes diferentes (ex.: work_year,
employee_residence), ajuste COL_ANO/COL_PAIS no topo do app.py.
- Crie a pasta:
mkdir dashboard-salarios && cd dashboard-salarios - Crie ambiente virtual:
python -m venv .venv - Ative: Linux/mac:
source .venv/bin/activate| Windows:.venv\Scripts\activate - Instale tudo:
pip install streamlit pandas plotly numpy kaleido - Crie app.py com o código acima
- Baixe o CSV clicando no botão verde
- Execute:
streamlit run app.py
🌾 Dashboard de Produtividade Agrícola
Neste exemplo, saímos dos dados de TI e entramos no domínio da Agricultura de Precisão. O objetivo é analisar a produtividade de soja (sacas/ha) correlacionada com a pluviosidade (chuva) em diferentes estados.
- Integração de dados de produtividade por região.
- Gráficos de dispersão para identificar correlação entre clima e colheita.
- Uso de
tabs(abas) para organizar diferentes visões do negócio.
import streamlit as st
import pandas as pd
import plotly.express as px
# Configuração da página
st.set_page_config(page_title="Agro 5.0 - Dashboard", layout="wide", page_icon="🌾")
st.title("🌾 Dashboard de Produtividade: Safra de Soja")
st.markdown("---")
# 1. Gerando dados de exemplo (Simulando dados de cooperativas)
@st.cache_data
def load_agro_data():
data = {
'Estado': ['MT', 'PR', 'RS', 'GO', 'MS', 'MG', 'BA', 'SP'],
'Produtividade': [62.5, 58.2, 55.1, 60.8, 59.4, 57.8, 54.2, 56.5],
'Chuva_Acumulada_mm': [1800, 1650, 1700, 1550, 1450, 1400, 1100, 1350],
'Area_Plantada_M_ha': [10.2, 5.6, 6.1, 4.2, 3.8, 2.1, 1.8, 1.2]
}
return pd.DataFrame(data)
df_agro = load_agro_data()
# 2. Sidebar com Filtros
st.sidebar.header("🎛️ Filtros de Safra")
estado_selecionado = st.sidebar.multiselect(
"Selecione os Estados:",
options=df_agro['Estado'].unique(),
default=df_agro['Estado'].unique()
)
df_filtrado = df_agro[df_agro['Estado'].isin(estado_selecionado)]
# 3. Métricas Principais
m1, m2, m3 = st.columns(3)
m1.metric("Média de Produtividade", f"{df_filtrado['Produtividade'].mean():.1f} sacas/ha")
m2.metric("Maior Produtor (Filtro)", df_filtrado.loc[df_filtrado['Produtividade'].idxmax(), 'Estado'])
m3.metric("Total Área (M ha)", f"{df_filtrado['Area_Plantada_M_ha'].sum():.1f}")
# 4. Organização por Abas
tab_barras, tab_correlacao = st.tabs(["📊 Produtividade por Estado", "🌦️ Clima vs Colheita"])
with tab_barras:
fig_prod = px.bar(
df_filtrado.sort_values('Produtividade', ascending=False),
x='Estado', y='Produtividade',
color='Produtividade',
text_auto=True,
title="Produtividade Média por Estado (Sacas por Hectare)",
color_continuous_scale='Greens'
)
st.plotly_chart(fig_prod, use_container_width=True)
with tab_correlacao:
fig_scatter = px.scatter(
df_filtrado,
x='Chuva_Acumulada_mm',
y='Produtividade',
size='Area_Plantada_M_ha',
color='Estado',
hover_name='Estado',
title="Correlação: Chuva Acumulada vs Produtividade",
labels={'Chuva_Acumulada_mm': 'Chuva (mm)', 'Produtividade': 'Sacas/ha'}
)
st.plotly_chart(fig_scatter, use_container_width=True)
st.sidebar.markdown("---")
st.sidebar.info("Dashboard Agro 5.0 - Prof. Jéfer Benedett Dörr")
Dicas finais para o seu dashboard
- Use
@st.cache_dataem funções pesadas (leitura CSV, cálculos longos). - Evite loops for no pandas → use vetorização (.apply, groupby).
- Adicione
st.spinnerdurante carregamentos longos. - Para deploy:
streamlit run app.py --server.port 8501ou use Streamlit Community Cloud (gratuito). - No agro: integre com APIs de clima (INMET), imagens de drone ou sensores IoT.
Você agora sabe transformar uma análise em um app web interativo que qualquer produtor ou técnico pode usar. Próximo passo: crie seu próprio dashboard com dados reais da sua região ou experimento agrícola!
Deploy e Compartilhamento do seu Dashboard
Transforme seu app local em link público (grátis!):
- Crie conta no Streamlit Community Cloud
- Suba seu projeto para GitHub
- Conecte o repositório no Streamlit Cloud → deploy automático
Alternativa: Hugging Face Spaces (bom para dashboards com ML).