Djohell commited on
Commit
ad25235
·
1 Parent(s): 263e5f6

first_commit

Browse files
app.py ADDED
@@ -0,0 +1,665 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import plotly.express as px
4
+ import requests
5
+ import plotly.graph_objects as go
6
+ from plotly.subplots import make_subplots
7
+
8
+ st.set_page_config(page_title="Getaround – Dashboard Analyse", layout="wide")
9
+
10
+ st.title("🚙🚕 Bonjour et bienvenue sur le dashboard du projet Getaround ! 🚗🚑")
11
+
12
+ st.markdown("""
13
+
14
+ A votre disposition 3 rubriques :
15
+
16
+ 1. L'analyse des locations : leur type, les retards, les annulations
17
+ 2. L'analyse tarifaire
18
+ 3. Les prédiction par API
19
+ """)
20
+
21
+ tab1, tab2, tab3 = st.tabs(["📊⏱ Analyse des locations et retards", "💲Analyse tarifaire", "🔮✨ Prédiction par API"])
22
+
23
+ with tab1:
24
+
25
+ # Import des datasets
26
+
27
+ delay = pd.read_excel("data/get_around_delay_analysis.xlsx")
28
+ price=pd.read_csv('data/get_around_pricing_project.csv')
29
+ annulation_by_delay=pd.read_csv('data/annulation_by_delay.csv')
30
+
31
+ st.header("1. L'analyse des chiffres sur 21 310 locations")
32
+ st.subheader("- A quelle fréquence sont loués les véhicules ?")
33
+
34
+ # ANALYSE DES LOCATIONS
35
+
36
+ # Premier graph nombre de locations des véhicules
37
+
38
+ car_usage = delay['car_id'].value_counts()
39
+
40
+ car_usage_top10 = car_usage[car_usage <= 15]
41
+
42
+ fig = px.histogram(
43
+ car_usage_top10,
44
+ nbins=15,
45
+ title="Distribution des véhicules peu loués (1 à 15 locations)",
46
+ labels={'value': 'Nombre de locations', 'count': 'Nombre de véhicules'}
47
+ )
48
+
49
+ fig.update_layout(
50
+ bargap=0.1,
51
+ height=600
52
+ )
53
+
54
+ st.plotly_chart(fig, use_container_width=True)
55
+
56
+
57
+ st.markdown("<h4>👉 Près de 45% des véhicules n'ont été loués qu'une seule fois</h4>", unsafe_allow_html=True)
58
+
59
+ st.markdown("---")
60
+
61
+ st.header("2. L'Analyse du type de location et des annulations")
62
+ st.subheader("- Quel mode de location est privilégié et quelle proportion d'annulation ?")
63
+
64
+ # Second graph type de locations et annulations
65
+
66
+ checkin_counts = delay['checkin_type'].value_counts()
67
+ course_state = delay['state'].value_counts()
68
+
69
+ fig = make_subplots(
70
+ rows=1, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}]],
71
+ subplot_titles=("Répartition des types de check-in", "Répartition des statuts des locations")
72
+ )
73
+
74
+ fig.add_trace(
75
+ go.Pie(labels=checkin_counts.index, values=checkin_counts.values, name="Check-in type"),
76
+ row=1, col=1
77
+ )
78
+
79
+ fig.add_trace(
80
+ go.Pie(labels=course_state.index, values=course_state.values, name="State"),
81
+ row=1, col=2
82
+ )
83
+
84
+ fig.update_traces(hole=0, textinfo='label+percent')
85
+ fig.update_layout(
86
+ height=500,
87
+ width=900,
88
+
89
+ showlegend=False,
90
+ )
91
+
92
+ st.plotly_chart(fig, use_container_width=True)
93
+
94
+ st.markdown("<h4>👉 Concernant les locations, le checkin mobile représente 80% quand le connect n'est que de 20%." \
95
+ " La proportion de courses annulées est de 15% environ pour un total d'environ 3200", unsafe_allow_html=True)
96
+
97
+ st.markdown("---")
98
+
99
+ # Troisième graph proportion d'annulation selon le type de location
100
+
101
+ st.subheader("Quelle mode de location est plus impacté par les annulations ?")
102
+
103
+ total_per_type = delay.groupby('checkin_type')['state'].count()
104
+ canceled_per_type = delay[delay['state'] == 'canceled'].groupby('checkin_type')['state'].count()
105
+ proportion_canceled = (canceled_per_type / total_per_type).fillna(0) * 100
106
+
107
+ prop_delay = proportion_canceled.reset_index()
108
+ prop_delay.columns = ['checkin_type', 'proportion_canceled']
109
+
110
+ color_map = {
111
+ 'mobile': 'pink',
112
+ 'connect': 'royalblue'
113
+ }
114
+
115
+ fig = px.bar(
116
+ prop_delay,
117
+ x='checkin_type',
118
+ y='proportion_canceled',
119
+ text=prop_delay['proportion_canceled'].round(1),
120
+ labels={'checkin_type':'Type de check-in', 'proportion_canceled':'% Annulations'},
121
+ color='checkin_type',
122
+ color_discrete_map=color_map
123
+ )
124
+
125
+ fig.update_layout(
126
+ height=600
127
+ )
128
+ fig.update_traces(textposition='outside')
129
+
130
+ st.plotly_chart(fig, use_container_width=True)
131
+
132
+ st.markdown("<h4>👉 Selon le type de checkin, la proportion d'annulation est différente. Les utilisateurs de la version Connect " \
133
+ "sont plus susceptibles d'annuler leur location de 4 points"
134
+ "", unsafe_allow_html=True)
135
+
136
+ st.markdown("---")
137
+
138
+ st.header("3. Analyse des retards")
139
+
140
+ # Traitement outlier des retards
141
+
142
+ summary = delay['delay_at_checkout_in_minutes'].describe()
143
+
144
+ Q1 = delay['delay_at_checkout_in_minutes'].quantile(0.25)
145
+ Q3 = delay['delay_at_checkout_in_minutes'].quantile(0.75)
146
+ IQR = Q3 - Q1
147
+
148
+ lower_bound = Q1 - 1.5*IQR
149
+ upper_bound = Q3 + 1.5*IQR
150
+
151
+ delay_filtered = delay[(delay['delay_at_checkout_in_minutes'] >= lower_bound) &
152
+ (delay['delay_at_checkout_in_minutes'] <= upper_bound)]
153
+
154
+ # Quatrième Graph de distribution des retards
155
+
156
+ st.markdown("""
157
+ Regardons dans le détail la distribution des délais de retard""")
158
+
159
+ fig = px.histogram(
160
+ delay_filtered,
161
+ x='delay_at_checkout_in_minutes',
162
+ nbins=50,
163
+ color='checkin_type',
164
+ barmode='overlay',
165
+ labels={'delay_at_checkout_in_minutes': 'Retard au checkout (minutes)', 'count': 'Nombre de locations'},
166
+ color_discrete_map={'mobile': 'royalblue', 'connect': 'crimson'}
167
+ )
168
+
169
+ fig.update_layout(
170
+ height=600,
171
+ bargap=0.1
172
+ )
173
+
174
+ st.plotly_chart(fig, use_container_width=True)
175
+
176
+
177
+ st.markdown("""
178
+ 👉 L'absence de retard dans le total des courses est importante et les valeurs se centrent autour de 0.
179
+ Cependant nous constatons que de retards importants peuvent être présents."""
180
+ )
181
+
182
+ # Centrage sur les courses annulées :
183
+
184
+ # Création du dataframe relatant les cancel
185
+
186
+ delay_canceled = delay[delay["state"] == "canceled"][["rental_id", "car_id", "previous_ended_rental_id",
187
+ "checkin_type","time_delta_with_previous_rental_in_minutes"]]
188
+
189
+ # Rattachement de la location annulée à l'id de la location précédente
190
+
191
+ delay_merged = delay_canceled.merge(
192
+ delay[["rental_id", "car_id", "delay_at_checkout_in_minutes", "checkin_type"]],
193
+ left_on="previous_ended_rental_id",
194
+ right_on="rental_id",
195
+ suffixes=("_canceled", "_previous")
196
+ )
197
+
198
+ # Inspection de l'écart entre le delta et le retard
199
+
200
+ delay_problems = delay_merged[
201
+ delay_merged["delay_at_checkout_in_minutes"] > delay_merged["time_delta_with_previous_rental_in_minutes"]
202
+ ]
203
+
204
+ # Découpage des retards en tranches horaires
205
+
206
+ bins = [0, 15, 30, 60, 120, 9999]
207
+ labels = ["0-15 min", "15-30 min", "30-60 min", "1-2 h", ">2 h"]
208
+
209
+ delay_problems["delay_category"] = pd.cut(
210
+ delay_problems["delay_at_checkout_in_minutes"],
211
+ bins=bins,
212
+ labels=labels,
213
+ include_lowest=True
214
+ )
215
+
216
+ st.markdown("---")
217
+
218
+ st.subheader("""Quelle est la proportion de la durée des différents retards dans les annulations ?""")
219
+
220
+ # Cinquième graph de la part des durées de retard dans les annulations selon le checkin :
221
+
222
+ fig = px.pie(
223
+ delay_problems,
224
+ names="delay_category",
225
+ facet_col="checkin_type_canceled",
226
+ )
227
+
228
+ fig.for_each_annotation(lambda a: a.update(
229
+ text=a.text.split("=")[-1]
230
+ ))
231
+
232
+ st.plotly_chart(fig, use_container_width=True)
233
+
234
+ st.markdown("""Les retards de plus de 2 heures représentent la majeure partie des annulations,
235
+ ce qui paraît logique pour le futur client. Le check-in mobile présente une proportion
236
+ de cette durée supérieure de 8,9 points au connect.""")
237
+
238
+
239
+ st.markdown("---")
240
+
241
+ st.header("""Quel est l'impact réel des retards sur les annulations, selon le type de check-in ?""")
242
+
243
+ # Création d'intervalles pour déterminer les différents créneaux de retard
244
+
245
+ bins = [-9999, 0, 15, 30, 60, 120, 240, 9999]
246
+ labels = ["≤0", "1-15 min", "15-30 min", "30-60 min", "1-2 h", "2-4 h", ">4 h"]
247
+
248
+ delay_merged["delay_group"] = pd.cut(
249
+ delay_merged["delay_at_checkout_in_minutes"],
250
+ bins=bins,
251
+ labels=labels,
252
+ include_lowest=True
253
+ )
254
+
255
+ # Groupage des annulations par intervalles de retard
256
+ annulation_by_delay = (
257
+ delay_merged.groupby(["delay_group", "checkin_type_canceled"])["rental_id_canceled"]
258
+ .count()
259
+ .reset_index(name="nb_annulations")
260
+ )
261
+
262
+ # Sixième graph pour visualisation de l'importance de la durée des retards
263
+
264
+ fig = px.bar(
265
+ annulation_by_delay,
266
+ x="delay_group",
267
+ y="nb_annulations",
268
+ color="checkin_type_canceled",
269
+ barmode="group",
270
+ labels={
271
+ "delay_group": "Temps de retard",
272
+ "nb_annulations": "Nombre d'annulations",
273
+ "checkin_type_canceled": "Type de check-in"
274
+ }
275
+ )
276
+
277
+ st.plotly_chart(fig, use_container_width=True)
278
+
279
+ # Analyse des locations maintenues malgré un retard précédent
280
+
281
+ st.header("""Quels sont les chiffres des locations maintenues malgré un retard précédent ?""")
282
+
283
+ # Création du dataframe relatant les ended
284
+ delay_ended = delay[delay["state"] == "ended"][["rental_id", "car_id", "previous_ended_rental_id",
285
+ "checkin_type","time_delta_with_previous_rental_in_minutes"]]
286
+
287
+ delay_ended_merged = delay_ended.merge(
288
+ delay[["rental_id", "car_id", "delay_at_checkout_in_minutes", "checkin_type"]],
289
+ left_on="previous_ended_rental_id",
290
+ right_on="rental_id",
291
+ suffixes=("_ended", "_previous") )
292
+
293
+ delay_ended_merged["delay_group"] = pd.cut(
294
+ delay_ended_merged["delay_at_checkout_in_minutes"],
295
+ bins=bins,
296
+ labels=labels,
297
+ include_lowest=True
298
+ )
299
+
300
+ # Agrégation : nombre de courses terminées malgré un retard
301
+ ended_by_delay = (
302
+ delay_ended_merged.groupby(["delay_group", "checkin_type_ended"])["rental_id_ended"].count().reset_index(name="nb_ended")
303
+ )
304
+
305
+ # Titre Streamlit
306
+ st.title("Analyse des locations maintenues malgré un retard")
307
+
308
+ # Graphique Plotly
309
+ fig = px.bar(
310
+ ended_by_delay,
311
+ x="delay_group",
312
+ y="nb_ended",
313
+ color="checkin_type_ended",
314
+ barmode="group",
315
+ title="Évolution des locations maintenues selon la durée du retard et le type de check-in",
316
+ labels={
317
+ "delay_group": "Temps de retard",
318
+ "nb_ended": "Nombre de locations maintenues",
319
+ "checkin_type_ended": "Type de check-in"
320
+ }
321
+ )
322
+
323
+ st.plotly_chart(fig, use_container_width=True)
324
+
325
+ st.subheader('Les locations maintenue malgré un retard précédent sont en nombre beaucoup plus important que les annulations. ' \
326
+ 'Sur le seul créneau de 1 à 15 minutes, elles sont au nombre de 203, quand seulement 17 sont annulées sur ce même retard. ' \
327
+ 'Pour le créneau de 15 à 30 minutes, elles sont 127 de maintenues contre 17 annulées.')
328
+
329
+ st.markdown('---')
330
+
331
+ st.subheader("Premier constat")
332
+ st.markdown("""
333
+ Les annulations des véhicules précédemment loués ne sont pas liées à un retard,
334
+ puisque dans **100 cas sur 229 (43%)**, il n'y en a pas.
335
+
336
+ - Sur ce point, il y a donc d'autres causes à aller chercher potentiellement pour *Get Around*,
337
+ mais qui ne dépendent pas du retard de la précédente location.
338
+ - Cela restera néanmoins un point important à creuser pour la société afin d'optimiser le CA.
339
+ - Il est d'ailleurs presque **2 fois plus prononcé sur le connect que sur le mobile**.
340
+ - Le fait de ne pas avoir à rencontrer le propriétaire favorise peut-être le désengagement du locataire.
341
+ """)
342
+
343
+
344
+ st.subheader("Second constat")
345
+ st.markdown("""
346
+ Les annulations dans les cas de retard de **1 à 60 minutes** :
347
+
348
+ - Sont **croissantes pour le connect**
349
+ - Sont **décroissantes pour le mobile**, mais 3 fois plus nombreuses sur le plus petit retard
350
+ - Représentent **55 cas sur les 129 restants (42% des annulations)**
351
+ """)
352
+
353
+
354
+ st.subheader("Dernier constat")
355
+ st.markdown("""
356
+ Les retards les plus modérés de **1 à 30 minutes** :
357
+
358
+ - Entraînent plus d'annulations sur le **mobile** que sur le **connect**
359
+ - Représentent **34 cas sur les 129 restants (14% des annulations seulement)**
360
+ """)
361
+
362
+ st.markdown("---")
363
+
364
+ st.subheader("Première conclusion sur les locations")
365
+
366
+ st.markdown("### 📊 Analyse des annulations et facteurs en jeu")
367
+
368
+ st.markdown("""
369
+ ❗ Le **facteur temps** (les retards) n’apparaît **pas comme la cause principale** des annulations.
370
+ ➝ Même avec **60 minutes de retard**, on n’explique que **42% des annulations**.""")
371
+
372
+ st.markdown("🔍 Cela suggère que **d’autres causes majeures** restent à identifier :")
373
+
374
+ st.markdown("""
375
+ - politique tarifaire
376
+ - offre et concurrence
377
+ - notifications de disponibilité des véhicules
378
+ - comportements utilisateurs""")
379
+
380
+ st.markdown("📱 La **méthode de check-in mobile** est un levier à privilégier : elle concerne **80% des locations**.")
381
+
382
+ st.markdown("""⚖️ La mise en place d’un délai supplémentaire de disponibilité du véhicule aurait un impact :
383
+ - sur les **propriétaires** (perte potentielle de visibilité, donc moins de trafic et de revenus)
384
+ - sur les **locataires** (réduction de l’offre disponible, frustration potentielle)""")
385
+
386
+ st.markdown("""💡 Des pistes de solutions à explorer :
387
+ - sensibilisation des utilisateurs à la ponctualité
388
+ - notifications de rappel 30 minutes avant la fin de la location
389
+ - système de bonus/malus
390
+ - acompte à la réservation pour responsabiliser le locataire
391
+ - programme de fidélité """)
392
+
393
+ st.markdown(""" 📉 Enfin, il est nécessaire d’évaluer les **pertes potentielles pour les propriétaires**
394
+ si un délai additionnel est mis en place, surtout pour le mode de location majoritaire.
395
+ """)
396
+
397
+
398
+
399
+ # ANALYSE TARIFAIRE
400
+
401
+ with tab2:
402
+
403
+ st.header("1. Le parc automobile des propriétaires")
404
+
405
+ st.subheader("Quelles sont les 10 marques les plus présentes ?")
406
+
407
+ # Tri des véhicules pour déterminer les véhicules uniques
408
+
409
+ cols_check = [
410
+ "model_key", "engine_power", "fuel", "paint_color", "car_type",
411
+ "private_parking_available", "has_gps", "has_air_conditioning",
412
+ "automatic_car", "has_getaround_connect", "has_speed_regulator",
413
+ "winter_tires"
414
+ ]
415
+
416
+ price_dedup = price.drop_duplicates(subset=cols_check, keep="first")
417
+
418
+ # Premier graph de la représentation des 10 premières marques
419
+
420
+ model_counts = price_dedup["model_key"].value_counts().reset_index()
421
+ model_counts.columns = ["model_key", "total"]
422
+
423
+ top10_models = model_counts.head(10)
424
+
425
+ fig = px.bar(
426
+ top10_models,
427
+ x="model_key",
428
+ y="total",
429
+ labels={"model_key": "Marque", "total": "Véhicules"}
430
+ )
431
+
432
+ st.plotly_chart(fig, use_container_width=True)
433
+
434
+ # Second graph de la représentation des marques les plus louées
435
+
436
+ st.subheader('Quelles sont les marques ayant le plus de succès ?')
437
+
438
+ model_counts = price["model_key"].value_counts().reset_index()
439
+ model_counts.columns = ["model_key", "total"]
440
+
441
+ top10_models = model_counts.head(10)
442
+
443
+ fig = px.bar(
444
+ top10_models,
445
+ x="model_key",
446
+ y="total",
447
+ labels={"model_key": "Marque", "total": "Véhicules"}
448
+ )
449
+
450
+ st.plotly_chart(fig, use_container_width=True)
451
+
452
+ st.markdown('Les 3 marques les plus présentes sont aussi les plus louées : Citroën, BMW et Renault')
453
+
454
+ st.markdown('---')
455
+
456
+ # Troisième graph pour estimer le prix d'une location
457
+
458
+ st.subheader("Quelle base tarifaire pour estimer le prix d'une location ?")
459
+
460
+ fig = px.histogram(
461
+ price,
462
+ x="rental_price_per_day",
463
+ nbins=30,
464
+ labels={"rental_price_per_day": "Prix journalier de la location"}
465
+ )
466
+
467
+ # Calcul médiane, moyenne et prix de référence
468
+ median_value = price["rental_price_per_day"].median()
469
+ mean_value = price["rental_price_per_day"].mean()
470
+ ref_price = ((mean_value + median_value)/2).round(2)
471
+
472
+ # Ligne verticale médiane
473
+ fig.add_vline(
474
+ x=median_value,
475
+ line_dash="dash",
476
+ line_color="red",
477
+ annotation_text=f"Médiane = {median_value:.2f}",
478
+ annotation_position="top right"
479
+ )
480
+
481
+ # Ligne verticale moyenne
482
+ fig.add_vline(
483
+ x=mean_value,
484
+ line_dash="dot",
485
+ line_color="blue",
486
+ annotation_text=f"Moyenne = {mean_value:.2f}",
487
+ annotation_position="top left"
488
+ )
489
+
490
+ st.plotly_chart(fig, use_container_width=True)
491
+
492
+ st.markdown(" 👉 Le constat est simple, la moyenne tarifaire d'une location est de 121 dollars quand la médiane est à 119 dollars. " \
493
+ "La prix d'une location est donc fixé à la valeur intermédiaire soit 120,11 dollars.")
494
+
495
+ # Quatrième graph pour visualiser le montant des pertes selon les retards
496
+
497
+ st.subheader(("Quelle est le réel impact de perte de CA des retards ?"))
498
+
499
+ # Mise en place des intervalles
500
+
501
+ ordre = ["≤0", "1-15 min", "15-30 min", "30-60 min", "1-2 h", "2-4 h", ">4 h"]
502
+
503
+ # Transformer delay_group en catégorie ordonnée
504
+ annulation_by_delay["delay_group"] = pd.Categorical(
505
+ annulation_by_delay["delay_group"],
506
+ categories=ordre,
507
+ ordered=True
508
+ )
509
+
510
+ # On calcule la perte potentielle pour chaque ligne
511
+ annulation_by_delay["perte_potentielle"] = annulation_by_delay["nb_annulations"] * ref_price
512
+
513
+ # Tableau loss pour visualiser la perte par délai et type de checkin
514
+ loss = annulation_by_delay.pivot_table(
515
+ index="delay_group",
516
+ columns="checkin_type_canceled",
517
+ values="perte_potentielle",
518
+ aggfunc="sum",
519
+ fill_value=0
520
+ )
521
+
522
+ # Ajouter une colonne de perte totale par intervalle
523
+ loss["total_perte"] = loss.sum(axis=1)
524
+
525
+ # Affichage du graph
526
+
527
+ fig = go.Figure()
528
+
529
+ fig.add_trace(go.Bar(
530
+ x=loss.index,
531
+ y=loss["connect"],
532
+ name='Connect',
533
+ marker_color='blue',
534
+ hovertemplate='Connect: %{y:.2f} €<extra></extra>'
535
+ ))
536
+
537
+ fig.add_trace(go.Bar(
538
+ x=loss.index,
539
+ y=loss["mobile"],
540
+ name='Mobile',
541
+ marker_color='orange',
542
+ hovertemplate='Mobile: %{y:.2f} €<extra></extra>'
543
+ ))
544
+
545
+ fig.update_layout(
546
+ title="Perte potentielle de CA en fonction du retard",
547
+ xaxis_title="Intervalle de retard",
548
+ yaxis_title="Perte potentielle (€)",
549
+ template='plotly_white',
550
+ hovermode="x unified",
551
+ barmode="group"
552
+ )
553
+
554
+ st.plotly_chart(fig, use_container_width=True)
555
+
556
+ st.markdown("""
557
+ ### 📊 Analyse des pertes de CA liées aux retards
558
+
559
+ - **La plus grande perte de CA n'est clairement pas constituée par les retards**,
560
+ puisque nous avons **12 011 $** de perdus **sans aucun retard**, soit près de **la moitié des pertes (48,54%)**.
561
+ - **Mettre en place un délai supplémentaire ne paraît pas justifié**, puisque le nombre d'annulations liées à des
562
+ petits retards (1 à 15 min) représente **17 courses**, pour un total de **2 041 $**, soit **à peine 10% des pertes**.
563
+ - Pour atteindre l’équivalent des pertes non dues à un retard, il faudrait mettre en place un **délai de plus de 2 heures**,
564
+ sans garantie de trouver une course, ce qui va forcément **nuire à la visibilité des propriétaires**.
565
+ - Ici la conclusion est simple : si nous ajoutons un délai de 30 minutes, cela
566
+ diminuera l'impact de **34 cas seulement sur un total de 17 000 locations mobiles**. Il vaut mieux donc ne pas rajouter de délai au risque
567
+ d'insatisfaire un très grand nombre de propriétaires.
568
+ """)
569
+
570
+ st.subheader("En conclusion, il vaut mieux donc se concentrer sur les causes d'annulation tierces " \
571
+ "qui représentent près de la moitié du CA perdu")
572
+
573
+ st.markdown("""---""")
574
+
575
+ # Affichage de la matrice de corrélation
576
+
577
+ st.subheader("Regardons enfin les corrélations potentielles en vue " \
578
+ "de la mise en place d'un modèle prédictif")
579
+
580
+ bool_features = [
581
+ "private_parking_available", "has_gps", "has_air_conditioning",
582
+ "automatic_car", "has_getaround_connect", "has_speed_regulator", "winter_tires",
583
+ ]
584
+ price[bool_features] = price[bool_features].astype(int)
585
+
586
+ numeric_cols = ["mileage", "engine_power", "rental_price_per_day"] + bool_features
587
+
588
+ corr_matrix = price[numeric_cols].corr()
589
+
590
+ fig = px.imshow(
591
+ corr_matrix,
592
+ text_auto=".2f",
593
+ color_continuous_scale="RdBu_r",
594
+ )
595
+
596
+ fig.update_layout(
597
+ width=900,
598
+ height=700
599
+ )
600
+
601
+ st.plotly_chart(fig, use_container_width=True)
602
+
603
+ st.subheader("Les principaux facteurs semblent être : la puissance, la boîte de vitesses, " \
604
+ "la présence de Get Around connect ainsi qu'un parking privé")
605
+
606
+ with tab3:
607
+
608
+ st.subheader("Découvrez notre estimation du prix de location de votre véhicule !")
609
+
610
+ col1, col2 = st.columns(2)
611
+
612
+ with col1:
613
+ car_brand = st.selectbox("Marque du véhicule", [
614
+ "Citroën", "Peugeot", "PGO", "Renault", "Audi", "BMW", "Ford", "Mercedes",
615
+ "Opel", "Porsche", "Volkswagen", "KIA Motors", "Alfa Romeo", "Ferrari",
616
+ "Fiat", "Lamborghini", "Maserati", "Lexus", "Honda", "Mazda", "Mini",
617
+ "Mitsubishi", "Nissan", "SEAT", "Subaru", "Suzuki", "Toyota", "Yamaha"
618
+ ])
619
+ mileage = st.number_input("Kilométrage", min_value=0, max_value=400_000, value=100_000)
620
+ engine_power = st.number_input("Puissance du moteur", min_value=10, max_value=300, value=120)
621
+ fuel = st.selectbox("Carburant", ["diesel", "petrol", "hybrid_petrol", "electro"])
622
+
623
+ with col2:
624
+ paint_color = st.selectbox("Couleur", ["black", "grey", "white", "red", "silver", "blue",
625
+ "orange", "beige", "brown", "green"])
626
+ car_type = st.selectbox("Type de véhicule", ["convertible", "coupe", "estate", "hatchback",
627
+ "sedan", "subcompact", "suv", "van"])
628
+ private_parking_available = st.checkbox("Parking privé disponible")
629
+ has_gps = st.checkbox("GPS")
630
+ has_air_conditioning = st.checkbox("Climatisation")
631
+ automatic_car = st.checkbox("Boîte automatique")
632
+ has_getaround_connect = st.checkbox("Getaround Connect")
633
+ has_speed_regulator = st.checkbox("Régulateur de vitesse")
634
+ winter_tires = st.checkbox("Pneus hiver")
635
+
636
+ # Bouton pour appeler l'API
637
+
638
+ if st.button("Estimation du prix"):
639
+ data = {
640
+ "car_brand": car_brand,
641
+ "mileage": mileage,
642
+ "engine_power": engine_power,
643
+ "fuel": fuel,
644
+ "paint_color": paint_color,
645
+ "car_type": car_type,
646
+ "private_parking_available": private_parking_available,
647
+ "has_gps": has_gps,
648
+ "has_air_conditioning": has_air_conditioning,
649
+ "automatic_car": automatic_car,
650
+ "has_getaround_connect": has_getaround_connect,
651
+ "has_speed_regulator": has_speed_regulator,
652
+ "winter_tires": winter_tires
653
+ }
654
+
655
+
656
+ API_URL = "https://djohell-get-around-api.hf.space/predict"
657
+
658
+ try:
659
+ response = requests.post(API_URL, json=data)
660
+ response.raise_for_status()
661
+ prediction = response.json().get("prediction")
662
+ st.success(f"Prix journalier estimé : {prediction} $")
663
+
664
+ except requests.exceptions.RequestException as e:
665
+ st.error(f"Erreur lors de l'appel à l'API : {e}")
data/annulation_by_delay.csv ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ delay_group,checkin_type_canceled,nb_annulations
2
+ ≤0,connect,66
3
+ ≤0,mobile,34
4
+ 1-15 min,connect,4
5
+ 1-15 min,mobile,13
6
+ 15-30 min,connect,9
7
+ 15-30 min,mobile,8
8
+ 30-60 min,connect,14
9
+ 30-60 min,mobile,7
10
+ 1-2 h,connect,11
11
+ 1-2 h,mobile,9
12
+ 2-4 h,connect,8
13
+ 2-4 h,mobile,7
14
+ >4 h,connect,9
15
+ >4 h,mobile,7
data/ended_by_delay.csv ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ delay_group,checkin_type_ended,nb_ended
2
+ ≤0,connect,421
3
+ ≤0,mobile,335
4
+ 1-15 min,connect,84
5
+ 1-15 min,mobile,119
6
+ 15-30 min,connect,37
7
+ 15-30 min,mobile,90
8
+ 30-60 min,connect,49
9
+ 30-60 min,mobile,100
10
+ 1-2 h,connect,48
11
+ 1-2 h,mobile,115
12
+ 2-4 h,connect,24
13
+ 2-4 h,mobile,46
14
+ >4 h,connect,7
15
+ >4 h,mobile,47
data/get_around_pricing_project.csv ADDED
The diff for this file is too large to render. See raw diff
 
data/get_around_pricing_project_clean.csv ADDED
The diff for this file is too large to render. See raw diff
 
requirements.txt CHANGED
@@ -1,3 +1,8 @@
1
- altair
2
- pandas
3
- streamlit
 
 
 
 
 
 
1
+ streamlit
2
+ pandas
3
+ plotly
4
+ matplotlib
5
+ seaborn
6
+ scikit-learn
7
+ requests
8
+ openpyxl