Spaces:
Sleeping
Sleeping
import streamlit as st | |
import pandas as pd | |
import numpy as np | |
import plotly.express as px | |
import plotly.graph_objects as go | |
from datetime import datetime | |
import re | |
# Configuration de la page | |
st.set_page_config( | |
page_title="Analyse des tendances YouTube 2025", | |
page_icon="📊", | |
layout="wide" | |
) | |
# Fonction pour charger les données | |
def load_data(): | |
# Chargement des données | |
df = pd.read_csv('tendances_2025_final.csv') | |
categories = pd.read_csv('category.csv', sep=';') | |
# Conversion des types de données | |
numeric_cols = ['view_count', 'likes', 'dislikes', 'comment_count'] | |
for col in numeric_cols: | |
df[col] = pd.to_numeric(df[col], errors='coerce') | |
# Extraction de la date de trending correctement formatée | |
# Le format semble être YY.DD.MM | |
df['trending_date_formatted'] = pd.to_datetime(df['trending_date'].apply( | |
lambda x: '20' + x.split('.')[0] + '-' + x.split('.')[2] + '-' + x.split('.')[1] | |
), errors='coerce') | |
# Conversion de la date de publication | |
df['publishedAt'] = pd.to_datetime(df['publishedAt'], errors='coerce') | |
# Conversion de la durée en minutes | |
def convert_duration(duration_str): | |
if pd.isna(duration_str): | |
return np.nan | |
parts = duration_str.split(':') | |
if len(parts) == 2: # MM:SS | |
return int(parts[0]) + int(parts[1])/60 | |
elif len(parts) == 3: # HH:MM:SS | |
return int(parts[0])*60 + int(parts[1]) + int(parts[2])/60 | |
return np.nan | |
df['duration_minutes'] = df['duration'].apply(convert_duration) | |
# Ajout des noms de catégories | |
df = df.merge(categories, left_on='categoryId', right_on='ID', how='left') | |
# Calcul des métriques supplémentaires | |
df['likes_per_view'] = df['likes'] / df['view_count'] | |
df['comments_per_view'] = df['comment_count'] / df['view_count'] | |
return df | |
# Fonction pour créer un histogramme | |
def create_histogram(df, column, title, color='#1f77b4', nbins=20): | |
fig = px.histogram(df, x=column, nbins=nbins, title=title) | |
fig.update_layout(bargap=0.1) | |
return fig | |
# Fonction pour créer un graphique en barres | |
def create_bar_chart(df, x_col, y_col, title, color='#1f77b4'): | |
fig = px.bar(df, x=x_col, y=y_col, title=title, color_discrete_sequence=[color]) | |
return fig | |
# Fonction pour créer un nuage de points | |
def create_scatter_plot(df, x_col, y_col, size_col, hover_data, title): | |
fig = px.scatter(df, x=x_col, y=y_col, size=size_col, | |
hover_data=hover_data, title=title) | |
return fig | |
# Fonction pour créer une carte de chaleur | |
def create_heatmap(df, categories, metrics): | |
# Création d'un DataFrame pivot avec les moyennes par catégorie | |
heatmap_data = pd.DataFrame() | |
for metric in metrics: | |
for cat in categories: | |
cat_data = df[df['Category name'] == cat] | |
if not cat_data.empty: | |
heatmap_data.loc[cat, metric] = cat_data[metric].mean() | |
# Normalisation des données pour la carte de chaleur | |
normalized_data = (heatmap_data - heatmap_data.min()) / (heatmap_data.max() - heatmap_data.min()) | |
# Création de la carte de chaleur | |
fig = go.Figure(data=go.Heatmap( | |
z=normalized_data.values, | |
x=normalized_data.columns, | |
y=normalized_data.index, | |
colorscale='Viridis', | |
hovertemplate='Catégorie: %{y}<br>Métrique: %{x}<br>Valeur: %{z}<extra></extra>' | |
)) | |
fig.update_layout( | |
title='Carte de chaleur des métriques par catégorie', | |
xaxis_title='Métriques', | |
yaxis_title='Catégories' | |
) | |
return fig | |
# Chargement des données | |
df = load_data() | |
# Sidebar pour les filtres | |
st.sidebar.title("Filtres") | |
# Filtre par catégorie | |
categories = sorted(df['Category name'].dropna().unique()) | |
selected_categories = st.sidebar.multiselect( | |
"Sélectionner les catégories", | |
categories, | |
default=categories[:3] # Par défaut, sélectionne les 3 premières catégories | |
) | |
# Filtre par date de tendance | |
min_date = df['trending_date_formatted'].min().date() | |
max_date = df['trending_date_formatted'].max().date() | |
date_range = st.sidebar.date_input( | |
"Période de tendance", | |
[min_date, max_date], | |
min_value=min_date, | |
max_value=max_date | |
) | |
# Filtre par chaîne YouTube | |
channels = sorted(df['channelTitle'].dropna().unique()) | |
selected_channels = st.sidebar.multiselect( | |
"Sélectionner les chaînes", | |
channels, | |
default=[] | |
) | |
# Application des filtres | |
filtered_df = df.copy() | |
if selected_categories: | |
filtered_df = filtered_df[filtered_df['Category name'].isin(selected_categories)] | |
if len(date_range) == 2: | |
start_date, end_date = date_range | |
filtered_df = filtered_df[ | |
(filtered_df['trending_date_formatted'].dt.date >= start_date) & | |
(filtered_df['trending_date_formatted'].dt.date <= end_date) | |
] | |
if selected_channels: | |
filtered_df = filtered_df[filtered_df['channelTitle'].isin(selected_channels)] | |
# Titre principal | |
st.title("📊 Analyse des tendances YouTube 2025") | |
# Métriques principales | |
st.header("📈 Indicateurs clés") | |
col1, col2, col3, col4 = st.columns(4) | |
with col1: | |
st.metric("Nombre de vidéos", f"{len(filtered_df):,}") | |
with col2: | |
avg_views = filtered_df['view_count'].mean() | |
st.metric("Vues moyennes", f"{avg_views:,.0f}") | |
with col3: | |
avg_likes_per_view = filtered_df['likes_per_view'].mean() * 100 | |
st.metric("Likes/Vues moyen", f"{avg_likes_per_view:.2f}%") | |
with col4: | |
avg_duration = filtered_df['duration_minutes'].mean() | |
st.metric("Durée moyenne", f"{avg_duration:.1f} min") | |
# Visualisations | |
st.header("🔍 Visualisations") | |
# Distribution des vues | |
col1, col2 = st.columns(2) | |
with col1: | |
views_hist = create_histogram(filtered_df, 'view_count', 'Distribution des vues', color='#FF0000', nbins=30) | |
st.plotly_chart(views_hist, use_container_width=True) | |
with col2: | |
duration_hist = create_histogram(filtered_df, 'duration_minutes', 'Distribution des durées (minutes)', color='#00BFD6', nbins=25) | |
st.plotly_chart(duration_hist, use_container_width=True) | |
# Engagement par catégorie | |
col1, col2 = st.columns(2) | |
with col1: | |
category_engagement = filtered_df.groupby('Category name').agg({ | |
'likes_per_view': 'mean', | |
'comments_per_view': 'mean' | |
}).reset_index() | |
category_engagement['likes_per_view'] = category_engagement['likes_per_view'] * 100 | |
category_engagement['comments_per_view'] = category_engagement['comments_per_view'] * 100 | |
likes_by_category = create_bar_chart( | |
category_engagement, | |
'Category name', | |
'likes_per_view', | |
'Taux de likes par catégorie (%)', | |
color='#FF9900' | |
) | |
st.plotly_chart(likes_by_category, use_container_width=True) | |
with col2: | |
comments_by_category = create_bar_chart( | |
category_engagement, | |
'Category name', | |
'comments_per_view', | |
'Taux de commentaires par catégorie (%)', | |
color='#00CC96' | |
) | |
st.plotly_chart(comments_by_category, use_container_width=True) | |
# Nuage de points des vidéos | |
scatter_plot = create_scatter_plot( | |
filtered_df, | |
'view_count', | |
'likes_per_view', | |
'duration_minutes', | |
['title', 'channelTitle', 'Category name'], | |
'Relation entre vues et taux de likes' | |
) | |
st.plotly_chart(scatter_plot, use_container_width=True) | |
# Carte de chaleur des métriques par catégorie | |
if selected_categories: | |
metrics = ['view_count', 'likes_per_view', 'comments_per_view', 'duration_minutes'] | |
heatmap = create_heatmap(filtered_df, selected_categories, metrics) | |
st.plotly_chart(heatmap, use_container_width=True) | |
# Top vidéos par vues | |
st.header("🏆 Top 10 des vidéos les plus vues") | |
top_videos = filtered_df.nlargest(10, 'view_count')[['title', 'channelTitle', 'Category name', 'view_count', 'likes', 'comment_count', 'duration_minutes']] | |
top_videos = top_videos.rename(columns={ | |
'title': 'Titre', | |
'channelTitle': 'Chaîne', | |
'Category name': 'Catégorie', | |
'view_count': 'Vues', | |
'likes': 'Likes', | |
'comment_count': 'Commentaires', | |
'duration_minutes': 'Durée (min)' | |
}) | |
st.dataframe(top_videos, use_container_width=True) | |
# Téléchargement des données filtrées | |
st.header("📥 Télécharger les données filtrées") | |
csv = filtered_df.to_csv(index=False).encode('utf-8') | |
st.download_button( | |
label="Télécharger en CSV", | |
data=csv, | |
file_name="youtube_trends_filtered.csv", | |
mime="text/csv", | |
) | |
# Informations sur l'application | |
st.sidebar.markdown("---") | |
st.sidebar.info(""" | |
**À propos de cette application** | |
Cette application analyse les tendances YouTube de 2025. | |
Vous pouvez filtrer les données par catégorie, date et chaîne pour explorer les métriques importantes. | |
Données mises à jour le 9 avril 2025. | |
""") |