minor changes
This commit is contained in:
parent
b7e510b727
commit
1ee81b826d
6 changed files with 160635 additions and 0 deletions
1
Paris_trees_analysis.ipynb
Normal file
1
Paris_trees_analysis.ipynb
Normal file
File diff suppressed because one or more lines are too long
221
README.MD
Normal file
221
README.MD
Normal file
|
|
@ -0,0 +1,221 @@
|
||||||
|
# Projet d'Analyse des Arbres Parisiens
|
||||||
|
|
||||||
|
## 📋 Contexte du projet
|
||||||
|
|
||||||
|
Ce projet a été réalisé dans le cadre d'une mission d'analyse de données pour la **Ville de Paris**. L'objectif est d'explorer et d'analyser le patrimoine arboré parisien à partir d'un jeu de données public recensant les arbres présents sur le territoire de la capitale.
|
||||||
|
|
||||||
|
Le projet met l'accent sur les fondamentaux de la data science : le nettoyage des données, la gestion des anomalies, l'analyse exploratoire et la visualisation statistique pour préparer le dataset à des analyses ultérieures ou à des applications métier (gestion urbaine, biodiversité, aménagement).
|
||||||
|
|
||||||
|
## 🎯 Objectifs
|
||||||
|
|
||||||
|
Les objectifs principaux du projet sont :
|
||||||
|
|
||||||
|
1. **Explorer le dataset** : Comprendre la structure, les types de variables et les caractéristiques des arbres parisiens
|
||||||
|
2. **Nettoyer les données** : Identifier et traiter les valeurs manquantes, aberrantes et incohérentes
|
||||||
|
3. **Analyser les distributions** : Réaliser des analyses univariées et bivariées pour comprendre les variables
|
||||||
|
4. **Visualiser les données** : Créer des graphiques et des cartes interactives pour faciliter la compréhension
|
||||||
|
5. **Préparer les données** : Fournir un dataset propre et exploitable pour des analyses futures
|
||||||
|
|
||||||
|
## 📊 Données
|
||||||
|
|
||||||
|
Les données proviennent de l'**Open Data de la Ville de Paris** et contiennent des informations détaillées sur les arbres :
|
||||||
|
- Localisation géographique (coordonnées GPS, arrondissement, adresse)
|
||||||
|
- Caractéristiques botaniques (genre, espèce, variété)
|
||||||
|
- Dimensions (hauteur, circonférence)
|
||||||
|
- Statut (arbre remarquable, domanialité)
|
||||||
|
- Date de plantation
|
||||||
|
|
||||||
|
Le dataset est inclus dans le projet sous le nom `p2-trees-fr.csv`.
|
||||||
|
|
||||||
|
**Source** : [Open Data Ville de Paris - Les arbres](https://opendata.paris.fr/explore/dataset/les-arbres/)
|
||||||
|
|
||||||
|
## 📁 Structure du projet
|
||||||
|
|
||||||
|
```
|
||||||
|
├── Paris_trees_analysis.ipynb
|
||||||
|
├── soutenance_projet2.pptx
|
||||||
|
├── data_analysis_functions.py
|
||||||
|
├── map_arrondissement.html
|
||||||
|
├── map_lieu.html
|
||||||
|
└── p2-trees-fr.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1. `Paris_trees_analysis.ipynb`
|
||||||
|
|
||||||
|
**Notebook Principal d'Analyse des Arbres Parisiens**
|
||||||
|
|
||||||
|
Ce notebook constitue le cœur du projet et couvre l'ensemble du processus d'analyse exploratoire :
|
||||||
|
|
||||||
|
**Chargement et aperçu des données** :
|
||||||
|
- Import du fichier CSV contenant les données des arbres
|
||||||
|
- Affichage des premières lignes pour une compréhension rapide
|
||||||
|
- Inspection de la structure du DataFrame (types, dimensions)
|
||||||
|
|
||||||
|
**Analyse exploratoire initiale** :
|
||||||
|
- Utilisation des méthodes pandas (`info()`, `head()`, `describe()`, `value_counts()`)
|
||||||
|
- Identification de la taille du dataset
|
||||||
|
- Analyse des types de colonnes et des valeurs manquantes
|
||||||
|
- Statistiques descriptives pour les variables numériques
|
||||||
|
|
||||||
|
**Identification et nettoyage des anomalies** :
|
||||||
|
- **Valeurs aberrantes** :
|
||||||
|
- Détection pour la circonférence et la hauteur des arbres
|
||||||
|
- Utilisation de méthodes statistiques (IQR, z-score)
|
||||||
|
- Correction ou remplacement des valeurs extrêmes par `NaN`
|
||||||
|
- **Valeurs manquantes** :
|
||||||
|
- Analyse approfondie des colonnes avec NaN
|
||||||
|
- Imputation intelligente pour certaines variables (`domanialite`, `libelle_francais`)
|
||||||
|
- Utilisation de colonnes corrélées (`genre`, `espece`, `variete`) pour l'imputation
|
||||||
|
|
||||||
|
**Analyse univariée** :
|
||||||
|
- **Variables quantitatives** : distribution de la hauteur, circonférence, année de plantation
|
||||||
|
- **Variables catégorielles** : répartition par genre, espèce, arrondissement, statut remarquable
|
||||||
|
- Visualisations : histogrammes, boxplots, diagrammes circulaires, barplots
|
||||||
|
|
||||||
|
**Analyse bivariée** :
|
||||||
|
- Relations entre le statut `remarquable` et les variables quantitatives (`hauteur_m`, `circonference_cm`)
|
||||||
|
- Relations entre `remarquable` et variables catégorielles (`espece`, `genre`)
|
||||||
|
- Corrélations entre variables numériques
|
||||||
|
- Tests statistiques pour valider les observations
|
||||||
|
|
||||||
|
**Analyse géographique** :
|
||||||
|
- Distribution des arbres par arrondissement
|
||||||
|
- Cartographie interactive des arbres parisiens
|
||||||
|
- Identification des zones à forte densité arborée
|
||||||
|
|
||||||
|
**Export des données nettoyées** :
|
||||||
|
- Sauvegarde du DataFrame préparé pour analyses ultérieures
|
||||||
|
|
||||||
|
### 2. `soutenance_projet2.pptx`
|
||||||
|
|
||||||
|
**Présentation de Soutenance**
|
||||||
|
|
||||||
|
Cette présentation synthétise :
|
||||||
|
- Le contexte et les objectifs du projet
|
||||||
|
- La méthodologie d'exploration et de nettoyage des données
|
||||||
|
- Les principales découvertes sur le patrimoine arboré parisien
|
||||||
|
- Les analyses statistiques et visualisations clés
|
||||||
|
- Les insights métier (arbres remarquables, distribution par arrondissement, espèces dominantes)
|
||||||
|
- Les recommandations pour la gestion du patrimoine arboré
|
||||||
|
- Les perspectives d'amélioration et d'exploitation des données
|
||||||
|
|
||||||
|
### 3. `data_analysis_functions.py`
|
||||||
|
|
||||||
|
**Module de Fonctions Utilitaires d'Analyse**
|
||||||
|
|
||||||
|
Ce module Python contient des fonctions réutilisables pour :
|
||||||
|
- Le nettoyage des données (suppression de doublons, gestion des NaN)
|
||||||
|
- L'identification des valeurs aberrantes (méthodes IQR, z-score, percentiles)
|
||||||
|
- Le traitement des outliers (remplacement, suppression, cap)
|
||||||
|
- La transformation des variables (normalisation, encodage)
|
||||||
|
- La génération de rapports de qualité des données
|
||||||
|
- Des fonctions de visualisation standardisées
|
||||||
|
|
||||||
|
Ces fonctions ont été développées pour être réutilisables sur d'autres projets d'analyse de données.
|
||||||
|
|
||||||
|
### 4. `map_arrondissement.html`
|
||||||
|
|
||||||
|
**Carte Interactive des Arbres par Arrondissement**
|
||||||
|
|
||||||
|
Fichier HTML contenant une carte interactive (probablement Folium ou Plotly) qui visualise :
|
||||||
|
- La distribution des arbres par arrondissement de Paris
|
||||||
|
- Des statistiques agrégées par zone géographique
|
||||||
|
- Des visualisations interactives permettant de zoomer et d'explorer les données
|
||||||
|
- Potentiellement des indicateurs sur la densité arborée, les espèces dominantes par arrondissement
|
||||||
|
|
||||||
|
### 5. `map_lieu.html`
|
||||||
|
|
||||||
|
**Carte Interactive des Arbres par Lieu**
|
||||||
|
|
||||||
|
Fichier HTML contenant une carte interactive détaillée qui présente :
|
||||||
|
- La localisation précise des arbres individuels sur le territoire parisien
|
||||||
|
- Des marqueurs interactifs avec informations détaillées (espèce, hauteur, circonférence)
|
||||||
|
- Des filtres pour explorer les arbres par caractéristiques
|
||||||
|
- Des clusters pour améliorer la lisibilité dans les zones à forte densité
|
||||||
|
|
||||||
|
### 6. `p2-trees-fr.csv`
|
||||||
|
|
||||||
|
**Dataset des Arbres Parisiens**
|
||||||
|
|
||||||
|
Fichier CSV contenant les données brutes ou légèrement prétraitées sur les arbres de Paris, incluant :
|
||||||
|
- Identifiants et références
|
||||||
|
- Localisation (coordonnées GPS, arrondissement, adresse)
|
||||||
|
- Caractéristiques botaniques (genre, espèce, variété, nom français)
|
||||||
|
- Dimensions (hauteur en mètres, circonférence en cm)
|
||||||
|
- Statut (remarquable ou non, domanialité)
|
||||||
|
- Date de plantation
|
||||||
|
|
||||||
|
## 🛠️ Technologies utilisées
|
||||||
|
|
||||||
|
- **Python** : Langage principal pour l'analyse de données
|
||||||
|
- pandas, numpy : manipulation et analyse de données
|
||||||
|
- matplotlib, seaborn : visualisation de données
|
||||||
|
- folium ou plotly : cartographie interactive
|
||||||
|
- scipy : tests statistiques
|
||||||
|
- scikit-learn : détection d'outliers
|
||||||
|
- **Jupyter Notebook** : développement et documentation
|
||||||
|
- **HTML/JavaScript** : cartes interactives
|
||||||
|
|
||||||
|
## 📈 Méthodologie
|
||||||
|
|
||||||
|
1. **Exploration initiale** : Compréhension de la structure et du contenu du dataset
|
||||||
|
2. **Nettoyage** : Identification et traitement des valeurs manquantes et aberrantes
|
||||||
|
3. **Analyses univariées** : Étude de la distribution de chaque variable
|
||||||
|
4. **Analyses bivariées** : Exploration des relations entre variables
|
||||||
|
5. **Visualisation géographique** : Cartographie interactive des arbres
|
||||||
|
6. **Documentation** : Présentation des résultats et insights
|
||||||
|
|
||||||
|
## 🎯 Résultats attendus
|
||||||
|
|
||||||
|
- ✅ Compréhension approfondie du patrimoine arboré parisien
|
||||||
|
- ✅ Dataset nettoyé et prêt pour des analyses avancées
|
||||||
|
- ✅ Identification des arbres remarquables et de leurs caractéristiques
|
||||||
|
- ✅ Insights sur la répartition géographique des arbres
|
||||||
|
- ✅ Cartes interactives pour explorer les données visuellement
|
||||||
|
- ✅ Module Python réutilisable pour des analyses similaires
|
||||||
|
|
||||||
|
## 💡 Points clés du projet
|
||||||
|
|
||||||
|
### Qualité des données
|
||||||
|
Comme tout jeu de données réel, le dataset présente des défis :
|
||||||
|
- **Valeurs manquantes** : certaines caractéristiques botaniques incomplètes
|
||||||
|
- **Valeurs aberrantes** : erreurs de saisie dans les mesures (hauteurs et circonférences impossibles)
|
||||||
|
- **Incohérences** : variations dans les formats et les appellations
|
||||||
|
|
||||||
|
### Arbres remarquables
|
||||||
|
Une attention particulière a été portée aux arbres remarquables :
|
||||||
|
- Caractéristiques différenciant les arbres remarquables des autres
|
||||||
|
- Relations entre dimensions (hauteur, circonférence) et statut remarquable
|
||||||
|
- Répartition géographique des arbres remarquables
|
||||||
|
|
||||||
|
### Analyse géographique
|
||||||
|
Le projet intègre une dimension spatiale importante :
|
||||||
|
- Distribution des arbres par arrondissement
|
||||||
|
- Identification des zones à forte densité arborée
|
||||||
|
- Cartes interactives pour une exploration intuitive
|
||||||
|
- Possibilité d'identifier des patterns géographiques
|
||||||
|
|
||||||
|
### Biodiversité urbaine
|
||||||
|
Les analyses révèlent des informations sur la biodiversité :
|
||||||
|
- Espèces d'arbres les plus fréquentes à Paris
|
||||||
|
- Diversité botanique par arrondissement
|
||||||
|
- Âge moyen des arbres (via les dates de plantation)
|
||||||
|
|
||||||
|
## 🎓 Compétences développées
|
||||||
|
|
||||||
|
- Chargement et exploration de données tabulaires
|
||||||
|
- Nettoyage de données réelles avec anomalies
|
||||||
|
- Gestion des valeurs manquantes avec stratégies d'imputation
|
||||||
|
- Détection et traitement des valeurs aberrantes
|
||||||
|
- Analyses statistiques univariées et bivariées
|
||||||
|
- Visualisation de données (graphiques et cartes)
|
||||||
|
- Développement de fonctions Python réutilisables
|
||||||
|
- Communication de résultats d'analyse de données
|
||||||
|
|
||||||
|
## 👤 Auteur
|
||||||
|
|
||||||
|
**Grégoire Mureau**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Ce projet illustre les fondamentaux de l'analyse exploratoire de données : exploration, nettoyage, visualisation et communication d'insights, appliqués à un cas concret d'open data urbain.*
|
||||||
351
data_analysis_functions.py
Normal file
351
data_analysis_functions.py
Normal file
|
|
@ -0,0 +1,351 @@
|
||||||
|
import pandas as pd
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import seaborn as sns
|
||||||
|
import folium
|
||||||
|
from folium.plugins import MarkerCluster
|
||||||
|
import branca
|
||||||
|
from IPython.display import display
|
||||||
|
from scipy.spatial import cKDTree
|
||||||
|
from geopy.distance import geodesic
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
def plot_univariate_variable_analysis(
|
||||||
|
df: pd.DataFrame,
|
||||||
|
variable_name: str,
|
||||||
|
graph_type: str,
|
||||||
|
variable_title_type: str = None,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Plots the distribution of a specified variable in a DataFrame based on the graph type.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
df (pd.DataFrame): The DataFrame containing the data.
|
||||||
|
variable_name (str): The name of the variable to plot.
|
||||||
|
graph_type (str): The type of graph to plot (e.g., 'pie', 'heat_map', 'bars', 'hist', 'bars_log').
|
||||||
|
variable_title_type (str, optional): An optional title type for the variable. Defaults to None.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If the graph_type is not recognized or if latitude/longitude columns are missing.
|
||||||
|
"""
|
||||||
|
if variable_title_type is None:
|
||||||
|
variable_title_type = graph_type
|
||||||
|
|
||||||
|
graph_types = ['pie', 'heat_map', 'bars', 'hist', 'bars_log', 'boxplot']
|
||||||
|
graph_type = graph_type.lower()
|
||||||
|
|
||||||
|
if graph_type not in graph_types:
|
||||||
|
raise ValueError('Error: wrong graph_type')
|
||||||
|
|
||||||
|
if graph_type == 'pie':
|
||||||
|
value_counts = df[variable_name].value_counts() # Get counts without normalization
|
||||||
|
total = value_counts.sum() # Total count for percentage calculation
|
||||||
|
|
||||||
|
# Group classes with less than 2% into "Others" (using raw counts)
|
||||||
|
others_count = value_counts[value_counts / total * 100 < 2]
|
||||||
|
if not others_count.empty:
|
||||||
|
others_label = f"Others ({', '.join(others_count.index)})"
|
||||||
|
value_counts = value_counts[value_counts / total * 100 >= 2]
|
||||||
|
value_counts[others_label] = others_count.sum() # Add the "Others" category
|
||||||
|
|
||||||
|
plt.pie(
|
||||||
|
value_counts,
|
||||||
|
labels=[f"{label} ({count/total*100:.1f}%)" for label, count in zip(value_counts.index, value_counts.values)], # Percentage in labels
|
||||||
|
autopct=lambda p: f'{int(p * total / 100)}' # Count on wedges
|
||||||
|
)
|
||||||
|
plt.title(f"{variable_name} Distribution")
|
||||||
|
|
||||||
|
elif graph_type == 'heat_map':
|
||||||
|
# Check if the DataFrame contains the necessary geographic columns
|
||||||
|
if 'geo_point_2d_a' in df.columns and 'geo_point_2d_b' in df.columns and variable_name in df.columns:
|
||||||
|
# Group by the variable of interest (e.g., 'city', 'region') and count the occurrences in each sector
|
||||||
|
sector_data = df.groupby([variable_name]).agg({
|
||||||
|
'geo_point_2d_a': 'mean',
|
||||||
|
'geo_point_2d_b': 'mean',
|
||||||
|
}).reset_index()
|
||||||
|
|
||||||
|
# Add a "population density" column based on the number of occurrences of the variable in each sector
|
||||||
|
sector_data['counts'] = df.groupby(variable_name).size().values
|
||||||
|
|
||||||
|
# Calculate the occurrence percentages
|
||||||
|
total_counts = sector_data['counts'].sum()
|
||||||
|
sector_data['percentage'] = (sector_data['counts'] / total_counts) * 100
|
||||||
|
|
||||||
|
# Common zoom level used for city-level maps in Folium (and Leaflet)
|
||||||
|
zoom_level = 12
|
||||||
|
# Create a map centered around the average geographic points
|
||||||
|
m = folium.Map(location=[sector_data['geo_point_2d_a'].mean(), sector_data['geo_point_2d_b'].mean()], zoom_start=zoom_level)
|
||||||
|
|
||||||
|
# Normalize population densities for the heat map
|
||||||
|
min_density = sector_data['counts'].min()
|
||||||
|
max_density = sector_data['counts'].max()
|
||||||
|
|
||||||
|
# Create the colormap with branca
|
||||||
|
colormap = branca.colormap.LinearColormap(
|
||||||
|
colors=['yellow', 'orange', 'red'], # Choose a classic heatmap palette (yellow-orange-red)
|
||||||
|
vmin=min_density, vmax=max_density
|
||||||
|
).to_step(10) # Divide the colormap into 10 steps for the legend
|
||||||
|
|
||||||
|
# Extract coordinates of the sectors
|
||||||
|
coords = sector_data[['geo_point_2d_a', 'geo_point_2d_b']].values
|
||||||
|
tree = cKDTree(coords) # Create the k-d tree with the coordinates
|
||||||
|
|
||||||
|
# Initialize the minimum distance to a very large number
|
||||||
|
min_distance = float('inf')
|
||||||
|
closest_pair = None
|
||||||
|
|
||||||
|
# Find the closest pairs
|
||||||
|
for i, point in enumerate(coords):
|
||||||
|
# Find the closest neighbor from point i (excluding the point itself)
|
||||||
|
distances, indices = tree.query(point, k=2) # k=2 to get the closest neighbor and itself
|
||||||
|
|
||||||
|
# The second closest will be the nearest (the first is the point itself)
|
||||||
|
closest_distance = distances[1]
|
||||||
|
closest_index = indices[1]
|
||||||
|
|
||||||
|
# If the found distance is smaller, update the minimum distance and associated points
|
||||||
|
if closest_distance < min_distance:
|
||||||
|
min_distance = closest_distance
|
||||||
|
closest_pair = (point, coords[closest_index])
|
||||||
|
|
||||||
|
# Convert the minimum Euclidean distance to geodesic distance for more accuracy
|
||||||
|
min_distance = geodesic(closest_pair[0], closest_pair[1]).meters
|
||||||
|
|
||||||
|
# Limit the circle size to half the smallest distance between two points
|
||||||
|
max_circle_radius = min_distance / 2
|
||||||
|
|
||||||
|
# Average Earth radius in meters
|
||||||
|
EARTH_RADIUS = 6371000
|
||||||
|
|
||||||
|
# Calculate the size of a pixel in meters (at the given average latitude)
|
||||||
|
lat_rad = np.radians(np.mean(sector_data['geo_point_2d_b']))
|
||||||
|
meters_per_pixel = (2 * np.pi * EARTH_RADIUS) / (256 * (2 ** zoom_level)) * np.cos(lat_rad)
|
||||||
|
|
||||||
|
max_circle_radius_in_pixels = max_circle_radius / meters_per_pixel
|
||||||
|
|
||||||
|
# Add each sector as a colored circle based on population density
|
||||||
|
for _, row in sector_data.iterrows():
|
||||||
|
color = colormap(row['counts']) # Use the colormap to get the color based on density
|
||||||
|
|
||||||
|
# Calculate the size of the marker based on density
|
||||||
|
# size = np.log(row['population_density'] + 1) * 5 # Logarithmic for a reasonable scale
|
||||||
|
# size = max(10, min(size, max_circle_radius)) # Limit the size to max_circle_radius
|
||||||
|
size = max_circle_radius_in_pixels
|
||||||
|
|
||||||
|
# Add a circle to the map
|
||||||
|
folium.CircleMarker(
|
||||||
|
location=[row['geo_point_2d_a'], row['geo_point_2d_b']],
|
||||||
|
radius=size, # Circle size based on density and minimum distance
|
||||||
|
color=color,
|
||||||
|
fill=True,
|
||||||
|
fill_color=color, # Fill with the same color
|
||||||
|
fill_opacity=0.7,
|
||||||
|
popup=f"{variable_name}: {row[variable_name]}<br>Trees: {row['counts']}<br>Percentage: {row['percentage']:.3}%",
|
||||||
|
# Allow the size to adjust based on zoom level
|
||||||
|
zoom_on_click=True
|
||||||
|
).add_to(m)
|
||||||
|
|
||||||
|
# Add the colormap legend to the map
|
||||||
|
colormap.caption = f"Trees per {variable_name}"
|
||||||
|
colormap.add_to(m)
|
||||||
|
|
||||||
|
# Force the legend to the bottom left corner using CSS style
|
||||||
|
m.get_root().html.add_child(folium.Element("""
|
||||||
|
<style>
|
||||||
|
.leaflet-control-colormap {
|
||||||
|
position: absolute !important;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 10px;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
"""))
|
||||||
|
|
||||||
|
# Display the map
|
||||||
|
display(m)
|
||||||
|
m.save(f'./map_{variable_name}.html')
|
||||||
|
return # End the function
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ValueError("The DataFrame must contain 'geo_point_2d_a', 'geo_point_2d_b', and the sector variable to display a map.")
|
||||||
|
|
||||||
|
elif graph_type in ['bars', 'bars_log']:
|
||||||
|
value_counts = df[variable_name].value_counts()
|
||||||
|
|
||||||
|
# Group smaller classes into "others" if there are more than 26
|
||||||
|
if len(value_counts) > 26:
|
||||||
|
top_categories = value_counts[:25]
|
||||||
|
others_count = value_counts[25:].sum()
|
||||||
|
value_counts = pd.concat([top_categories, pd.Series({f'others ({len(value_counts[25:])})': others_count})])
|
||||||
|
|
||||||
|
total = value_counts.sum()
|
||||||
|
plt.figure(figsize=(10, 6))
|
||||||
|
ax = sns.barplot(x=value_counts.index, y=value_counts.values)
|
||||||
|
plt.title(f"{variable_name} Distribution")
|
||||||
|
|
||||||
|
# Add percentages inside the bars
|
||||||
|
for p, percentage in zip(ax.patches, value_counts / total * 100):
|
||||||
|
rotation = 90 if len(value_counts) > 10 else 0
|
||||||
|
ax.annotate(
|
||||||
|
f"{percentage:.1f}%",
|
||||||
|
(p.get_x() + p.get_width() * 0.50, p.get_height() * 0.90),
|
||||||
|
ha='center', va='center', fontsize=10, rotation=rotation, color='black'
|
||||||
|
)
|
||||||
|
|
||||||
|
plt.xlabel(variable_name)
|
||||||
|
plt.ylabel('Count')
|
||||||
|
if graph_type == 'bars_log':
|
||||||
|
plt.yscale('log')
|
||||||
|
plt.xticks(rotation=90 if len(value_counts) > 10 else 0) # Rotate labels if too many
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
elif graph_type == 'hist':
|
||||||
|
plt.figure(figsize=(10, 6))
|
||||||
|
# sns.histplot(df[variable_name][df[variable_name] <= df[variable].quantile(0.99)], kde=True)
|
||||||
|
sns.histplot(df[variable_name], kde=True, color='blue', bins=15)
|
||||||
|
plt.title(f"{variable_name} Distribution")
|
||||||
|
plt.xlabel(variable_name)
|
||||||
|
plt.ylabel('Count')
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
elif graph_type == "boxplot":
|
||||||
|
# sns.boxplot(y=df[variable_name][df[variable_name] <= df[variable_name].quantile(0.99)])
|
||||||
|
sns.boxplot(y=df[variable_name])
|
||||||
|
plt.title(f"{variable_name} Distribution (Boxplot)")
|
||||||
|
plt.ylabel(variable_name)
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def plot_bivariate_variable_analysis(
|
||||||
|
df: pd.DataFrame,
|
||||||
|
variable1_name: str,
|
||||||
|
variable2_name: str,
|
||||||
|
graph_type: str,
|
||||||
|
variable_title_type: str = None,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Plots the distribution of a specified variable in a DataFrame based on the graph type.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
df (pd.DataFrame): The DataFrame containing the data.
|
||||||
|
variable1_name (str): The name of the first variable to plot. (x)
|
||||||
|
variable2_name (str): The name of the second variable to plot. (y)
|
||||||
|
graph_type (str): The type of graph to plot (e.g., 'pie', 'heat_map', 'bars', 'hist', 'bars_log').
|
||||||
|
variable_title_type (str, optional): An optional title type for the variable. Defaults to None.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If the graph_type is not recognized or if latitude/longitude columns are missing.
|
||||||
|
"""
|
||||||
|
if variable_title_type is None:
|
||||||
|
variable_title_type = graph_type
|
||||||
|
|
||||||
|
graph_types = ['boxplot']
|
||||||
|
graph_type = graph_type.lower()
|
||||||
|
|
||||||
|
if graph_type == "boxplot":
|
||||||
|
plt.figure(figsize=(8, 6))
|
||||||
|
sns.boxplot(x=variable1_name, y=variable2_name, data=df)
|
||||||
|
plt.title(f'Boxplot {variable1_name} vs. {variable2_name}')
|
||||||
|
plt.xlabel(variable1_name)
|
||||||
|
plt.ylabel(variable2_name)
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
def compute_bivariate_stats_indicators(
|
||||||
|
df: pd.DataFrame,
|
||||||
|
variable1_name: str,
|
||||||
|
variable2_name: str,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Computes statistic indicator in order to determine possible correspondance between two variables.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
df (pd.DataFrame): The DataFrame containing the data.
|
||||||
|
variable1_name (str): The name of the first variable. (x)
|
||||||
|
variable2_name (str): The name of the second variable. (y)
|
||||||
|
"""
|
||||||
|
import statsmodels.formula.api as sm
|
||||||
|
from statsmodels.stats.anova import anova_lm
|
||||||
|
import pandas as pd
|
||||||
|
from scipy.stats import pearsonr
|
||||||
|
|
||||||
|
|
||||||
|
variables_types = {}
|
||||||
|
variable_names = [variable1_name, variable2_name]
|
||||||
|
for variable_name in variable_names:
|
||||||
|
if type(df[df[variable_name].notna()][variable_name].values[0]) == str:
|
||||||
|
variables_types[variable_name] = 'categorical'
|
||||||
|
else:
|
||||||
|
variables_types[variable_name] = 'quantitative'
|
||||||
|
|
||||||
|
if ('quantitative' in variables_types.values()) and ('categorical' in variables_types.values()): # ANOVA analysis
|
||||||
|
print("Both quantitative and categorical variables : ANOVA analysis")
|
||||||
|
# Build the formula string
|
||||||
|
if variables_types[variable1_name] == 'quantitative' and variables_types[variable2_name] == 'categorical':
|
||||||
|
formula_string = f'{variable1_name} ~ C({variable2_name})'
|
||||||
|
elif variables_types[variable1_name] == 'categorical' and variables_types[variable2_name] == 'quantitative':
|
||||||
|
formula_string = f'{variable2_name} ~ C({variable1_name})'
|
||||||
|
|
||||||
|
# Fit the ANOVA model
|
||||||
|
model = sm.ols(formula_string, data=df).fit()
|
||||||
|
anova_table = anova_lm(model, typ=2)
|
||||||
|
|
||||||
|
# Extract eta_squared
|
||||||
|
eta_squared = anova_table['sum_sq'][0] / sum(anova_table['sum_sq'])
|
||||||
|
|
||||||
|
result = eta_squared, anova_table
|
||||||
|
|
||||||
|
elif 'quantitative' in variables_types.values(): # Pearson correlation analysis
|
||||||
|
print("Both quantitative variables : Pearson correlation analysis")
|
||||||
|
|
||||||
|
# Remove rows with NaN values in either 'remarquable', 'circonference_cm', or 'hauteur_m'
|
||||||
|
data_for_correlation = df.dropna(subset=[variable1_name, variable2_name])
|
||||||
|
|
||||||
|
# Calculate Pearson correlation coefficient between 'remarquable' and 'circonference_cm'
|
||||||
|
correlation_circonference, p_value_circonference = pearsonr(
|
||||||
|
data_for_correlation[variable1_name], data_for_correlation[variable2_name]
|
||||||
|
)
|
||||||
|
|
||||||
|
result = correlation_circonference, p_value_circonference
|
||||||
|
|
||||||
|
|
||||||
|
else: # 2 categorical variables, khi2 to add
|
||||||
|
pass
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def compute_distances(df, tree_index):
|
||||||
|
"""
|
||||||
|
Computes distances between a specific tree and all other trees in the DataFrame.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
df: DataFrame containing tree data with 'geo_point_2d_a' (longitude) and 'geo_point_2d_b' (latitude) columns.
|
||||||
|
tree_index: Index of the specific tree in the DataFrame.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A pandas Series containing distances to all other trees from the specified tree.
|
||||||
|
Returns None if the tree_index is out of range or if necessary columns are missing.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Safety checks
|
||||||
|
if not all(col in df.columns for col in ['geo_point_2d_a', 'geo_point_2d_b']):
|
||||||
|
print("Error: 'geo_point_2d_a' or 'geo_point_2d_b' columns not found in DataFrame.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if tree_index not in df.index:
|
||||||
|
print(f"Error: tree_index {tree_index} is out of range.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Get coordinates of the chosen tree
|
||||||
|
tree_coords = (df.loc[tree_index, 'geo_point_2d_a'], df.loc[tree_index, 'geo_point_2d_b'])
|
||||||
|
|
||||||
|
# Calculate distances to all other trees
|
||||||
|
distances = []
|
||||||
|
for index, row in df.iterrows():
|
||||||
|
other_tree_coords = (row['geo_point_2d_a'], row['geo_point_2d_b']) # Assuming latitude, longitude order
|
||||||
|
distance = geodesic(tree_coords, other_tree_coords).meters
|
||||||
|
distances.append(distance)
|
||||||
|
|
||||||
|
return pd.Series(distances, index=df.index) # Use original index
|
||||||
727
map_arrondissement.html
Normal file
727
map_arrondissement.html
Normal file
File diff suppressed because one or more lines are too long
159335
map_lieu.html
Normal file
159335
map_lieu.html
Normal file
File diff suppressed because one or more lines are too long
BIN
soutenance_projet2.pptx
Normal file
BIN
soutenance_projet2.pptx
Normal file
Binary file not shown.
Loading…
Add table
Reference in a new issue