import dash from dash import dcc, html, Input, Output, exceptions import pandas as pd from sklearn.cluster import KMeans import plotly.express as px import dash_bootstrap_components as dbc # 1. Data Preparation (Assuming you've read your data into a DataFrame named 'df') df = pd.read_csv('permanently_cleaned_geolocation_data.csv') df = df.rename(columns = {'id_left': 'id', 'geolocation_state': 'state', 'geolocation_lat': 'latitude', 'geolocation_lng': 'longitude'}) df['object'] = df['object'].astype(str).str.capitalize() # 2. Dash App Setup app = dash.Dash(__name__, external_stylesheets=['https://codepen.io/chriddyp/pen/bWLwgP.css']) # State Filter Options with "Select All" state_options = [{'label': 'Select All', 'value': 'all'}] + [ {'label': i, 'value': i} for i in df['state'].unique() ] app.layout = html.Div([ html.H1("Olist Warehouse Location Optimization Tool", style={'textAlign': 'center'}), # Outer container for slicers html.Div(id='outer_div', children=[ html.Div([ # Slicer 1 (Object) html.Label("Select Object:"), dcc.Dropdown(id='object-filter', options=[{'label': i, 'value': i} for i in df['object'].unique()], multi=True) ], style={'width': '250px', 'margin': '10px'}), # Adjust width as needed html.Div([ # Slicer 2 (State) html.Label("Select State:"), dcc.Dropdown(id='state-filter', options=state_options, value='all', multi=True) ], style={'width': '250px', 'margin': '10px'}), html.Div([ # Slicer 3 (Number of Clusters) html.Label("Number of Warehouses:"), dcc.Input(id='cluster-input', type='number', min=1, value=3) ], style={'width': '250px', 'margin': '10px'}), ], style={ 'display': 'flex', 'flex-direction': 'row', 'justify-content': 'center', 'align-items': 'center', 'flex-wrap': 'wrap' }), html.Div( # Map container dcc.Graph(id='cluster-plot', style={'height': '600px', 'width': '800px'}), style={ 'display': 'flex', 'justify-content': 'center', 'align-items': 'center' } ) ]) # 5. Callback for Interactivity @app.callback( Output('cluster-plot', 'figure'), [Input('object-filter', 'value'), Input('state-filter', 'value'), Input('cluster-input', 'value')] ) def update_plot(selected_objects, selected_states, num_clusters): # Handle "Select All" for States if 'all' in selected_states: selected_states = df['state'].unique() if selected_objects is None or num_clusters is None: return px.scatter() # Empty plot if filters are not selected filtered_df = df[df['object'].isin(selected_objects) & df['state'].isin(selected_states)] if filtered_df.empty: return px.scatter() # Clustering kmeans = KMeans(n_clusters=num_clusters).fit(filtered_df[['longitude', 'latitude']]) filtered_df['cluster'] = kmeans.labels_ # Calculate Cluster Centers cluster_centers = filtered_df.groupby('cluster')[['longitude', 'latitude']].mean().reset_index() # Create Plot (NO hovertemplate here) fig = px.scatter_mapbox(filtered_df, lat="latitude", lon="longitude", color="cluster", hover_name=None, custom_data=['cluster'], mapbox_style="carto-positron") # Add Cluster Centers to Plot (WITH hover data) fig.add_scattermapbox( lat=cluster_centers["latitude"], lon=cluster_centers["longitude"], mode="markers", marker=dict(size=20, color="red"), name="Warehouses", hoverinfo="text", # Enable hover for cluster centers text=[ f"Warehouse {i + 1}
Lat: {lat:.4f}
Lon: {lon:.4f}" for i, lat, lon in zip( cluster_centers.index, cluster_centers["latitude"], cluster_centers["longitude"] ) ], showlegend=False, ) return fig if __name__ == '__main__': app.run_server(debug=True, port=8068)