Spaces:
Sleeping
Sleeping
| 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 | |
| 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(""" | |
| <style> | |
| .stTabs [role="tablist"] { | |
| justify-content: center; | |
| gap: 25px; | |
| } | |
| .stTabs [role="tab"] { | |
| font-size: 17px; | |
| font-weight: 600; | |
| padding: 10px 20px; | |
| border-radius: 10px; | |
| background-color: #f8f9fa; | |
| color: #444; | |
| transition: all 0.3s ease-in-out; | |
| } | |
| .stTabs [role="tab"]:hover { | |
| background-color: #e0e0e0; | |
| transform: scale(1.05); | |
| } | |
| .stTabs [role="tab"][aria-selected="true"] { | |
| background-color: #4CAF50; | |
| color: white; | |
| font-weight: 700; | |
| box-shadow: 0px 4px 8px rgba(0,0,0,0.2); | |
| } | |
| </style> | |
| """, 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("<h4>👉 Manhattan et le Queens sont les principaux foyers de taxis. " | |
| "Suivent Brooklyn, le Bronx, Staten Island et enfin l'aéroport de Newark</h4>", 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""" | |
| <div style="display: flex; justify-content: center;"> | |
| <img src="{image_url}" width="1000"> | |
| </div> | |
| """, | |
| unsafe_allow_html=True | |
| ) | |
| st.write("") | |
| st.markdown(""" | |
| <h5>👉 Observations sur les horaires des trajets :</h5> | |
| <ul> | |
| <li><strong>Matinée (6h-9h, jours de semaine) :</strong> activité principalement liée aux trajets professionnels.</li> | |
| <li><strong>Après-midi (14h-22h) :</strong> densité accrue le mercredi entre 15h et 21h.</li> | |
| <li><strong>Vendredi :</strong> activité constante en fin de journée jusqu'au samedi 1h du matin.</li> | |
| <li><strong>Samedi :</strong> concentration entre 15h et dimanche 1h du matin, liée aux loisirs.</li> | |
| </ul> | |
| <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> | |
| """, 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'] + "<br>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']}<br>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"] + "<br>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. | |
| """) |