data / app.py
Tracy André
updated
65b1dae
raw
history blame
15.5 kB
import os
os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
import gradio as gr
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import warnings
from datasets import load_dataset
import pandas as pd
from huggingface_hub import HfApi
import urllib.parse
warnings.filterwarnings('ignore')
# Configuration Hugging Face
hf_token = os.environ.get("HF_TOKEN")
dataset_id = "HackathonCRA/2024"
# Configuration des graphiques
plt.style.use('default')
sns.set_palette("husl")
class AgricultureAnalyzer:
def __init__(self):
self.df = None
self.risk_analysis = None
def load_data(self):
"""Charge les données du dataset Hugging Face"""
try:
self.df = load_dataset(
dataset_id,
split="train",
token=hf_token # ou use_auth_token=hf_token selon la version
).to_pandas()
# Nettoyage éventuel (ex: suppression NA)
self.df = self.df.dropna(subset=["numparcell", "surfparc", "millesime"])
return f"✅ Données chargées avec succès : {len(self.df)} enregistrements"
except Exception as e:
return f"❌ Erreur lors du chargement du dataset : {str(e)}"
def analyze_data(self):
"""Analyse des données et calcul des risques"""
if self.df is None:
return "Erreur: Aucune donnée chargée"
# Analyse générale
general_stats = {
'total_parcelles': self.df['numparcell'].nunique(),
'total_interventions': len(self.df),
'surface_totale': self.df['surfparc'].sum(),
'surface_moyenne': self.df['surfparc'].mean(),
'periode': f"{self.df['millesime'].min()} - {self.df['millesime'].max()}"
}
# Analyse des herbicides
herbicides_df = self.df[self.df['familleprod'] == 'Herbicides'].copy()
herbicide_stats = {
'nb_interventions_herbicides': len(herbicides_df),
'pourcentage_herbicides': (len(herbicides_df) / len(self.df)) * 100,
'parcelles_traitees': herbicides_df['numparcell'].nunique()
}
# Calcul de l'analyse des risques
self.calculate_risk_analysis()
return general_stats, herbicide_stats
def calculate_risk_analysis(self):
"""Calcule l'analyse des risques par parcelle"""
# Groupement des données par parcelle
risk_analysis = self.df.groupby(['numparcell', 'nomparc', 'libelleusag', 'surfparc']).agg({
'familleprod': lambda x: (x == 'Herbicides').sum(), # Nb traitements herbicides
'libevenem': lambda x: len(x.unique()), # Diversité des événements
'produit': lambda x: len(x.unique()), # Diversité des produits
'quantitetot': 'sum' # Quantité totale
}).round(2)
# Quantités d'herbicides spécifiques
herbicide_quantities = self.df[self.df['familleprod'] == 'Herbicides'].groupby(
['numparcell', 'nomparc', 'libelleusag', 'surfparc'])['quantitetot'].sum().fillna(0)
risk_analysis['Quantite_herbicides'] = herbicide_quantities.reindex(risk_analysis.index, fill_value=0)
risk_analysis.columns = ['Nb_herbicides', 'Diversite_evenements', 'Diversite_produits',
'Quantite_totale', 'Quantite_herbicides']
# Calcul de l'IFT approximatif
risk_analysis['IFT_herbicide_approx'] = (risk_analysis['Quantite_herbicides'] /
risk_analysis.index.get_level_values('surfparc')).round(2)
# Classification du risque
def classify_risk(row):
ift = row['IFT_herbicide_approx']
nb_herb = row['Nb_herbicides']
if ift == 0 and nb_herb == 0:
return 'TRÈS FAIBLE'
elif ift < 1 and nb_herb <= 1:
return 'FAIBLE'
elif ift < 3 and nb_herb <= 3:
return 'MODÉRÉ'
elif ift < 5 and nb_herb <= 5:
return 'ÉLEVÉ'
else:
return 'TRÈS ÉLEVÉ'
risk_analysis['Risque_adventice'] = risk_analysis.apply(classify_risk, axis=1)
# Tri par risque
risk_order = ['TRÈS FAIBLE', 'FAIBLE', 'MODÉRÉ', 'ÉLEVÉ', 'TRÈS ÉLEVÉ']
risk_analysis['Risk_Score'] = risk_analysis['Risque_adventice'].map({r: i for i, r in enumerate(risk_order)})
self.risk_analysis = risk_analysis.sort_values(['Risk_Score', 'IFT_herbicide_approx'])
def get_summary_stats(self):
"""Retourne les statistiques de résumé"""
if self.df is None:
return "Aucune donnée disponible"
stats_text = f"""
## 📊 Statistiques Générales
- **Nombre total de parcelles**: {self.df['numparcell'].nunique()}
- **Nombre d'interventions**: {len(self.df):,}
- **Surface totale**: {self.df['surfparc'].sum():.2f} hectares
- **Surface moyenne par parcelle**: {self.df['surfparc'].mean():.2f} hectares
- **Période**: {self.df['millesime'].min()} - {self.df['millesime'].max()}
## 🧪 Analyse Herbicides
"""
herbicides_df = self.df[self.df['familleprod'] == 'Herbicides']
if len(herbicides_df) > 0:
stats_text += f"""
- **Interventions herbicides**: {len(herbicides_df)} ({(len(herbicides_df)/len(self.df)*100):.1f}%)
- **Parcelles traitées**: {herbicides_df['numparcell'].nunique()}
- **Produits herbicides différents**: {herbicides_df['produit'].nunique()}
"""
if self.risk_analysis is not None:
risk_distribution = self.risk_analysis['Risque_adventice'].value_counts()
stats_text += f"""
## 🎯 Répartition des Risques Adventices
"""
for risk_level in ['TRÈS FAIBLE', 'FAIBLE', 'MODÉRÉ', 'ÉLEVÉ', 'TRÈS ÉLEVÉ']:
if risk_level in risk_distribution:
count = risk_distribution[risk_level]
pct = (count / len(self.risk_analysis)) * 100
stats_text += f"- **{risk_level}**: {count} parcelles ({pct:.1f}%)\n"
return stats_text
def get_low_risk_recommendations(self):
"""Retourne les recommandations pour les parcelles à faible risque"""
if self.risk_analysis is None:
return "Analyse des risques non disponible"
low_risk = self.risk_analysis[
self.risk_analysis['Risque_adventice'].isin(['TRÈS FAIBLE', 'FAIBLE'])
].head(10)
recommendations = "## 🌾 TOP 10 - Parcelles Recommandées pour Cultures Sensibles (Pois, Haricot)\n\n"
for idx, row in low_risk.iterrows():
parcelle, nom, culture, surface = idx
recommendations += f"""
**Parcelle {parcelle}** ({nom})
- Culture actuelle: {culture}
- Surface: {surface:.2f} ha
- Niveau de risque: {row['Risque_adventice']}
- IFT herbicide: {row['IFT_herbicide_approx']:.2f}
- Nombre d'herbicides: {row['Nb_herbicides']}
---
"""
return recommendations
def create_risk_visualization(self):
"""Crée la visualisation des risques"""
if self.risk_analysis is None:
return None
risk_df = self.risk_analysis.reset_index()
fig = px.scatter(risk_df,
x='surfparc',
y='IFT_herbicide_approx',
color='Risque_adventice',
size='Nb_herbicides',
hover_data=['nomparc', 'libelleusag'],
color_discrete_map={
'TRÈS FAIBLE': 'green',
'FAIBLE': 'lightgreen',
'MODÉRÉ': 'orange',
'ÉLEVÉ': 'red',
'TRÈS ÉLEVÉ': 'darkred'
},
title="🎯 Analyse du Risque Adventice par Parcelle",
labels={
'surfparc': 'Surface de la parcelle (ha)',
'IFT_herbicide_approx': 'IFT Herbicide (approximatif)',
'Risque_adventice': 'Niveau de risque'
})
fig.update_layout(width=800, height=600, title_font_size=16)
return fig
def create_culture_analysis(self):
"""Analyse par type de culture"""
if self.df is None:
return None
culture_counts = self.df['libelleusag'].value_counts()
fig = px.pie(values=culture_counts.values,
names=culture_counts.index,
title="🌱 Répartition des Cultures")
fig.update_layout(width=700, height=500)
return fig
def create_risk_distribution(self):
"""Distribution des niveaux de risque"""
if self.risk_analysis is None:
return None
risk_counts = self.risk_analysis['Risque_adventice'].value_counts()
fig = px.bar(x=risk_counts.index,
y=risk_counts.values,
color=risk_counts.index,
color_discrete_map={
'TRÈS FAIBLE': 'green',
'FAIBLE': 'lightgreen',
'MODÉRÉ': 'orange',
'ÉLEVÉ': 'red',
'TRÈS ÉLEVÉ': 'darkred'
},
title="📊 Distribution des Niveaux de Risque Adventice",
labels={'x': 'Niveau de risque', 'y': 'Nombre de parcelles'})
fig.update_layout(width=700, height=500, showlegend=False)
return fig
# Initialisation de l'analyseur
analyzer = AgricultureAnalyzer()
analyzer.load_data()
# Interface Gradio
def create_interface():
with gr.Blocks(title="🌾 Analyse Adventices Agricoles CRA", theme=gr.themes.Soft()) as demo:
gr.Markdown("""
# 🌾 Analyse des Adventices Agricoles - CRA Bretagne
**Objectif**: Anticiper et réduire la pression des adventices dans les parcelles agricoles bretonnes
Cette application analyse les données historiques pour identifier les parcelles les plus adaptées
à la culture de plantes sensibles comme le pois ou le haricot.
""")
with gr.Tabs():
with gr.TabItem("📊 Vue d'ensemble"):
gr.Markdown("## Statistiques générales des données agricoles")
stats_output = gr.Markdown(analyzer.get_summary_stats())
with gr.Row():
culture_plot = gr.Plot(analyzer.create_culture_analysis())
risk_dist_plot = gr.Plot(analyzer.create_risk_distribution())
with gr.TabItem("🎯 Analyse des Risques"):
gr.Markdown("## Cartographie des risques adventices par parcelle")
risk_plot = gr.Plot(analyzer.create_risk_visualization())
gr.Markdown("""
**Interprétation du graphique**:
- **Axe X**: Surface de la parcelle (hectares)
- **Axe Y**: IFT Herbicide approximatif
- **Couleur**: Niveau de risque adventice
- **Taille**: Nombre d'herbicides utilisés
Les parcelles vertes (risque faible) sont idéales pour les cultures sensibles.
""")
with gr.TabItem("🌾 Recommandations"):
gr.Markdown(analyzer.get_low_risk_recommendations())
gr.Markdown("""
## 💡 Conseils pour la gestion des adventices
### Parcelles à Très Faible Risque (Vertes)
- ✅ **Idéales pour pois et haricot**
- ✅ Historique d'usage herbicide minimal
- ✅ Pression adventice faible attendue
### Parcelles à Faible Risque (Vert clair)
- ⚠️ Surveillance légère recommandée
- ✅ Conviennent aux cultures sensibles avec précautions
### Parcelles à Risque Modéré/Élevé (Orange/Rouge)
- ❌ Éviter pour cultures sensibles
- 🔍 Rotation nécessaire avant implantation
- 📈 Surveillance renforcée des adventices
### Stratégies alternatives
- **Rotation longue**: 3-4 ans avant cultures sensibles
- **Cultures intermédiaires**: CIPAN pour réduire la pression
- **Techniques mécaniques**: Hersage, binage
- **Biostimulants**: Renforcement naturel des cultures
""")
with gr.TabItem("ℹ️ À propos"):
gr.Markdown("""
## 🎯 Méthodologie
Cette analyse se base sur :
### Calcul de l'IFT (Indice de Fréquence de Traitement)
- **IFT ≈ Quantité appliquée / Surface de parcelle**
- Indicateur de l'intensité des traitements herbicides
### Classification des risques
- **TRÈS FAIBLE**: IFT = 0, aucun herbicide
- **FAIBLE**: IFT < 1, usage minimal
- **MODÉRÉ**: IFT < 3, usage modéré
- **ÉLEVÉ**: IFT < 5, usage important
- **TRÈS ÉLEVÉ**: IFT ≥ 5, usage intensif
### Données analysées
- **Source**: Station Expérimentale de Kerguéhennec
- **Période**: Campagne 2025
- **Variables**: Interventions, produits, quantités, surfaces
---
**Développé pour le Hackathon CRA Bretagne** 🏆
*Application d'aide à la décision pour une agriculture durable*
""")
# Bouton de rafraîchissement
refresh_btn = gr.Button("🔄 Actualiser les données", variant="secondary")
def refresh_data():
analyzer.load_data()
return (
analyzer.get_summary_stats(),
analyzer.create_culture_analysis(),
analyzer.create_risk_distribution(),
analyzer.create_risk_visualization(),
analyzer.get_low_risk_recommendations()
)
refresh_btn.click(
refresh_data,
outputs=[stats_output, culture_plot, risk_dist_plot, risk_plot]
)
return demo
# Lancement de l'application
if __name__ == "__main__":
demo = create_interface()
# Configuration pour Hugging Face Spaces
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False # Pas besoin de share sur HF Spaces
)