Spaces:
Configuration error
Configuration error
junlongcheng
commited on
Commit
•
94a2717
1
Parent(s):
ee6f22b
Upload 3 files
Browse files- .gitattributes +1 -0
- app.py +116 -0
- permanently_cleaned_geolocation_data.csv +3 -0
- requirements.txt +5 -0
.gitattributes
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
permanently_cleaned_geolocation_data.csv filter=lfs diff=lfs merge=lfs -text
|
app.py
ADDED
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import dash
|
2 |
+
from dash import dcc, html, Input, Output, exceptions
|
3 |
+
import pandas as pd
|
4 |
+
from sklearn.cluster import KMeans
|
5 |
+
import plotly.express as px
|
6 |
+
import dash_bootstrap_components as dbc
|
7 |
+
|
8 |
+
# 1. Data Preparation (Assuming you've read your data into a DataFrame named 'df')
|
9 |
+
df = pd.read_csv('permanently_cleaned_geolocation_data.csv')
|
10 |
+
|
11 |
+
df = df.rename(columns = {'id_left': 'id',
|
12 |
+
'geolocation_state': 'state',
|
13 |
+
'geolocation_lat': 'latitude',
|
14 |
+
'geolocation_lng': 'longitude'})
|
15 |
+
|
16 |
+
df['object'] = df['object'].astype(str).str.capitalize()
|
17 |
+
|
18 |
+
# 2. Dash App Setup
|
19 |
+
app = dash.Dash(__name__, external_stylesheets=['https://codepen.io/chriddyp/pen/bWLwgP.css'])
|
20 |
+
|
21 |
+
# State Filter Options with "Select All"
|
22 |
+
state_options = [{'label': 'Select All', 'value': 'all'}] + [
|
23 |
+
{'label': i, 'value': i} for i in df['state'].unique()
|
24 |
+
]
|
25 |
+
|
26 |
+
app.layout = html.Div([
|
27 |
+
html.H1("Olist Warehouse Location Optimization Tool", style={'textAlign': 'center'}),
|
28 |
+
|
29 |
+
# Outer container for slicers
|
30 |
+
html.Div(id='outer_div', children=[
|
31 |
+
html.Div([ # Slicer 1 (Object)
|
32 |
+
html.Label("Select Object:"),
|
33 |
+
dcc.Dropdown(id='object-filter', options=[{'label': i, 'value': i} for i in df['object'].unique()], multi=True)
|
34 |
+
], style={'width': '250px', 'margin': '10px'}), # Adjust width as needed
|
35 |
+
|
36 |
+
html.Div([ # Slicer 2 (State)
|
37 |
+
html.Label("Select State:"),
|
38 |
+
dcc.Dropdown(id='state-filter', options=state_options, value='all', multi=True)
|
39 |
+
], style={'width': '250px', 'margin': '10px'}),
|
40 |
+
|
41 |
+
html.Div([ # Slicer 3 (Number of Clusters)
|
42 |
+
html.Label("Number of Warehouses:"),
|
43 |
+
dcc.Input(id='cluster-input', type='number', min=1, value=3)
|
44 |
+
], style={'width': '250px', 'margin': '10px'}),
|
45 |
+
], style={
|
46 |
+
'display': 'flex',
|
47 |
+
'flex-direction': 'row',
|
48 |
+
'justify-content': 'center',
|
49 |
+
'align-items': 'center',
|
50 |
+
'flex-wrap': 'wrap'
|
51 |
+
}),
|
52 |
+
|
53 |
+
html.Div( # Map container
|
54 |
+
dcc.Graph(id='cluster-plot', style={'height': '600px', 'width': '800px'}),
|
55 |
+
style={
|
56 |
+
'display': 'flex',
|
57 |
+
'justify-content': 'center',
|
58 |
+
'align-items': 'center'
|
59 |
+
}
|
60 |
+
)
|
61 |
+
])
|
62 |
+
|
63 |
+
# 5. Callback for Interactivity
|
64 |
+
@app.callback(
|
65 |
+
Output('cluster-plot', 'figure'),
|
66 |
+
[Input('object-filter', 'value'),
|
67 |
+
Input('state-filter', 'value'),
|
68 |
+
Input('cluster-input', 'value')]
|
69 |
+
)
|
70 |
+
def update_plot(selected_objects, selected_states, num_clusters):
|
71 |
+
# Handle "Select All" for States
|
72 |
+
if 'all' in selected_states:
|
73 |
+
selected_states = df['state'].unique()
|
74 |
+
|
75 |
+
if selected_objects is None or num_clusters is None:
|
76 |
+
return px.scatter() # Empty plot if filters are not selected
|
77 |
+
|
78 |
+
filtered_df = df[df['object'].isin(selected_objects) & df['state'].isin(selected_states)]
|
79 |
+
|
80 |
+
if filtered_df.empty:
|
81 |
+
return px.scatter()
|
82 |
+
|
83 |
+
# Clustering
|
84 |
+
kmeans = KMeans(n_clusters=num_clusters).fit(filtered_df[['longitude', 'latitude']])
|
85 |
+
filtered_df['cluster'] = kmeans.labels_
|
86 |
+
|
87 |
+
# Calculate Cluster Centers
|
88 |
+
cluster_centers = filtered_df.groupby('cluster')[['longitude', 'latitude']].mean().reset_index()
|
89 |
+
|
90 |
+
# Create Plot (NO hovertemplate here)
|
91 |
+
fig = px.scatter_mapbox(filtered_df, lat="latitude", lon="longitude", color="cluster",
|
92 |
+
hover_name=None,
|
93 |
+
custom_data=['cluster'],
|
94 |
+
mapbox_style="carto-positron")
|
95 |
+
|
96 |
+
# Add Cluster Centers to Plot (WITH hover data)
|
97 |
+
fig.add_scattermapbox(
|
98 |
+
lat=cluster_centers["latitude"],
|
99 |
+
lon=cluster_centers["longitude"],
|
100 |
+
mode="markers",
|
101 |
+
marker=dict(size=20, color="red"),
|
102 |
+
name="Warehouses",
|
103 |
+
hoverinfo="text", # Enable hover for cluster centers
|
104 |
+
text=[
|
105 |
+
f"Warehouse {i + 1}<br>Lat: {lat:.4f}<br>Lon: {lon:.4f}"
|
106 |
+
for i, lat, lon in zip(
|
107 |
+
cluster_centers.index, cluster_centers["latitude"], cluster_centers["longitude"]
|
108 |
+
)
|
109 |
+
],
|
110 |
+
showlegend=False,
|
111 |
+
)
|
112 |
+
|
113 |
+
return fig
|
114 |
+
|
115 |
+
if __name__ == '__main__':
|
116 |
+
app.run_server(debug=True, port=8068)
|
permanently_cleaned_geolocation_data.csv
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:601485a3bab25314c4eb6d77f8203ac110f3d11c6803c7939929b221a8c3df43
|
3 |
+
size 16261986
|
requirements.txt
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
dash==2.14.2
|
2 |
+
dash_bootstrap_components==1.6.0
|
3 |
+
pandas==2.2.2
|
4 |
+
plotly==5.9.0
|
5 |
+
scikit_learn==1.2.2
|