|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# -*- coding: utf-8 -*- |
| 3 | +""" |
| 4 | +Plan V3 - Smart Home Installation Analytics Dashboard |
| 5 | +Versão corrigida e funcional |
| 6 | +""" |
| 7 | + |
| 8 | +from dash import Dash, dcc, html, Input, Output |
| 9 | +import plotly.express as px |
| 10 | +import pandas as pd |
| 11 | +import plotly.graph_objects as go |
| 12 | +from sklearn.linear_model import LinearRegression |
| 13 | +import numpy as np |
| 14 | + |
| 15 | +# ===== DADOS SIMULADOS ===== |
| 16 | +# Baseados no Plan_Data_V3.py mas integrados no próprio arquivo |
| 17 | + |
| 18 | +data = { |
| 19 | + 'Region': ['North', 'North', 'North', 'South', 'South', 'South', 'East', 'East', 'West', 'West'], |
| 20 | + 'City': ['Seattle', 'Seattle', 'Portland', 'Austin', 'Dallas', 'Houston', 'New York', 'Boston', 'San Francisco', 'Los Angeles'], |
| 21 | + 'Installation_Type': ['Solar Panels', 'Smart Thermostat', 'Solar Panels', 'Smart Thermostat', 'Security System', 'Solar Panels', 'Smart Thermostat', 'Security System', 'Solar Panels', 'Smart Thermostat'], |
| 22 | + 'Installation_Cost': [15000, 8000, 18000, 6000, 12000, 20000, 7500, 14000, 22000, 9000], |
| 23 | + 'Annual_Energy_Savings': [1800, 900, 2100, 750, 800, 2400, 850, 900, 2600, 950], |
| 24 | + 'Number_of_Devices': [8, 5, 10, 4, 12, 9, 6, 11, 12, 7], |
| 25 | + 'Customer_Satisfaction': [4.2, 3.8, 4.5, 4.0, 4.3, 4.1, 3.9, 4.4, 4.6, 4.2], |
| 26 | + 'Latitude': [47.6062, 47.6062, 45.5051, 30.2672, 32.7767, 29.7604, 40.7128, 42.3601, 37.7749, 34.0522], |
| 27 | + 'Longitude': [-122.3321, -122.3321, -122.6750, -97.7431, -96.7970, -95.3698, -74.0060, -71.0589, -122.4194, -118.2437] |
| 28 | +} |
| 29 | + |
| 30 | +df_complex = pd.DataFrame(data) |
| 31 | + |
| 32 | +# ===== ANÁLISE PREDITIVA ===== |
| 33 | +# Preparar dados para predição de economia de energia |
| 34 | +X = df_complex[['Installation_Cost', 'Number_of_Devices', 'Customer_Satisfaction']] |
| 35 | +y = df_complex['Annual_Energy_Savings'] |
| 36 | + |
| 37 | +# Treinar modelo de regressão linear |
| 38 | +model = LinearRegression() |
| 39 | +model.fit(X, y) |
| 40 | + |
| 41 | +# Importância das features (coeficientes absolutos) |
| 42 | +feature_importance = pd.DataFrame({ |
| 43 | + 'feature': ['Installation Cost', 'Number of Devices', 'Customer Satisfaction'], |
| 44 | + 'importance': np.abs(model.coef_) |
| 45 | +}).sort_values('importance', ascending=False) |
| 46 | + |
| 47 | +# ===== APLICAÇÃO DASH ===== |
| 48 | +app = Dash(__name__) |
| 49 | +app.title = "🏡 Smart Home Analytics Dashboard" |
| 50 | + |
| 51 | +# Layout do Dashboard |
| 52 | +app.layout = html.Div([ |
| 53 | + # Cabeçalho |
| 54 | + html.Div([ |
| 55 | + html.H1("🏡 Smart Home Installation Analytics Dashboard", |
| 56 | + style={'textAlign': 'center', 'color': '#2c3e50', 'marginBottom': '30px'}), |
| 57 | + |
| 58 | + html.P("Análise abrangente de instalações de casa inteligente com visualizações complexas e análise preditiva", |
| 59 | + style={'textAlign': 'center', 'color': '#7f8c8d', 'fontSize': '18px'}) |
| 60 | + ], style={'marginBottom': '30px'}), |
| 61 | + |
| 62 | + # Filtros |
| 63 | + html.Div([ |
| 64 | + html.Div([ |
| 65 | + html.Label("Região:", style={'fontWeight': 'bold', 'marginBottom': '5px'}), |
| 66 | + dcc.Dropdown( |
| 67 | + id='region-dropdown', |
| 68 | + options=[{'label': i, 'value': i} for i in df_complex['Region'].unique()] + [{'label': 'Todas', 'value': 'All'}], |
| 69 | + value='All', |
| 70 | + clearable=False, |
| 71 | + style={'marginBottom': '15px'} |
| 72 | + ), |
| 73 | + ], style={'width': '30%', 'display': 'inline-block', 'marginRight': '3%'}), |
| 74 | + |
| 75 | + html.Div([ |
| 76 | + html.Label("Cidade:", style={'fontWeight': 'bold', 'marginBottom': '5px'}), |
| 77 | + dcc.Dropdown( |
| 78 | + id='city-dropdown', |
| 79 | + options=[{'label': i, 'value': i} for i in df_complex['City'].unique()] + [{'label': 'Todas', 'value': 'All'}], |
| 80 | + value='All', |
| 81 | + clearable=False, |
| 82 | + style={'marginBottom': '15px'} |
| 83 | + ), |
| 84 | + ], style={'width': '30%', 'display': 'inline-block', 'marginRight': '3%'}), |
| 85 | + |
| 86 | + html.Div([ |
| 87 | + html.Label("Tipo de Instalação:", style={'fontWeight': 'bold', 'marginBottom': '5px'}), |
| 88 | + dcc.Dropdown( |
| 89 | + id='type-dropdown', |
| 90 | + options=[{'label': i, 'value': i} for i in df_complex['Installation_Type'].unique()] + [{'label': 'Todos', 'value': 'All'}], |
| 91 | + value='All', |
| 92 | + clearable=False, |
| 93 | + style={'marginBottom': '15px'} |
| 94 | + ), |
| 95 | + ], style={'width': '30%', 'display': 'inline-block'}), |
| 96 | + ], style={ |
| 97 | + 'padding': '20px', |
| 98 | + 'backgroundColor': '#f8f9fa', |
| 99 | + 'borderRadius': '10px', |
| 100 | + 'marginBottom': '30px' |
| 101 | + }), |
| 102 | + |
| 103 | + # Linha superior de gráficos |
| 104 | + html.Div([ |
| 105 | + # Sunburst hierárquico |
| 106 | + html.Div([ |
| 107 | + html.H3("📊 Distribuição Hierárquica", style={'textAlign': 'center', 'color': '#2c3e50'}), |
| 108 | + dcc.Graph(id='hierarchical-chart') |
| 109 | + ], style={ |
| 110 | + 'width': '48%', |
| 111 | + 'display': 'inline-block', |
| 112 | + 'marginRight': '4%', |
| 113 | + 'backgroundColor': 'white', |
| 114 | + 'padding': '15px', |
| 115 | + 'borderRadius': '10px', |
| 116 | + 'boxShadow': '0 2px 4px rgba(0,0,0,0.1)' |
| 117 | + }), |
| 118 | + |
| 119 | + # Mapa geográfico |
| 120 | + html.Div([ |
| 121 | + html.H3("🗺️ Distribuição Geográfica", style={'textAlign': 'center', 'color': '#2c3e50'}), |
| 122 | + dcc.Graph(id='map-visualization') |
| 123 | + ], style={ |
| 124 | + 'width': '48%', |
| 125 | + 'display': 'inline-block', |
| 126 | + 'backgroundColor': 'white', |
| 127 | + 'padding': '15px', |
| 128 | + 'borderRadius': '10px', |
| 129 | + 'boxShadow': '0 2px 4px rgba(0,0,0,0.1)' |
| 130 | + }), |
| 131 | + ], style={'marginBottom': '30px'}), |
| 132 | + |
| 133 | + # Linha inferior de gráficos |
| 134 | + html.Div([ |
| 135 | + # Scatter plot |
| 136 | + html.Div([ |
| 137 | + html.H3("💰 Custo vs. Economia", style={'textAlign': 'center', 'color': '#2c3e50'}), |
| 138 | + dcc.Graph(id='scatter-plot') |
| 139 | + ], style={ |
| 140 | + 'width': '48%', |
| 141 | + 'display': 'inline-block', |
| 142 | + 'marginRight': '4%', |
| 143 | + 'backgroundColor': 'white', |
| 144 | + 'padding': '15px', |
| 145 | + 'borderRadius': '10px', |
| 146 | + 'boxShadow': '0 2px 4px rgba(0,0,0,0.1)' |
| 147 | + }), |
| 148 | + |
| 149 | + # Análise preditiva |
| 150 | + html.Div([ |
| 151 | + html.H3("🔮 Importância dos Fatores", style={'textAlign': 'center', 'color': '#2c3e50'}), |
| 152 | + dcc.Graph(id='predictive-viz') |
| 153 | + ], style={ |
| 154 | + 'width': '48%', |
| 155 | + 'display': 'inline-block', |
| 156 | + 'backgroundColor': 'white', |
| 157 | + 'padding': '15px', |
| 158 | + 'borderRadius': '10px', |
| 159 | + 'boxShadow': '0 2px 4px rgba(0,0,0,0.1)' |
| 160 | + }), |
| 161 | + ]) |
| 162 | +], style={ |
| 163 | + 'padding': '20px', |
| 164 | + 'fontFamily': 'Arial, sans-serif', |
| 165 | + 'backgroundColor': '#ecf0f1' |
| 166 | +}) |
| 167 | + |
| 168 | +# ===== CALLBACKS ===== |
| 169 | + |
| 170 | +# Callback para atualizar opções de cidade baseado na região |
| 171 | +@app.callback( |
| 172 | + Output('city-dropdown', 'options'), |
| 173 | + Output('city-dropdown', 'value'), |
| 174 | + Input('region-dropdown', 'value') |
| 175 | +) |
| 176 | +def update_cities(selected_region): |
| 177 | + if selected_region == 'All': |
| 178 | + cities = df_complex['City'].unique() |
| 179 | + else: |
| 180 | + cities = df_complex[df_complex['Region'] == selected_region]['City'].unique() |
| 181 | + |
| 182 | + city_options = [{'label': i, 'value': i} for i in cities] + [{'label': 'Todas', 'value': 'All'}] |
| 183 | + return city_options, 'All' |
| 184 | + |
| 185 | +# Callback principal para atualizar todos os gráficos |
| 186 | +@app.callback( |
| 187 | + Output('hierarchical-chart', 'figure'), |
| 188 | + Output('map-visualization', 'figure'), |
| 189 | + Output('scatter-plot', 'figure'), |
| 190 | + Output('predictive-viz', 'figure'), |
| 191 | + Input('region-dropdown', 'value'), |
| 192 | + Input('city-dropdown', 'value'), |
| 193 | + Input('type-dropdown', 'value') |
| 194 | +) |
| 195 | +def update_dashboard(selected_region, selected_city, selected_type): |
| 196 | + # Filtrar dados |
| 197 | + filtered_df = df_complex.copy() |
| 198 | + |
| 199 | + if selected_region != 'All': |
| 200 | + filtered_df = filtered_df[filtered_df['Region'] == selected_region] |
| 201 | + if selected_city != 'All': |
| 202 | + filtered_df = filtered_df[filtered_df['City'] == selected_city] |
| 203 | + if selected_type != 'All': |
| 204 | + filtered_df = filtered_df[filtered_df['Installation_Type'] == selected_type] |
| 205 | + |
| 206 | + # ===== GRÁFICO SUNBURST HIERÁRQUICO ===== |
| 207 | + if not filtered_df.empty: |
| 208 | + hierarchical_fig = px.sunburst( |
| 209 | + filtered_df, |
| 210 | + path=['Region', 'City', 'Installation_Type'], |
| 211 | + values='Installation_Cost', |
| 212 | + color='Installation_Cost', |
| 213 | + color_continuous_scale='Viridis', |
| 214 | + title='Distribuição de Custos por Região > Cidade > Tipo' |
| 215 | + ) |
| 216 | + hierarchical_fig.update_traces( |
| 217 | + hovertemplate='<b>%{label}</b><br>Custo: $%{value:,.0f}<br>Percentual: %{percentParent}<extra></extra>' |
| 218 | + ) |
| 219 | + else: |
| 220 | + hierarchical_fig = go.Figure() |
| 221 | + hierarchical_fig.add_annotation(text="Nenhum dado disponível", showarrow=False) |
| 222 | + |
| 223 | + hierarchical_fig.update_layout(height=400, margin=dict(t=40, b=0, l=0, r=0)) |
| 224 | + |
| 225 | + # ===== MAPA GEOGRÁFICO ===== |
| 226 | + if not filtered_df.empty: |
| 227 | + map_fig = px.scatter_geo( |
| 228 | + filtered_df, |
| 229 | + lat='Latitude', |
| 230 | + lon='Longitude', |
| 231 | + hover_name='City', |
| 232 | + size='Installation_Cost', |
| 233 | + color='Region', |
| 234 | + projection="albers usa", |
| 235 | + title='Instalações nos Estados Unidos' |
| 236 | + ) |
| 237 | + map_fig.update_layout(geo=dict(scope='usa')) |
| 238 | + map_fig.update_traces( |
| 239 | + hovertemplate='<b>%{hovertext}</b><br>Custo: $%{marker.size:,.0f}<br>Região: %{marker.color}<extra></extra>' |
| 240 | + ) |
| 241 | + else: |
| 242 | + map_fig = go.Figure() |
| 243 | + map_fig.add_annotation(text="Nenhum dado disponível", showarrow=False) |
| 244 | + |
| 245 | + map_fig.update_layout(height=400, margin=dict(t=40, b=0, l=0, r=0)) |
| 246 | + |
| 247 | + # ===== SCATTER PLOT ===== |
| 248 | + if not filtered_df.empty: |
| 249 | + scatter_fig = px.scatter( |
| 250 | + filtered_df, |
| 251 | + x='Installation_Cost', |
| 252 | + y='Annual_Energy_Savings', |
| 253 | + color='Installation_Type', |
| 254 | + size='Customer_Satisfaction', |
| 255 | + hover_name='City', |
| 256 | + title='Relação Custo de Instalação vs. Economia Anual' |
| 257 | + ) |
| 258 | + scatter_fig.update_traces( |
| 259 | + hovertemplate='<b>%{hovertext}</b><br>Custo: $%{x:,.0f}<br>Economia: $%{y:,.0f}<br>Satisfação: %{marker.size}<extra></extra>' |
| 260 | + ) |
| 261 | + else: |
| 262 | + scatter_fig = go.Figure() |
| 263 | + scatter_fig.add_annotation(text="Nenhum dado disponível", showarrow=False) |
| 264 | + |
| 265 | + scatter_fig.update_layout(height=400, margin=dict(t=40, b=20, l=50, r=20)) |
| 266 | + |
| 267 | + # ===== ANÁLISE PREDITIVA ===== |
| 268 | + predictive_fig = px.bar( |
| 269 | + feature_importance, |
| 270 | + x='importance', |
| 271 | + y='feature', |
| 272 | + orientation='h', |
| 273 | + color='importance', |
| 274 | + color_continuous_scale='Blues', |
| 275 | + title='Fatores que Mais Influenciam a Economia de Energia' |
| 276 | + ) |
| 277 | + predictive_fig.update_layout( |
| 278 | + height=400, |
| 279 | + margin=dict(t=40, b=20, l=120, r=20), |
| 280 | + yaxis={'categoryorder': 'total ascending'} |
| 281 | + ) |
| 282 | + predictive_fig.update_traces( |
| 283 | + hovertemplate='<b>%{y}</b><br>Importância: %{x:.2f}<extra></extra>' |
| 284 | + ) |
| 285 | + |
| 286 | + return hierarchical_fig, map_fig, scatter_fig, predictive_fig |
| 287 | + |
| 288 | +# ===== EXECUÇÃO ===== |
| 289 | +if __name__ == '__main__': |
| 290 | + app.run(debug=True, host='0.0.0.0', port=8053) |
0 commit comments