# =============================================================================
# DASHBOARD INTERATIVO - EVOLUÇÃO DE INDICADORES-CHAVE
# =============================================================================
# Este script cria um dashboard moderno e interativo usando Plotly Dash.
# Ele exibe a evolução de indicadores-chave extraídos de um PDF, com gráficos
# dinâmicos, filtros por ano e legendas interativas.
# Compatível com Replit.
# =============================================================================
import dash
from dash import dcc, html, Input, Output
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
# =============================================================================
# DADOS DE EXEMPLO (SUBSTITUIR PELOS DADOS REAIS DO PDF)
# =============================================================================
# Simulando dados de indicadores-chave (exemplo: receita, lucro, clientes)
# Em um cenário real, extraia esses dados do PDF usando bibliotecas como
# PyPDF2, pdfplumber ou tabula-py.
np.random.seed(42)
anos = list(range(2018, 2024))
meses = ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez']
# Criando dados mensais para cada ano
dados_mensais = []
for ano in anos:
for mes in meses:
receita = np.random.uniform(80000, 150000) * (1 + (ano - 2018) * 0.15)
lucro = receita * np.random.uniform(0.15, 0.35)
clientes = int(np.random.uniform(500, 1200) * (1 + (ano - 2018) * 0.1))
dados_mensais.append({
'Ano': ano,
'Mes': mes,
'Receita': receita,
'Lucro': lucro,
'Clientes': clientes,
'Margem_Lucro': (lucro / receita) * 100
})
df = pd.DataFrame(dados_mensais)
# Dados anuais agregados
df_anual = df.groupby('Ano').agg({
'Receita': 'sum',
'Lucro': 'sum',
'Clientes': 'sum',
'Margem_Lucro': 'mean'
}).reset_index()
# =============================================================================
# INICIALIZAÇÃO DO DASHBOARD
# =============================================================================
app = dash.Dash(__name__)
app.title = "Dashboard de Indicadores-Chave"
# Paleta de cores moderna e limpa
CORES = {
'fundo': '#f8f9fa',
'texto': '#2c3e50',
'primaria': '#3498db',
'secundaria': '#2ecc71',
'terciaria': '#e74c3c',
'quartenaria': '#f39c12'
}
# =============================================================================
# LAYOUT DO DASHBOARD
# =============================================================================
app.layout = html.Div(
style={
'backgroundColor': CORES['fundo'],
'fontFamily': 'Segoe UI, Roboto, Helvetica, Arial, sans-serif',
'padding': '20px',
'minHeight': '100vh'
},
children=[
# Cabeçalho
html.Div(
style={'textAlign': 'center', 'marginBottom': '30px'},
children=[
html.H1(
"Dashboard de Indicadores-Chave",
style={'color': CORES['texto'], 'fontWeight': '300', 'fontSize': '36px'}
),
html.P(
"Evolução dos principais indicadores - 2018 a 2023",
style={'color': '#7f8c8d', 'fontSize': '16px'}
)
]
),
# Filtros globais
html.Div(
style={'marginBottom': '20px', 'padding': '15px', 'backgroundColor': 'white', 'borderRadius': '8px',
'boxShadow': '0 2px 4px rgba(0,0,0,0.1)'},
children=[
html.Label("Filtrar por Ano:", style={'fontWeight': 'bold', 'marginRight': '15px'}),
dcc.Dropdown(
id='filtro-ano',
options=[{'label': 'Todos', 'value': 'todos'}] +
[{'label': str(ano), 'value': ano} for ano in anos],
value='todos',
multi=False,
style={'width': '200px', 'display': 'inline-block', 'verticalAlign': 'middle'}
),
html.Label("Métrica:", style={'fontWeight': 'bold', 'marginLeft': '30px', 'marginRight': '15px'}),
dcc.Dropdown(
id='filtro-metrica',
options=[
{'label': 'Receita', 'value': 'Receita'},
{'label': 'Lucro', 'value': 'Lucro'},
{'label': 'Clientes', 'value': 'Clientes'},
{'label': 'Margem de Lucro (%)', 'value': 'Margem_Lucro'}
],
value='Receita',
multi=False,
style={'width': '250px', 'display': 'inline-block', 'verticalAlign': 'middle'}
)
]
),
# Gráfico 1: Evolução Anual
html.Div(
style={'marginBottom': '30px', 'backgroundColor': 'white', 'padding': '20px', 'borderRadius': '8px',
'boxShadow': '0 2px 4px rgba(0,0,0,0.1)'},
children=[
html.H3("Evolução Anual dos Indicadores", style={'color': CORES['texto'], 'fontWeight': '400'}),
dcc.Graph(id='grafico-anual')
]
),
# Gráfico 2: Comparação Mensal
html.Div(
style={'marginBottom': '30px', 'backgroundColor': 'white', 'padding': '20px', 'borderRadius': '8px',
'boxShadow': '0 2px 4px rgba(0,0,0,0.1)'},
children=[
html.H3("Comparação Mensal por Ano", style={'color': CORES['texto'], 'fontWeight': '400'}),
dcc.Graph(id='grafico-mensal')
]
),
# Gráfico 3: Distribuição e Comparação
html.Div(
style={'marginBottom': '30px', 'backgroundColor': 'white', 'padding': '20px', 'borderRadius': '8px',
'boxShadow': '0 2px 4px rgba(0,0,0,0.1)'},
children=[
html.H3("Distribuição e Comparação entre Períodos", style={'color': CORES['texto'], 'fontWeight': '400'}),
dcc.Graph(id='grafico-comparacao')
]
),
# Gráfico 4: Tendência e Projeção
html.Div(
style={'marginBottom': '30px', 'backgroundColor': 'white', 'padding': '20px', 'borderRadius': '8px',
'boxShadow': '0 2px 4px rgba(0,0,0,0.1)'},
children=[
html.H3("Tendência e Projeção", style={'color': CORES['texto'], 'fontWeight': '400'}),
dcc.Graph(id='grafico-tendencia')
]
),
# Rodapé
html.Footer(
style={'textAlign': 'center', 'color': '#7f8c8d', 'marginTop': '30px', 'fontSize': '14px'},
children=[
html.P("Dashboard gerado automaticamente - Dados de exemplo para demonstração")
]
)
]
)
# =============================================================================
# CALLBACKS PARA INTERATIVIDADE
# =============================================================================
@app.callback(
Output('grafico-anual', 'figure'),
[Input('filtro-ano', 'value'),
Input('filtro-metrica', 'value')]
)
def atualizar_grafico_anual(ano_selecionado, metrica):
"""Atualiza o gráfico de evolução anual com base nos filtros."""
df_filtrado = df_anual.copy()
if ano_selecionado != 'todos':
df_filtrado = df_filtrado[df_filtrado['Ano'] <= ano_selecionado]
fig = go.Figure()
# Adicionar barras para o período atual
fig.add_trace(go.Bar(
x=df_filtrado['Ano'],
y=df_filtrado[metrica],
name='Período Atual',
marker_color=CORES['primaria'],
text=df_filtrado[metrica].round(2) if metrica != 'Clientes' else df_filtrado[metrica],
textposition='outside',
hovertemplate='<b>%{x}</b><br>' + metrica + ': %{y:,.2f}<extra></extra>'
))
# Adicionar linha de tendência
fig.add_trace(go.Scatter(
x=df_filtrado['Ano'],
y=df_filtrado[metrica],
mode='lines+markers',
name='Tendência',
line=dict(color=CORES['secundaria'], width=3),
marker=dict(size=8),
hovertemplate='<b>%{x}</b><br>Tendência: %{y:,.2f}<extra></extra>'
))
fig.update_layout(
template='plotly_white',
hovermode='x unified',
legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1),
margin=dict(l=40, r=40, t=40, b=40),
xaxis_title='Ano',
yaxis_title=metrica,
font=dict(family='Segoe UI, Roboto, Helvetica, Arial, sans-serif', color=CORES['texto'])
)
return fig
@app.callback(
Output('grafico-mensal', 'figure'),
[Input('filtro-ano', 'value'),
Input('filtro-metrica', 'value')]
)
def atualizar_grafico_mensal(ano_selecionado, metrica):
"""Atualiza o gráfico mensal comparativo entre anos."""
df_filtrado = df.copy()
if ano_selecionado != 'todos':
df_filtrado = df_filtrado[df_filtrado['Ano'] == ano_selecionado]
fig = px.line(
df_filtrado,
x='Mes',
y=metrica,
color='Ano',
markers=True,
title=f'Evolução Mensal - {metrica}',
labels={'Mes': 'Mês', metrica: metrica, 'Ano': 'Ano'},
color_discrete_sequence=px.colors.qualitative.Set2
)
fig.update_traces(
line=dict(width=2.5),
marker=dict(size=6),
hovertemplate='<b>%{x}</b><br>' + metrica + ': %{y:,.2f}<br>Ano: %{customdata}<extra></extra>'
)
fig.update_layout(
template='plotly_white',
hovermode='x unified',
legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1),
margin=dict(l=40, r=40, t=60, b=40),
xaxis_title='Mês',
yaxis_title=metrica,
font=dict(family='Segoe UI, Roboto, Helvetica, Arial, sans-serif', color=CORES['texto'])
)
return fig
@app.callback(
Output('grafico-comparacao', 'figure'),
[Input('filtro-ano', 'value'),
Input('filtro-metrica', 'value')]
)
def atualizar_grafico_comparacao(ano_selecionado, metrica):
"""Cria gráfico de comparação entre períodos (atual vs anterior)."""
df_filtrado = df_anual.copy()
if ano_selecionado != 'todos':
df_filtrado = df_filtrado[df_filtrado['Ano'] <= ano_selecionado]
# Calcular variação percentual
df_filtrado['Variacao'] = df_filtrado[metrica].pct_change() * 100
fig = go.Figure()
# Barras para valores
fig.add_trace(go.Bar(
x=df_filtrado['Ano'],
y=df_filtrado[metrica],
name='Valor',
marker_color=CORES['primaria'],
text=df_filtrado[metrica].round(2) if metrica != 'Clientes' else df_filtrado[metrica],
textposition='outside',
hovertemplate='<b>%{x}</b><br>Valor: %{y:,.2f}<extra></extra>'
))
# Linha para variação percentual
fig.add_trace(go.Scatter(
x=df_filtrado['Ano'],
y=df_filtrado['Variacao'],
name='Variação (%)',
yaxis='y2',
mode='lines+markers',
line=dict(color=CORES['terciaria'], width=2),
marker=dict(size=8),
hovertemplate='<b>%{x}</b><br>Variação: %{y:.1f}%<extra></extra>'
))
fig.update_layout(
template='plotly_white',
hovermode='x unified',
legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1),
margin=dict(l=40, r=40, t=40, b=40),
xaxis_title='Ano',
yaxis_title=metrica,
yaxis2=dict(
title='Variação (%)',
overlaying='y',
side='right',
showgrid=False
),
font=dict(family='Segoe UI, Roboto, Helvetica, Arial, sans-serif', color=CORES['texto'])
)
return fig
@app.callback(
Output('grafico-tendencia', 'figure'),
[Input('filtro-ano', 'value'),
Input('filtro-metrica', 'value')]
)
def atualizar_grafico_tendencia(ano_selecionado, metrica):
"""Cria gráfico de tendência com projeção linear."""
df_filtrado = df_anual.copy()
if ano_selecionado != 'todos':
df_filtrado = df_filtrado[df_filtrado['Ano'] <= ano_selecionado]
# Calcular tendência linear
x = df_filtrado['Ano'].values
y = df_filtrado[metrica].values
z = np.polyfit(x, y, 1)
p = np.poly1d(z)
# Projeção para 2 anos futuros
anos_futuros = list(range(df_filtrado['Ano'].max() + 1, df_filtrado['Ano'].max() + 3))
projecao = p(anos_futuros)
fig = go.Figure()
# Dados históricos
fig.add_trace(go.Scatter(
x=df_filtrado['Ano'],
y=df_filtrado[metrica],
mode='lines+markers',
name='Histórico',
line=dict(color=CORES['primaria'], width=3),
marker=dict(size=10),
hovertemplate='<b>%{x}</b><br>Histórico: %{y:,.2f}<extra></extra>'
))
# Linha de tendência
fig.add_trace(go.Scatter(
x=df_filtrado['Ano'],
y=p(x),
mode='lines',
name='Tendência',
line=dict(color=CORES['secundaria'], width=2, dash='dash'),
hovertemplate='<b>%{x}</b><br>Tendência: %{y:,.2f}<extra></extra>'
))
# Projeção
fig.add_trace(go.Scatter(
x=anos_futuros,
y=projecao,
mode='lines+markers',
name='Projeção',
line=dict(color=CORES['terciaria'], width=2, dash='dot'),
marker=dict(size=8, symbol='diamond'),
hovertemplate='<b>%{x}</b><br>Projeção: %{y:,.2f}<extra></extra>'
))
fig.update_layout(
template='plotly_white',
hovermode='x unified',
legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1),
margin=dict(l=40, r=40, t=40, b=40),
xaxis_title='Ano',
yaxis_title=metrica,
font=dict(family='Segoe UI, Roboto, Helvetica, Arial, sans-serif', color=CORES['texto'])
)
return fig
# =============================================================================
# EXECUÇÃO DO SERVIDOR
# =============================================================================
if __name__ == '__main__':
app.run_server(debug=True, host='0.0.0.0', port=8050)
Discussão
0 comentários