Spaces:
Sleeping
Sleeping
Upload 3 files
Browse files- app.py +165 -12
- mon_modele.joblib +2 -2
- mon_modele_rf1.joblib +3 -0
app.py
CHANGED
|
@@ -1,15 +1,23 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
import joblib
|
| 4 |
import numpy as np
|
| 5 |
import streamlit as st
|
| 6 |
import requests
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
# -------------------------------------------------------------------------
|
| 9 |
# PARAMÈTRES ET CHARGEMENT DU MODÈLE
|
| 10 |
# -------------------------------------------------------------------------
|
| 11 |
-
API_KEY =
|
| 12 |
-
|
|
|
|
|
|
|
| 13 |
|
| 14 |
# Tarif fixe (0.70 $/km) pour le modèle
|
| 15 |
TARIF_PAR_KM = 0.70
|
|
@@ -89,6 +97,24 @@ def get_distance_and_duration(lat1, lng1, lat2, lng2, api_key):
|
|
| 89 |
return dist_km, dur_min
|
| 90 |
return None, None
|
| 91 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
def get_weather(lat, lng, api_key):
|
| 93 |
"""
|
| 94 |
Récupère la météo actuelle à partir des coordonnées GPS.
|
|
@@ -100,19 +126,104 @@ def get_weather(lat, lng, api_key):
|
|
| 100 |
f"&lon={lng}"
|
| 101 |
f"&appid={api_key}"
|
| 102 |
f"&units=metric"
|
| 103 |
-
f"&lang=
|
| 104 |
)
|
| 105 |
response = requests.get(url)
|
| 106 |
if response.status_code == 200:
|
| 107 |
data = response.json()
|
| 108 |
temp = data["main"]["temp"]
|
| 109 |
description = data["weather"][0]["description"]
|
| 110 |
-
|
|
|
|
| 111 |
else:
|
| 112 |
return None, None
|
| 113 |
|
| 114 |
|
| 115 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
# -------------------------------------------------------------------------
|
| 117 |
# INTERFACE UTILISATEUR : ADRESSES
|
| 118 |
# -------------------------------------------------------------------------
|
|
@@ -127,7 +238,8 @@ if pickup_input:
|
|
| 127 |
pickup_suggestions = get_address_suggestions(pickup_input, API_KEY)
|
| 128 |
|
| 129 |
pickup_options = [s["description"] for s in pickup_suggestions]
|
| 130 |
-
selected_pickup = st.selectbox("Suggestions pour l'adresse de départ :", pickup_options
|
|
|
|
| 131 |
|
| 132 |
st.header("Adresse d'arrivée")
|
| 133 |
destination_input = st.text_input(
|
|
@@ -145,6 +257,11 @@ selected_destination = st.selectbox("Suggestions pour l'adresse d'arrivée :", d
|
|
| 145 |
# -------------------------------------------------------------------------
|
| 146 |
# BOUTON POUR TOUT CALCULER
|
| 147 |
# -------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
if st.button("Prédire le prix"):
|
| 149 |
# Vérifier que l'utilisateur a sélectionné une suggestion
|
| 150 |
if not selected_pickup or not selected_destination:
|
|
@@ -178,6 +295,15 @@ if st.button("Prédire le prix"):
|
|
| 178 |
API_KEY
|
| 179 |
)
|
| 180 |
print(pickup_coords)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
if dist_km is not None and dur_min is not None:
|
| 182 |
# Affichage
|
| 183 |
if dur_min > 60:
|
|
@@ -186,26 +312,53 @@ if st.button("Prédire le prix"):
|
|
| 186 |
#print(heures, minutes)
|
| 187 |
st.write(f"**Durée estimée** : {int(heures)} heures {int(minutes)} minutes")
|
| 188 |
else:
|
| 189 |
-
st.write(f"**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
|
| 191 |
# Appel du modèle ML :
|
| 192 |
# => Le modèle attend [distance, durée, prix_km=0.70]
|
| 193 |
# => On lui passe donc ces 3 features
|
| 194 |
|
| 195 |
-
donnee_entree = np.array([[dist_km, dur_min, TARIF_PAR_KM]])
|
| 196 |
-
prediction_modele = modele.predict(donnee_entree)
|
| 197 |
|
| 198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
else:
|
| 200 |
st.error("Impossible de calculer la distance ou la durée.")
|
| 201 |
else:
|
| 202 |
st.error("Impossible de récupérer les coordonnées GPS.")
|
| 203 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
# Affichage de la meteo
|
| 205 |
meteo = get_weather(pickup_coords[0], pickup_coords[1], API_METEO)
|
| 206 |
if meteo:
|
| 207 |
temperature, descrip = meteo
|
| 208 |
-
st.write(f"Temperature : {round(temperature)} degré celsius, principalement : {descrip} en ce moment")
|
| 209 |
|
| 210 |
|
| 211 |
else:
|
|
|
|
| 1 |
+
from os import write
|
| 2 |
+
|
| 3 |
import joblib
|
| 4 |
import numpy as np
|
| 5 |
import streamlit as st
|
| 6 |
import requests
|
| 7 |
+
import pandas as pd
|
| 8 |
+
|
| 9 |
+
from dotenv import load_dotenv
|
| 10 |
+
import os
|
| 11 |
+
load_dotenv(".api_env")
|
| 12 |
+
|
| 13 |
|
| 14 |
# -------------------------------------------------------------------------
|
| 15 |
# PARAMÈTRES ET CHARGEMENT DU MODÈLE
|
| 16 |
# -------------------------------------------------------------------------
|
| 17 |
+
API_KEY = os.getenv("API_KEY")
|
| 18 |
+
API_METEO = os.getenv("API_METEO")
|
| 19 |
+
modele_linear = joblib.load("mon_modele.joblib")
|
| 20 |
+
modele_rf = joblib.load("mon_modele_rf1.joblib")
|
| 21 |
|
| 22 |
# Tarif fixe (0.70 $/km) pour le modèle
|
| 23 |
TARIF_PAR_KM = 0.70
|
|
|
|
| 97 |
return dist_km, dur_min
|
| 98 |
return None, None
|
| 99 |
|
| 100 |
+
def interpret_weather(description):
|
| 101 |
+
"""
|
| 102 |
+
|
| 103 |
+
:param description: recupere la description qui est de base une chaine de caractere qui
|
| 104 |
+
nous est retournée par la fonction get_weather.
|
| 105 |
+
:return: nous retourne un des mots clés (rain, snow, clear)
|
| 106 |
+
"""
|
| 107 |
+
if description is not None:
|
| 108 |
+
# convertissons toute la description en minuscule
|
| 109 |
+
description_lower = description.lower()
|
| 110 |
+
if "rain" in description_lower :
|
| 111 |
+
return "rain"
|
| 112 |
+
elif "snow" in description_lower :
|
| 113 |
+
return "snow"
|
| 114 |
+
else:
|
| 115 |
+
return "clear"
|
| 116 |
+
|
| 117 |
+
|
| 118 |
def get_weather(lat, lng, api_key):
|
| 119 |
"""
|
| 120 |
Récupère la météo actuelle à partir des coordonnées GPS.
|
|
|
|
| 126 |
f"&lon={lng}"
|
| 127 |
f"&appid={api_key}"
|
| 128 |
f"&units=metric"
|
| 129 |
+
f"&lang=en"
|
| 130 |
)
|
| 131 |
response = requests.get(url)
|
| 132 |
if response.status_code == 200:
|
| 133 |
data = response.json()
|
| 134 |
temp = data["main"]["temp"]
|
| 135 |
description = data["weather"][0]["description"]
|
| 136 |
+
desc = interpret_weather(description)
|
| 137 |
+
return temp, desc
|
| 138 |
else:
|
| 139 |
return None, None
|
| 140 |
|
| 141 |
|
| 142 |
|
| 143 |
+
def encoding_weather(description):
|
| 144 |
+
"""
|
| 145 |
+
|
| 146 |
+
:param description: recupere la description du retour de la fonction get_weather.
|
| 147 |
+
:return: nous retourne 0 ou 1 (pour que le modele comprenne)
|
| 148 |
+
"""
|
| 149 |
+
if description is not None:
|
| 150 |
+
description_lower = description.lower()
|
| 151 |
+
if description_lower == "rain":
|
| 152 |
+
return 1,0
|
| 153 |
+
elif description_lower == "snow":
|
| 154 |
+
return 0,1
|
| 155 |
+
else:
|
| 156 |
+
return 0,0
|
| 157 |
+
else:
|
| 158 |
+
print("Description non recuperée")
|
| 159 |
+
return
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
def interpret_traffic_level(elem):
|
| 164 |
+
"""
|
| 165 |
+
Compare duration_in_traffic vs. duration
|
| 166 |
+
pour en déduire un label (Low, Medium, High).
|
| 167 |
+
"""
|
| 168 |
+
dur_no_traffic = elem["duration"]["value"] # en secondes
|
| 169 |
+
dur_with_traffic = elem["duration_in_traffic"]["value"]
|
| 170 |
+
|
| 171 |
+
ratio = dur_with_traffic / dur_no_traffic
|
| 172 |
+
|
| 173 |
+
# Ex. si la durée augmente de moins de 10% => Low
|
| 174 |
+
# entre 10% et 30% => Medium
|
| 175 |
+
# plus de 30% => High
|
| 176 |
+
if ratio < 1.10:
|
| 177 |
+
return "Low"
|
| 178 |
+
elif ratio < 1.30:
|
| 179 |
+
return "Medium"
|
| 180 |
+
else:
|
| 181 |
+
return "High"
|
| 182 |
+
|
| 183 |
+
def get_traffic_conditions(lat1, lng1, lat2, lng2, api_key):
|
| 184 |
+
"""
|
| 185 |
+
Récupère les conditions de trafic entre deux points via l'API Distance Matrix.
|
| 186 |
+
Retourne le niveau de trafic (Low, Medium, High).
|
| 187 |
+
"""
|
| 188 |
+
origin = f"{lat1},{lng1}"
|
| 189 |
+
destination = f"{lat2},{lng2}"
|
| 190 |
+
url = (
|
| 191 |
+
"https://maps.googleapis.com/maps/api/distancematrix/json"
|
| 192 |
+
f"?origins={origin}"
|
| 193 |
+
f"&destinations={destination}"
|
| 194 |
+
f"&key={api_key}"
|
| 195 |
+
"&mode=driving"
|
| 196 |
+
"&departure_time=now"
|
| 197 |
+
"&traffic_model=best_guess"
|
| 198 |
+
)
|
| 199 |
+
resp = requests.get(url).json()
|
| 200 |
+
if resp["status"] == "OK":
|
| 201 |
+
elem = resp["rows"][0]["elements"][0]
|
| 202 |
+
if elem["status"] == "OK":
|
| 203 |
+
# Extraire duration & duration_in_traffic et créer un label
|
| 204 |
+
return interpret_traffic_level(elem)
|
| 205 |
+
return "Unknown"
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
def encodage_traffic_conditions(level):
|
| 209 |
+
"""
|
| 210 |
+
:param level: c'est le niveau du trafic qui sera (Low,Medium ou High)
|
| 211 |
+
:return: retourner un tuple avec deux valeurs 0 ou 1 pour pouvoir etre considerer par notre modele
|
| 212 |
+
"""
|
| 213 |
+
if level is not None:
|
| 214 |
+
if level == "Low":
|
| 215 |
+
return 1,0
|
| 216 |
+
elif level == "Medium":
|
| 217 |
+
return 0,1
|
| 218 |
+
elif level == "High":
|
| 219 |
+
return 0,0
|
| 220 |
+
else:
|
| 221 |
+
print("Le niveau du traffic est introuvable")
|
| 222 |
+
return
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
|
| 227 |
# -------------------------------------------------------------------------
|
| 228 |
# INTERFACE UTILISATEUR : ADRESSES
|
| 229 |
# -------------------------------------------------------------------------
|
|
|
|
| 238 |
pickup_suggestions = get_address_suggestions(pickup_input, API_KEY)
|
| 239 |
|
| 240 |
pickup_options = [s["description"] for s in pickup_suggestions]
|
| 241 |
+
selected_pickup = st.selectbox("Suggestions pour l'adresse de départ :", pickup_options,
|
| 242 |
+
placeholder= "Pas d'options proposés")
|
| 243 |
|
| 244 |
st.header("Adresse d'arrivée")
|
| 245 |
destination_input = st.text_input(
|
|
|
|
| 257 |
# -------------------------------------------------------------------------
|
| 258 |
# BOUTON POUR TOUT CALCULER
|
| 259 |
# -------------------------------------------------------------------------
|
| 260 |
+
|
| 261 |
+
"""
|
| 262 |
+
Donnees attendus par le modele : ['distance', 'minutes', 'prix_kilometre', 'Traffic_Conditions_Low',
|
| 263 |
+
'Traffic_Conditions_Medium', 'Weather_Rain', 'Weather_Snow']
|
| 264 |
+
"""
|
| 265 |
if st.button("Prédire le prix"):
|
| 266 |
# Vérifier que l'utilisateur a sélectionné une suggestion
|
| 267 |
if not selected_pickup or not selected_destination:
|
|
|
|
| 295 |
API_KEY
|
| 296 |
)
|
| 297 |
print(pickup_coords)
|
| 298 |
+
#
|
| 299 |
+
traffic_level = get_traffic_conditions(pickup_coords[0],pickup_coords[1],
|
| 300 |
+
dest_coords[0],dest_coords[1],API_KEY)
|
| 301 |
+
|
| 302 |
+
temp, desc = get_weather(pickup_coords[0],pickup_coords[1],API_METEO)
|
| 303 |
+
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
|
| 307 |
if dist_km is not None and dur_min is not None:
|
| 308 |
# Affichage
|
| 309 |
if dur_min > 60:
|
|
|
|
| 312 |
#print(heures, minutes)
|
| 313 |
st.write(f"**Durée estimée** : {int(heures)} heures {int(minutes)} minutes")
|
| 314 |
else:
|
| 315 |
+
st.write(f"**Durée estimée** : {int(dur_min)} minutes")
|
| 316 |
+
|
| 317 |
+
st.write(f"**Distance estimée** : {dist_km} km")
|
| 318 |
+
|
| 319 |
+
#
|
| 320 |
+
traffic_low, traffic_medium = encodage_traffic_conditions(traffic_level)
|
| 321 |
+
#
|
| 322 |
+
weather_rain, weather_snow = encoding_weather(desc)
|
| 323 |
+
|
| 324 |
|
| 325 |
# Appel du modèle ML :
|
| 326 |
# => Le modèle attend [distance, durée, prix_km=0.70]
|
| 327 |
# => On lui passe donc ces 3 features
|
| 328 |
|
|
|
|
|
|
|
| 329 |
|
| 330 |
+
|
| 331 |
+
colonnes = ['distance', 'minutes', 'prix_kilometre',
|
| 332 |
+
'Traffic_Conditions_Low', 'Traffic_Conditions_Medium',
|
| 333 |
+
'Weather_Rain', 'Weather_Snow']
|
| 334 |
+
|
| 335 |
+
donnee_entree = pd.DataFrame([[dist_km, dur_min, TARIF_PAR_KM,
|
| 336 |
+
traffic_low, traffic_medium,
|
| 337 |
+
weather_rain, weather_snow]], columns=colonnes)
|
| 338 |
+
if dist_km > 50:
|
| 339 |
+
prediction_modele1 = modele_linear.predict(donnee_entree)
|
| 340 |
+
print("prediction_modele1")
|
| 341 |
+
st.write(f"**Prédiction du modèle** : {prediction_modele1[0]:.2f} $")
|
| 342 |
+
else:
|
| 343 |
+
prediction_modele2 = modele_rf.predict(donnee_entree)
|
| 344 |
+
print("prediction_modele2")
|
| 345 |
+
st.write(f"**Prédiction du modèle** : {prediction_modele2[0]:.2f} $")
|
| 346 |
else:
|
| 347 |
st.error("Impossible de calculer la distance ou la durée.")
|
| 348 |
else:
|
| 349 |
st.error("Impossible de récupérer les coordonnées GPS.")
|
| 350 |
|
| 351 |
+
# Affichage des conditions du traffic
|
| 352 |
+
if traffic_level:
|
| 353 |
+
st.write(f"**Conditions de trafic** : {traffic_level}")
|
| 354 |
+
else:
|
| 355 |
+
st.write('**Conditions de traffic** : Inconnues')
|
| 356 |
+
|
| 357 |
# Affichage de la meteo
|
| 358 |
meteo = get_weather(pickup_coords[0], pickup_coords[1], API_METEO)
|
| 359 |
if meteo:
|
| 360 |
temperature, descrip = meteo
|
| 361 |
+
st.write(f"**Temperature** : {round(temperature)} degré celsius, principalement : {descrip} en ce moment")
|
| 362 |
|
| 363 |
|
| 364 |
else:
|
mon_modele.joblib
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8ea5add9f4812c6ef5bc877a5e7bfa5756a1c809788134c7f661baa40f27fa77
|
| 3 |
+
size 1088
|
mon_modele_rf1.joblib
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bd00d42d2822c53100c18b8b7aaf2aec045998a5922f0bf5e61b2e0d25c0336e
|
| 3 |
+
size 67948401
|