Djohell commited on
Commit
9dcbefe
·
verified ·
1 Parent(s): 9d3a456

Upload app.py

Browse files
Files changed (1) hide show
  1. src/app.py +503 -37
src/app.py CHANGED
@@ -1,40 +1,506 @@
1
- import altair as alt
2
- import numpy as np
3
  import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # import altair as alt
2
+ # import numpy as np
3
  import pandas as pd
4
  import streamlit as st
5
+ import plotly.express as px
6
+ import boto3
7
+ import plotly.graph_objects as go
8
+ import itertools
9
+ from sklearn.cluster import KMeans, DBSCAN
10
+ # from plotly.subplots import make_subplots
11
 
12
+ @st.cache_data
13
+ def compute_kmeans(df, n_clusters):
14
+ coords = df[["Lat", "Lon"]]
15
+ kmeans = KMeans(n_clusters=n_clusters, random_state=42)
16
+ df = df.copy()
17
+ df["clusterKmeans"] = kmeans.fit_predict(coords)
18
+ return df
19
+
20
+
21
+ st.set_page_config(page_title="UBER – Analyse des clusters", layout="wide")
22
+
23
+ st.markdown("""
24
+ <style>
25
+ .stTabs [role="tablist"] {
26
+ justify-content: center;
27
+ gap: 25px;
28
+ }
29
+ .stTabs [role="tab"] {
30
+ font-size: 17px;
31
+ font-weight: 600;
32
+ padding: 10px 20px;
33
+ border-radius: 10px;
34
+ background-color: #f8f9fa;
35
+ color: #444;
36
+ transition: all 0.3s ease-in-out;
37
+ }
38
+ .stTabs [role="tab"]:hover {
39
+ background-color: #e0e0e0;
40
+ transform: scale(1.05);
41
+ }
42
+ .stTabs [role="tab"][aria-selected="true"] {
43
+ background-color: #4CAF50;
44
+ color: white;
45
+ font-weight: 700;
46
+ box-shadow: 0px 4px 8px rgba(0,0,0,0.2);
47
+ }
48
+ </style>
49
+ """, unsafe_allow_html=True)
50
+
51
+ st.title("🚕 Bonjour et bienvenue sur le dashboard du projet Clustering d'UBER ! 🚕")
52
+
53
+ st.subheader(
54
+ "Ce projet vise à vérifier la concordance entre l'implantation des taxis UBER et la demande des clients. " \
55
+ "Les données utilisées datent d'Avril 2014 et proviennent de la ville de New York.")
56
+
57
+
58
+ tab1, tab2, tab3 = st.tabs(["📊⏱ Implantation des taxis et besoins", "🫧🫧🫧 Analyse par jour de la semaine", " ✍️ Constat et conclusions"])
59
+
60
+
61
+ # --- Import des datasets
62
+ taxi = pd.read_csv("https://uber-cdsd-joel.s3.eu-west-3.amazonaws.com/uber-trip-data/taxi-zone-lookup.csv")
63
+ apr_14 = pd.read_csv("https://uber-cdsd-joel.s3.eu-west-3.amazonaws.com/uber-trip-data/apr_14_clean.csv")
64
+
65
+ # --- Préparer la densité des stations taxis
66
+ def compute_taxi_density(taxi_df):
67
+ density = taxi_df['Borough'].value_counts()
68
+ df = density.reset_index()
69
+ df.columns = ['Borough', 'Count']
70
+ df = df[df['Borough'] != "Unknown"]
71
+
72
+ # Centres approximatifs
73
+ borough_centers = {
74
+ 'Manhattan': [40.7831, -73.9712],
75
+ 'Brooklyn': [40.6782, -73.9442],
76
+ 'Queens': [40.7282, -73.7949],
77
+ 'Bronx': [40.8448, -73.8648],
78
+ 'Staten Island': [40.5795, -74.1502],
79
+ 'EWR': [40.6895, -74.1745]
80
+ }
81
+ df['Latitude'] = df['Borough'].map(lambda x: borough_centers.get(x, [40.7128, -74.0060])[0])
82
+ df['Longitude'] = df['Borough'].map(lambda x: borough_centers.get(x, [40.7128, -74.0060])[1])
83
+ return df
84
+
85
+ df_density = compute_taxi_density(taxi)
86
+
87
+ with tab1:
88
+
89
+ st.subheader("1. Regardons dans un premier temps comment Uber a implanté ses différentes bases de taxi.")
90
+
91
+ # créer le graphique Plotly
92
+ fig = px.scatter_mapbox(
93
+ df_density,
94
+ lat='Latitude',
95
+ lon='Longitude',
96
+ size='Count',
97
+ size_max=50,
98
+ color='Borough',
99
+ hover_name='Borough',
100
+ hover_data={'Count':True, 'Latitude':False, 'Longitude':False},
101
+ zoom=10,
102
+ height=800,
103
+ mapbox_style='carto-positron'
104
+ )
105
+
106
+ # afficher dans Streamlit
107
+ st.plotly_chart(fig)
108
+
109
+ # Conclusions sur l'implantation des taxis
110
+
111
+ st.markdown("<h4>👉 Manhattan et le Queens sont les principaux foyers de taxis. "
112
+ "Suivent Brooklyn, le Bronx, Staten Island et enfin l'aéroport de Newark</h4>", unsafe_allow_html=True)
113
+
114
+ st.markdown("---")
115
+
116
+ # Visualisation heatmap et conclusion
117
+
118
+ st.header("2. Les pics horaires")
119
+ st.subheader("- Quels sont les cr��neaux les plus critiques ?")
120
+
121
+ image_url = "https://uber-cdsd-joel.s3.eu-west-3.amazonaws.com/heatmap+UBER.png"
122
+ st.markdown(
123
+ f"""
124
+ <div style="display: flex; justify-content: center;">
125
+ <img src="{image_url}" width="1000">
126
+ </div>
127
+ """,
128
+ unsafe_allow_html=True
129
+ )
130
+ st.write("")
131
+
132
+ st.markdown("""
133
+ <h5>👉 Observations sur les horaires des trajets :</h5>
134
+ <ul>
135
+ <li><strong>Matinée (6h-9h, jours de semaine) :</strong> activité principalement liée aux trajets professionnels.</li>
136
+ <li><strong>Après-midi (14h-22h) :</strong> densité accrue le mercredi entre 15h et 21h.</li>
137
+ <li><strong>Vendredi :</strong> activité constante en fin de journée jusqu'au samedi 1h du matin.</li>
138
+ <li><strong>Samedi :</strong> concentration entre 15h et dimanche 1h du matin, liée aux loisirs.</li>
139
+ </ul>
140
+ <p>Ces chiffres mettent en avant les besoins des clients pour les trajets professionnels le matin et pour les loisirs le soir et le week-end.</p>
141
+ """, unsafe_allow_html=True)
142
+
143
+ st.markdown("---")
144
+
145
+ st.subheader("3. Rentrons maintenant dans le détail des clusters d'activité d'Uber sur cette période ! " \
146
+ "Pour ce faire, l'algorithme Kmeans sera utilisé comme première approche.")
147
+
148
+ # KMeans : Préparation du dataset pour visu des clusters du mercredi 17h
149
+
150
+ apr_14_wed_17 = apr_14[(apr_14['day_of_week'] == 'Wednesday') & (apr_14['hour'] == 17)].copy()
151
+ coords = apr_14_wed_17[['Lat', 'Lon']]
152
+
153
+ kmeans = KMeans(n_clusters=8, random_state=42)
154
+ apr_14_wed_17['clusterKmeans'] = kmeans.fit_predict(coords)
155
+
156
+ fig = px.scatter_mapbox(
157
+ apr_14_wed_17,
158
+ lat="Lat",
159
+ lon="Lon",
160
+ color="clusterKmeans",
161
+ height=800,
162
+ width=600,
163
+ zoom=10,
164
+ mapbox_style="carto-positron")
165
+ st.plotly_chart(fig, use_container_width=True)
166
+
167
+ st.markdown("""
168
+ ### Les clusters définis dans Kmeans mettent en avant des zones distinctes :
169
+ - Le Sud de Manhattan (cluster 3)
170
+ - Le Centre de Manhattan (cluster 1)
171
+ - Le Nord de Manhattan avec notamment une concentration dans la moitié sud de Central Park (cluster 7), ainsi que le nord de Central Park (cluster 4)
172
+ - Les aéroports de La Guardia (cluster 0), JFK (cluster 2), et Newark (cluster 5)
173
+
174
+ Cela parait tout à fait pertinent compte-tenu du jour et de l'heure choisis.
175
+ En effet, cela met en avant les actifs de Manhattan ainsi que les affluences aux différents aéroports.
176
+ Les clusters 1, 3, 4 et 7 sont donc très denses et sont les plus importants.
177
+ """)
178
+
179
+ st.markdown("---")
180
+
181
+ st.subheader("""
182
+ 👉 Vérifions maintenant la concordance entre les clusters des courses et les implantations des taxis"""
183
+ )
184
+ st.write("")
185
+
186
+ # Visualisation Clusters Kmeans mercredi 17h avec concordance densité taxis
187
+
188
+ apr_14_wed_17 = apr_14[
189
+ (apr_14['day_of_week'] == 'Wednesday') &
190
+ (apr_14['hour'] == 17)
191
+ ].copy()
192
+
193
+ coords = apr_14_wed_17[['Lat', 'Lon']]
194
+
195
+ kmeans = KMeans(n_clusters=8, random_state=42)
196
+ apr_14_wed_17['clusterKmeans'] = kmeans.fit_predict(coords)
197
+
198
+ # Figure Mapbox unique
199
+ fig = go.Figure()
200
+
201
+ # --- couche clusters (points)
202
+ fig.add_trace(go.Scattermapbox(
203
+ lat=apr_14_wed_17['Lat'],
204
+ lon=apr_14_wed_17['Lon'],
205
+ mode='markers',
206
+ marker=dict(
207
+ size=6,
208
+ color=apr_14_wed_17['clusterKmeans'],
209
+ colorscale='Viridis',
210
+ opacity=0.6
211
+ ),
212
+ name='Clusters (Mercredi 17h)',
213
+ hoverinfo='text'
214
+ ))
215
+
216
+ # --- couche densité boroughs
217
+ fig.add_trace(go.Scattermapbox(
218
+ lat=df_density['Latitude'],
219
+ lon=df_density['Longitude'],
220
+ mode='markers',
221
+ marker=dict(
222
+ size=df_density['Count'],
223
+ sizemode='area',
224
+ sizeref=2.*max(df_density['Count'])/(50.**2),
225
+ color='red',
226
+ opacity=0.7
227
+ ),
228
+ text=df_density['Borough'] + "<br>Count: " + df_density['Count'].astype(str),
229
+ name='Densité par Borough'
230
+ ))
231
+
232
+ # Layout commun
233
+ fig.update_layout(
234
+ mapbox=dict(
235
+ style="carto-positron",
236
+ center=dict(lat=40.73, lon=-73.94),
237
+ zoom=10
238
+ ),
239
+ height=900,
240
+ width=600,
241
+ margin=dict(l=0, r=0, t=0, b=0)
242
+ )
243
+
244
+ st.plotly_chart(fig)
245
+
246
+ st.markdown("""
247
+ ### Compte-tenu des données prises en compte avec notamment un centrage sur les coordonnées de New-York :
248
+ - L'implantation dans Manhattan est bien évidemment pertinente, la localisation exacte n'étant pas précisée.
249
+ - Le Bronx dispose d'un flotte importante qui reste à proximité de Manhattan.
250
+ - L'implantation dans le Queens paraît discutable, mais nous ne disposons pas de la totalité de l'information des courses potentielles du secteur.
251
+ - L'aéroport de Newark paraît sous-dimensionné
252
+ - Staten Island ne paraît pas justifié, mais encore une fois les données ne concernent que New-York.
253
+ """)
254
+
255
+ st.write("---")
256
+
257
+ st.subheader("👉 Procédons maintenant avec un second algorithme de clustering : DBScan. " \
258
+ "Il a pour propriété notamment de traiter les outliers et de proposer une vision plus concise des clusters.")
259
+
260
+ # TEST DE DB SCAN
261
+
262
+ # Sélection des coordonnées
263
+ X = apr_14_wed_17[['Lat', 'Lon']]
264
+
265
+ # DBSCAN clustering avec données définies dans notebook
266
+ db = DBSCAN(
267
+ eps=0.008,
268
+ min_samples=10,
269
+ metric="manhattan"
270
+ )
271
+ db.fit(X)
272
+
273
+ # Ajout des labels au DataFrame
274
+ apr_14_wed_17["clusterDB"] = db.labels_
275
+
276
+ # Filtrer les outliers (-1)
277
+ df_clusters = apr_14_wed_17[apr_14_wed_17["clusterDB"] != -1]
278
+
279
+ # Carte
280
+ fig = px.scatter_mapbox(
281
+ df_clusters,
282
+ lat="Lat",
283
+ lon="Lon",
284
+ color="clusterDB",
285
+ zoom=10,
286
+ height=1000,
287
+ mapbox_style="carto-positron"
288
+ )
289
+
290
+ st.plotly_chart(fig, use_container_width=True)
291
+
292
+ st.markdown("""
293
+ ### Comme attendu, DBScan nous donne des clusters moins étendus, plus recentrés :
294
+ - Un total de 11 clusters.
295
+ - Manhattan n'est plus divisé et représente un cluster unique.
296
+ - Les 3 aéroports sont bien identifiés.
297
+ - Des clusters sont répartis autour de Manhattan (clusters 1, 2, 3, 5, 11 et 9).
298
+ """)
299
+
300
+ st.markdown("---")
301
+
302
+ st.subheader("👉 Vérifions enfin la concordance entre les clusters DBSCAN et l'implantation des taxis")
303
+
304
+ st.markdown("")
305
+
306
+ # Superposition des deux graphs de densité et clusters DBSCAN
307
+
308
+ # Attribuer une couleur à chaque cluster
309
+
310
+ colors = ['orange', 'blue', 'green', 'orange', 'purple', 'brown', 'pink', 'cyan', 'yellow', 'black']
311
+ cluster_colors = {c: colors[i % len(colors)] for i, c in enumerate(sorted(df_clusters['clusterDB'].unique()))}
312
+
313
+ # Figure
314
+ fig = go.Figure()
315
+
316
+ # Couche clusters
317
+ for c in sorted(df_clusters['clusterDB'].unique()):
318
+ dfc = df_clusters[df_clusters['clusterDB'] == c]
319
+ fig.add_trace(go.Scattermapbox(
320
+ lat=dfc['Lat'],
321
+ lon=dfc['Lon'],
322
+ mode='markers',
323
+ marker=dict(
324
+ size=6,
325
+ color=cluster_colors[c],
326
+ opacity=0.6
327
+ ),
328
+ name=f'Cluster {c}',
329
+ hoverinfo='text'
330
+ ))
331
+
332
+ # Couche densité boroughs
333
+ for i, row in df_density.iterrows():
334
+ fig.add_trace(go.Scattermapbox(
335
+ lat=[row['Latitude']],
336
+ lon=[row['Longitude']],
337
+ mode='markers',
338
+ marker=dict(
339
+ size=row['Count'],
340
+ color='red',
341
+ opacity=0.7
342
+ ),
343
+ name=row['Borough'],
344
+ hoverinfo='text',
345
+ text=f"{row['Borough']}<br>Count: {row['Count']}"
346
+ ))
347
+
348
+ # Layout
349
+ fig.update_layout(
350
+ mapbox=dict(
351
+ style="carto-positron",
352
+ center=dict(lat=40.73, lon=-73.94),
353
+ zoom=10
354
+ ),
355
+ height=900,
356
+ margin=dict(l=0, r=0, t=0, b=0)
357
+ )
358
+
359
+ # Streamlit
360
+ st.plotly_chart(fig, use_container_width=True)
361
+
362
+ st.write("---")
363
+
364
+ st.subheader("""
365
+ 👉 Analyse des clusters :
366
+
367
+ - On constate une bonne concordance entre les zones d'implantation des taxis et les clusters,
368
+ du moins dans le centre de New York.
369
+ - Les taxis du Bronx sont justifiés par la proximité de Manhattan.
370
+ - L'étude devant isoler cette zone, une concentration de taxis paraît plus éloigné du centre.
371
+ Cela est certainement dû à une activité importante dans une zone non concernée par l'étude.
372
+ """)
373
+
374
+ st.write("---")
375
+
376
+ st.subheader("""
377
+ 👉 Après cette étude comparative entre KMeans et DBScan, quel est l'algorithme le plus adapté à ce projet ?
378
+
379
+ Selon moi, KMeans me paraît plus précis dans la définition des clusters et permet d'avoir une approche plus fine dans la définition des clusters.
380
+ Le fait de définir soi-même les outliers permet de mieux contrôler les résultats, je l'utiliserais donc pour la suite de l'analyse qui va porter sur les différents jours de la semaine.
381
+
382
+ """)
383
+
384
+
385
+ with tab2:
386
+ st.header("L'analyse des clusters")
387
+ st.subheader("Évolution des clusters Uber selon les jours de la semaine et rapprochement avec l'implantation des taxis ")
388
+
389
+ clusters_by_day = {
390
+ "Monday": 7,
391
+ "Tuesday": 10,
392
+ "Wednesday": 7,
393
+ "Thursday": 6,
394
+ "Friday": 7,
395
+ "Saturday": 7,
396
+ "Sunday": 7,
397
+ }
398
+
399
+ days_translation = {
400
+ "Monday": "Lundi",
401
+ "Tuesday": "Mardi",
402
+ "Wednesday": "Mercredi",
403
+ "Thursday": "Jeudi",
404
+ "Friday": "Vendredi",
405
+ "Saturday": "Samedi",
406
+ "Sunday": "Dimanche",
407
+ }
408
+
409
+ selected_day_fr = st.selectbox(
410
+ "Choisissez le jour souhaité :",
411
+ list(days_translation.values())
412
+ )
413
+
414
+ fr_to_en = {v: k for k, v in days_translation.items()}
415
+ selected_day_en = fr_to_en[selected_day_fr]
416
+
417
+ filtered_df = apr_14[apr_14["day_of_week"] == selected_day_en].copy()
418
+
419
+ filtered_df = compute_kmeans(
420
+ filtered_df,
421
+ clusters_by_day[selected_day_en]
422
+ )
423
+
424
+ # --- Visualisation clusters + densité taxi ---
425
+ fig = go.Figure()
426
+
427
+ fig.add_trace(go.Scattermapbox(
428
+ lat=filtered_df["Lat"],
429
+ lon=filtered_df["Lon"],
430
+ mode="markers",
431
+ marker=dict(
432
+ size=6,
433
+ color=filtered_df["clusterKmeans"],
434
+ colorscale="Viridis",
435
+ opacity=0.6
436
+ ),
437
+ name=f"Clusters ({selected_day_fr})"
438
+ ))
439
+
440
+ fig.add_trace(go.Scattermapbox(
441
+ lat=df_density["Latitude"],
442
+ lon=df_density["Longitude"],
443
+ mode="markers",
444
+ marker=dict(
445
+ size=df_density["Count"],
446
+ sizemode="area",
447
+ sizeref=2.*max(df_density["Count"])/(50.**2),
448
+ color="red",
449
+ opacity=0.7
450
+ ),
451
+ text=df_density["Borough"] + "<br>Stations: " + df_density["Count"].astype(str),
452
+ hoverinfo="text",
453
+ name="Densité stations taxi"
454
+ ))
455
+
456
+ fig.update_layout(
457
+ mapbox=dict(
458
+ style="carto-positron",
459
+ center=dict(lat=40.73, lon=-73.94),
460
+ zoom=10
461
+ ),
462
+ height=900,
463
+ title=f"Clusters Uber ({selected_day_fr}) & densité des stations taxi",
464
+ margin=dict(l=0, r=0, t=40, b=0)
465
+ )
466
+
467
+ st.plotly_chart(fig, use_container_width=True)
468
+
469
+
470
+ # REPRENDRE ICI !!!!!!
471
+
472
+ with tab3:
473
+
474
+ st.subheader("Premier constat sur l'implantation des taxis")
475
+ st.markdown("""
476
+ La répartition des bases de taxis UBER semble globalement cohérente avec la demande observée :
477
+ - Les zones de Manhattan et Brooklyn sont très bien couvertes.
478
+ - Les zones du Queens et du Bronx paraissent surdimensionnées, mais cela peut s'expliquer par la proximité de Manhattan.
479
+ - Le dimensionnement paraît surévalué dans Staten Island et sous-évalué à Newark, mais le recentrager sur New-York peut expliquer cela.
480
+ """)
481
+
482
+ st.subheader("Second constat sur les pics horaires")
483
+ st.markdown("""
484
+ Les pics d'activité sont sur les créneaux suivants :
485
+ - Une activité certaine les matinées de la semaine entre 6h et 9h
486
+ - Les après-midi de 14h à 21h pour les mardi, mercredi, jeudi, vendredi
487
+ - Un pic particulièrement intense le mercredi après-midi
488
+ - Le vendredi soir jusqu'au samedi 1h du matin
489
+ - Le samedi après-midi jusqu'au dimanche 1h du matin également
490
+
491
+ Ces pics correspondent aux besoins professionnels (matinée en semaine) et de loisirs (fin d'après-midi et soirée, surtout le week-end).
492
+ """)
493
+
494
+ st.markdown("---")
495
+
496
+ st.subheader("Conclusion et recommandations")
497
+
498
+ st.markdown(""" L'étude des clusters Uber en avril 2014 à New York révèle une implantation globalement cohérente des taxis par rapport à la demande.
499
+ Cependant, quelques ajustements pourraient être envisagés :
500
+ - Réévaluer la densité des taxis dans le Queens et le Bronx pour optimiser les ressources, à vérifier avec des données plus complètes.
501
+ - Ajuster la couverture à Newark et Staten Island en fonction des besoins réels.
502
+ - Surveiller les pics d'activité identifiés pour adapter la flotte en conséquence, notamment les matinées en semaine et les fins d'après-midi/soirées du week-end.
503
+ """)
504
+
505
+ st.markdown(""" Bien évidemment, ces conclusions sont basées sur des données d'avril 2014 et pourraient différer avec des données plus récentes ou complètes.
506
+ """)