import pandas as pd import streamlit as st import plotly.express as px import boto3 import plotly.graph_objects as go import itertools from sklearn.cluster import KMeans, DBSCAN @st.cache_data def compute_kmeans(df, n_clusters): coords = df[["Lat", "Lon"]] kmeans = KMeans(n_clusters=n_clusters, random_state=42) df = df.copy() df["clusterKmeans"] = kmeans.fit_predict(coords) return df st.set_page_config(page_title="UBER – Analyse des clusters", layout="wide") st.markdown(""" """, unsafe_allow_html=True) st.title("🚕 Bonjour et bienvenue sur le dashboard du projet Clustering d'UBER ! 🚕") st.subheader( "Ce projet vise à vérifier la concordance entre l'implantation des taxis UBER et la demande des clients. " \ "Les données utilisées datent d'Avril 2014 et proviennent de la ville de New York.") tab1, tab2, tab3 = st.tabs(["📊⏱ Implantation des taxis et besoins", "🫧🫧🫧 Analyse par jour de la semaine", " ✍️ Constat et conclusions"]) # --- Import des datasets taxi = pd.read_csv("https://uber-cdsd-joel.s3.eu-west-3.amazonaws.com/uber-trip-data/taxi-zone-lookup.csv") apr_14 = pd.read_csv("https://uber-cdsd-joel.s3.eu-west-3.amazonaws.com/uber-trip-data/apr_14_clean.csv") # --- Préparer la densité des stations taxis def compute_taxi_density(taxi_df): density = taxi_df['Borough'].value_counts() df = density.reset_index() df.columns = ['Borough', 'Count'] df = df[df['Borough'] != "Unknown"] # Centres approximatifs borough_centers = { 'Manhattan': [40.7831, -73.9712], 'Brooklyn': [40.6782, -73.9442], 'Queens': [40.7282, -73.7949], 'Bronx': [40.8448, -73.8648], 'Staten Island': [40.5795, -74.1502], 'EWR': [40.6895, -74.1745] } df['Latitude'] = df['Borough'].map(lambda x: borough_centers.get(x, [40.7128, -74.0060])[0]) df['Longitude'] = df['Borough'].map(lambda x: borough_centers.get(x, [40.7128, -74.0060])[1]) return df df_density = compute_taxi_density(taxi) with tab1: st.subheader("1. Regardons dans un premier temps comment Uber a implanté ses différentes bases de taxi.") # créer le graphique Plotly fig = px.scatter_mapbox( df_density, lat='Latitude', lon='Longitude', size='Count', size_max=50, color='Borough', hover_name='Borough', hover_data={'Count':True, 'Latitude':False, 'Longitude':False}, zoom=10, height=800, mapbox_style='carto-positron' ) # afficher dans Streamlit st.plotly_chart(fig) # Conclusions sur l'implantation des taxis st.markdown("

👉 Manhattan et le Queens sont les principaux foyers de taxis. " "Suivent Brooklyn, le Bronx, Staten Island et enfin l'aéroport de Newark

", unsafe_allow_html=True) st.markdown("---") # Visualisation heatmap et conclusion st.header("2. Les pics horaires") st.subheader("- Quels sont les créneaux les plus critiques ?") image_url = "https://uber-cdsd-joel.s3.eu-west-3.amazonaws.com/heatmap+UBER.png" st.markdown( f"""
""", unsafe_allow_html=True ) st.write("") st.markdown("""
👉 Observations sur les horaires des trajets :

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.

""", unsafe_allow_html=True) st.markdown("---") st.subheader("3. Rentrons maintenant dans le détail des clusters d'activité d'Uber sur cette période ! " \ "Pour ce faire, l'algorithme Kmeans sera utilisé comme première approche.") # KMeans : Préparation du dataset pour visu des clusters du mercredi 17h apr_14_wed_17 = apr_14[(apr_14['day_of_week'] == 'Wednesday') & (apr_14['hour'] == 17)].copy() coords = apr_14_wed_17[['Lat', 'Lon']] kmeans = KMeans(n_clusters=8, random_state=42) apr_14_wed_17['clusterKmeans'] = kmeans.fit_predict(coords) fig = px.scatter_mapbox( apr_14_wed_17, lat="Lat", lon="Lon", color="clusterKmeans", height=800, width=600, zoom=10, mapbox_style="carto-positron") st.plotly_chart(fig, use_container_width=True) st.markdown(""" ### Les clusters définis dans Kmeans mettent en avant des zones distinctes : - Le Sud de Manhattan (cluster 3) - Le Centre de Manhattan (cluster 1) - 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) - Les aéroports de La Guardia (cluster 0), JFK (cluster 2), et Newark (cluster 5) Cela parait tout à fait pertinent compte-tenu du jour et de l'heure choisis. En effet, cela met en avant les actifs de Manhattan ainsi que les affluences aux différents aéroports. Les clusters 1, 3, 4 et 7 sont donc très denses et sont les plus importants. """) st.markdown("---") st.subheader(""" 👉 Vérifions maintenant la concordance entre les clusters des courses et les implantations des taxis""" ) st.write("") # Visualisation Clusters Kmeans mercredi 17h avec concordance densité taxis apr_14_wed_17 = apr_14[ (apr_14['day_of_week'] == 'Wednesday') & (apr_14['hour'] == 17) ].copy() coords = apr_14_wed_17[['Lat', 'Lon']] kmeans = KMeans(n_clusters=8, random_state=42) apr_14_wed_17['clusterKmeans'] = kmeans.fit_predict(coords) # Figure Mapbox unique fig = go.Figure() # --- couche clusters (points) fig.add_trace(go.Scattermapbox( lat=apr_14_wed_17['Lat'], lon=apr_14_wed_17['Lon'], mode='markers', marker=dict( size=6, color=apr_14_wed_17['clusterKmeans'], colorscale='Viridis', opacity=0.6 ), name='Clusters (Mercredi 17h)', hoverinfo='text' )) # --- couche densité boroughs fig.add_trace(go.Scattermapbox( lat=df_density['Latitude'], lon=df_density['Longitude'], mode='markers', marker=dict( size=df_density['Count'], sizemode='area', sizeref=2.*max(df_density['Count'])/(50.**2), color='red', opacity=0.7 ), text=df_density['Borough'] + "
Count: " + df_density['Count'].astype(str), name='Densité par Borough' )) # Layout commun fig.update_layout( mapbox=dict( style="carto-positron", center=dict(lat=40.73, lon=-73.94), zoom=10 ), height=900, width=600, margin=dict(l=0, r=0, t=0, b=0) ) st.plotly_chart(fig) st.markdown(""" ### Compte-tenu des données prises en compte avec notamment un centrage sur les coordonnées de New-York : - L'implantation dans Manhattan est bien évidemment pertinente, la localisation exacte n'étant pas précisée. - Le Bronx dispose d'un flotte importante qui reste à proximité de Manhattan. - L'implantation dans le Queens paraît discutable, mais nous ne disposons pas de la totalité de l'information des courses potentielles du secteur. - L'aéroport de Newark paraît sous-dimensionné - Staten Island ne paraît pas justifié, mais encore une fois les données ne concernent que New-York. """) st.write("---") st.subheader("👉 Procédons maintenant avec un second algorithme de clustering : DBScan. " \ "Il a pour propriété notamment de traiter les outliers et de proposer une vision plus concise des clusters.") # TEST DE DB SCAN # Sélection des coordonnées X = apr_14_wed_17[['Lat', 'Lon']] # DBSCAN clustering avec données définies dans notebook db = DBSCAN( eps=0.008, min_samples=10, metric="manhattan" ) db.fit(X) # Ajout des labels au DataFrame apr_14_wed_17["clusterDB"] = db.labels_ # Filtrer les outliers (-1) df_clusters = apr_14_wed_17[apr_14_wed_17["clusterDB"] != -1] # Carte fig = px.scatter_mapbox( df_clusters, lat="Lat", lon="Lon", color="clusterDB", zoom=10, height=1000, mapbox_style="carto-positron" ) st.plotly_chart(fig, use_container_width=True) st.markdown(""" ### Comme attendu, DBScan nous donne des clusters moins étendus, plus recentrés : - Un total de 11 clusters. - Manhattan n'est plus divisé et représente un cluster unique. - Les 3 aéroports sont bien identifiés. - Des clusters sont répartis autour de Manhattan (clusters 1, 2, 3, 5, 11 et 9). """) st.markdown("---") st.subheader("👉 Vérifions enfin la concordance entre les clusters DBSCAN et l'implantation des taxis") st.markdown("") # Superposition des deux graphs de densité et clusters DBSCAN # Attribuer une couleur à chaque cluster colors = ['orange', 'blue', 'green', 'orange', 'purple', 'brown', 'pink', 'cyan', 'yellow', 'black'] cluster_colors = {c: colors[i % len(colors)] for i, c in enumerate(sorted(df_clusters['clusterDB'].unique()))} # Figure fig = go.Figure() # Couche clusters for c in sorted(df_clusters['clusterDB'].unique()): dfc = df_clusters[df_clusters['clusterDB'] == c] fig.add_trace(go.Scattermapbox( lat=dfc['Lat'], lon=dfc['Lon'], mode='markers', marker=dict( size=6, color=cluster_colors[c], opacity=0.6 ), name=f'Cluster {c}', hoverinfo='text' )) # Couche densité boroughs for i, row in df_density.iterrows(): fig.add_trace(go.Scattermapbox( lat=[row['Latitude']], lon=[row['Longitude']], mode='markers', marker=dict( size=row['Count'], color='red', opacity=0.7 ), name=row['Borough'], hoverinfo='text', text=f"{row['Borough']}
Count: {row['Count']}" )) # Layout fig.update_layout( mapbox=dict( style="carto-positron", center=dict(lat=40.73, lon=-73.94), zoom=10 ), height=900, margin=dict(l=0, r=0, t=0, b=0) ) # Streamlit st.plotly_chart(fig, use_container_width=True) st.write("---") st.subheader(""" 👉 Analyse des clusters : - On constate une bonne concordance entre les zones d'implantation des taxis et les clusters, du moins dans le centre de New York. - Les taxis du Bronx sont justifiés par la proximité de Manhattan. - L'étude devant isoler cette zone, une concentration de taxis paraît plus éloigné du centre. Cela est certainement dû à une activité importante dans une zone non concernée par l'étude. """) st.write("---") st.subheader(""" 👉 Après cette étude comparative entre KMeans et DBScan, quel est l'algorithme le plus adapté à ce projet ? 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. 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. """) with tab2: st.header("L'analyse des clusters") st.subheader("Évolution des clusters Uber selon les jours de la semaine et rapprochement avec l'implantation des taxis ") clusters_by_day = { "Monday": 7, "Tuesday": 10, "Wednesday": 7, "Thursday": 6, "Friday": 7, "Saturday": 7, "Sunday": 7, } days_translation = { "Monday": "Lundi", "Tuesday": "Mardi", "Wednesday": "Mercredi", "Thursday": "Jeudi", "Friday": "Vendredi", "Saturday": "Samedi", "Sunday": "Dimanche", } selected_day_fr = st.selectbox( "Choisissez le jour souhaité :", list(days_translation.values()) ) fr_to_en = {v: k for k, v in days_translation.items()} selected_day_en = fr_to_en[selected_day_fr] filtered_df = apr_14[apr_14["day_of_week"] == selected_day_en].copy() filtered_df = compute_kmeans( filtered_df, clusters_by_day[selected_day_en] ) # --- Visualisation clusters + densité taxi --- fig = go.Figure() fig.add_trace(go.Scattermapbox( lat=filtered_df["Lat"], lon=filtered_df["Lon"], mode="markers", marker=dict( size=6, color=filtered_df["clusterKmeans"], colorscale="Viridis", opacity=0.6 ), name=f"Clusters ({selected_day_fr})" )) fig.add_trace(go.Scattermapbox( lat=df_density["Latitude"], lon=df_density["Longitude"], mode="markers", marker=dict( size=df_density["Count"], sizemode="area", sizeref=2.*max(df_density["Count"])/(50.**2), color="red", opacity=0.7 ), text=df_density["Borough"] + "
Stations: " + df_density["Count"].astype(str), hoverinfo="text", name="Densité stations taxi" )) fig.update_layout( mapbox=dict( style="carto-positron", center=dict(lat=40.73, lon=-73.94), zoom=10 ), height=900, title=f"Clusters Uber ({selected_day_fr}) & densité des stations taxi", margin=dict(l=0, r=0, t=40, b=0) ) st.plotly_chart(fig, use_container_width=True) # REPRENDRE ICI !!!!!! with tab3: st.subheader("Premier constat sur l'implantation des taxis") st.markdown(""" La répartition des bases de taxis UBER semble globalement cohérente avec la demande observée : - Les zones de Manhattan et Brooklyn sont très bien couvertes. - Les zones du Queens et du Bronx paraissent surdimensionnées, mais cela peut s'expliquer par la proximité de Manhattan. - Le dimensionnement paraît surévalué dans Staten Island et sous-évalué à Newark, mais le recentrager sur New-York peut expliquer cela. """) st.subheader("Second constat sur les pics horaires") st.markdown(""" Les pics d'activité sont sur les créneaux suivants : - Une activité certaine les matinées de la semaine entre 6h et 9h - Les après-midi de 14h à 21h pour les mardi, mercredi, jeudi, vendredi - Un pic particulièrement intense le mercredi après-midi - Le vendredi soir jusqu'au samedi 1h du matin - Le samedi après-midi jusqu'au dimanche 1h du matin également 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). """) st.markdown("---") st.subheader("Conclusion et recommandations") 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. Cependant, quelques ajustements pourraient être envisagés : - 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. - Ajuster la couverture à Newark et Staten Island en fonction des besoins réels. - 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. """) 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. """)