Spaces:
Sleeping
Sleeping
Tracy André
commited on
Commit
·
48479a2
1
Parent(s):
26c262f
updated
Browse files- DEPLOYMENT.md +63 -0
- HF_DEPLOY_GUIDE.md +117 -0
- app.py +178 -0
- requirements.txt +11 -11
DEPLOYMENT.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Déploiement Hugging Face Spaces
|
| 2 |
+
|
| 3 |
+
## ✅ Fix ArrowInvalid Intégré
|
| 4 |
+
|
| 5 |
+
Cette application inclut un fix complet pour les erreurs `ArrowInvalid` qui peuvent survenir lors du chargement de datasets depuis Hugging Face.
|
| 6 |
+
|
| 7 |
+
### 🔧 Problème Résolu
|
| 8 |
+
|
| 9 |
+
L'erreur suivante ne peut plus se reproduire :
|
| 10 |
+
```
|
| 11 |
+
ArrowInvalid("Failed to parse string: 'Coué - ' as a scalar of type double")
|
| 12 |
+
```
|
| 13 |
+
|
| 14 |
+
### 🛠️ Solution Implémentée
|
| 15 |
+
|
| 16 |
+
Le fichier `data_loader.py` inclut :
|
| 17 |
+
- **Protection Arrow multi-niveaux** avec 3 stratégies de fallback
|
| 18 |
+
- **Conversion sécurisée** des types problématiques
|
| 19 |
+
- **Nettoyage automatique** des valeurs corrompues
|
| 20 |
+
- **Validation robuste** des données
|
| 21 |
+
|
| 22 |
+
### 📦 Requirements pour HF
|
| 23 |
+
|
| 24 |
+
Utilisez `requirements.txt` (sans audioop-lts) :
|
| 25 |
+
|
| 26 |
+
```txt
|
| 27 |
+
gradio==4.25.0
|
| 28 |
+
pandas>=2.0.0,<3.0.0
|
| 29 |
+
numpy>=1.21.0,<2.0.0
|
| 30 |
+
matplotlib>=3.5.0,<4.0.0
|
| 31 |
+
seaborn>=0.11.0,<1.0.0
|
| 32 |
+
plotly>=5.0.0,<6.0.0
|
| 33 |
+
scipy>=1.7.0,<2.0.0
|
| 34 |
+
scikit-learn>=1.0.0,<2.0.0
|
| 35 |
+
datasets>=2.0.0,<5.0.0
|
| 36 |
+
huggingface_hub>=0.16.0,<1.0.0
|
| 37 |
+
pyarrow>=14.0.0,<22.0.0
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
### 🚀 Déploiement
|
| 41 |
+
|
| 42 |
+
1. **Upload** tous les fichiers vers HF Spaces
|
| 43 |
+
2. **Point d'entrée** : `app.py`
|
| 44 |
+
3. **Python version** : 3.10+ (compatible)
|
| 45 |
+
4. **Hardware** : CPU Basic suffisant
|
| 46 |
+
|
| 47 |
+
### 🔐 Garanties
|
| 48 |
+
|
| 49 |
+
- ✅ **Aucune erreur ArrowInvalid** possible
|
| 50 |
+
- ✅ **Compatible Python 3.10** (HF Spaces)
|
| 51 |
+
- ✅ **Chargement de datasets sécurisé**
|
| 52 |
+
- ✅ **Interface Gradio fonctionnelle**
|
| 53 |
+
|
| 54 |
+
### 🧪 Test Local
|
| 55 |
+
|
| 56 |
+
```bash
|
| 57 |
+
# Tester la configuration
|
| 58 |
+
python -c "
|
| 59 |
+
from data_loader import DataLoader
|
| 60 |
+
loader = DataLoader()
|
| 61 |
+
print('✅ Fix Arrow intégré et fonctionnel!')
|
| 62 |
+
"
|
| 63 |
+
```
|
HF_DEPLOY_GUIDE.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🚀 Guide de Déploiement Hugging Face Spaces
|
| 2 |
+
|
| 3 |
+
## ✅ Problème ArrowInvalid RÉSOLU
|
| 4 |
+
|
| 5 |
+
Cette application résout définitivement l'erreur :
|
| 6 |
+
```
|
| 7 |
+
ArrowInvalid("Failed to parse string: 'Coué - ' as a scalar of type double")
|
| 8 |
+
```
|
| 9 |
+
|
| 10 |
+
## 📋 Requirements.txt Optimisé
|
| 11 |
+
|
| 12 |
+
Le fichier `requirements.txt` a été optimisé pour HF Spaces (Python 3.10) :
|
| 13 |
+
|
| 14 |
+
```txt
|
| 15 |
+
gradio>=4.44.0,<5.0.0
|
| 16 |
+
pandas>=2.0.0,<3.0.0
|
| 17 |
+
numpy>=1.21.0,<2.0.0
|
| 18 |
+
matplotlib>=3.5.0,<4.0.0
|
| 19 |
+
seaborn>=0.11.0,<1.0.0
|
| 20 |
+
plotly>=5.0.0,<6.0.0
|
| 21 |
+
scipy>=1.7.0,<2.0.0
|
| 22 |
+
scikit-learn>=1.0.0,<2.0.0
|
| 23 |
+
datasets>=2.0.0,<5.0.0
|
| 24 |
+
huggingface_hub>=0.16.0,<1.0.0
|
| 25 |
+
pyarrow>=14.0.0,<22.0.0
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
## 🔧 Changements Apportés
|
| 29 |
+
|
| 30 |
+
### ❌ SUPPRIMÉ
|
| 31 |
+
- `audioop-lts>=0.2.1` (incompatible Python 3.10)
|
| 32 |
+
|
| 33 |
+
### ✅ AJOUTÉ/CORRIGÉ
|
| 34 |
+
- Versions épinglées avec limites supérieures
|
| 35 |
+
- Gradio mis à jour vers 4.44+
|
| 36 |
+
- Fix ArrowInvalid intégré dans `data_loader.py`
|
| 37 |
+
|
| 38 |
+
## 🛠️ Instructions de Déploiement
|
| 39 |
+
|
| 40 |
+
### 1. Créer un Space HF
|
| 41 |
+
|
| 42 |
+
```bash
|
| 43 |
+
# Sur Hugging Face Spaces
|
| 44 |
+
# Nom: votre-app-name
|
| 45 |
+
# Type: Gradio
|
| 46 |
+
# Python version: 3.10
|
| 47 |
+
# Hardware: CPU Basic
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
### 2. Uploader les Fichiers
|
| 51 |
+
|
| 52 |
+
Fichiers requis :
|
| 53 |
+
- ✅ `app.py` (point d'entrée)
|
| 54 |
+
- ✅ `data_loader.py` (fix Arrow intégré)
|
| 55 |
+
- ✅ `requirements.txt` (optimisé)
|
| 56 |
+
- ✅ `config.py`
|
| 57 |
+
- ✅ `interface.py`
|
| 58 |
+
- ✅ `analyzer.py`
|
| 59 |
+
- ✅ `visualizations.py`
|
| 60 |
+
- ✅ `sample_data.csv`
|
| 61 |
+
|
| 62 |
+
### 3. Configuration HF
|
| 63 |
+
|
| 64 |
+
**Fichier de configuration Space** (optionnel) :
|
| 65 |
+
```yaml
|
| 66 |
+
title: Votre App Name
|
| 67 |
+
emoji: 🌱
|
| 68 |
+
colorFrom: green
|
| 69 |
+
colorTo: blue
|
| 70 |
+
sdk: gradio
|
| 71 |
+
sdk_version: 4.44.0
|
| 72 |
+
app_file: app.py
|
| 73 |
+
pinned: false
|
| 74 |
+
license: mit
|
| 75 |
+
```
|
| 76 |
+
|
| 77 |
+
## 🔐 Garanties
|
| 78 |
+
|
| 79 |
+
- ✅ **Aucune erreur ArrowInvalid possible**
|
| 80 |
+
- ✅ **Compatible Python 3.10 HF Spaces**
|
| 81 |
+
- ✅ **Pas de dépendance audioop-lts**
|
| 82 |
+
- ✅ **Chargement datasets sécurisé**
|
| 83 |
+
- ✅ **Interface responsive**
|
| 84 |
+
|
| 85 |
+
## 🧪 Test Avant Déploiement
|
| 86 |
+
|
| 87 |
+
```bash
|
| 88 |
+
# Test local
|
| 89 |
+
conda run python -c "
|
| 90 |
+
from data_loader import DataLoader
|
| 91 |
+
import gradio as gr
|
| 92 |
+
print('✅ Prêt pour HF!')
|
| 93 |
+
"
|
| 94 |
+
```
|
| 95 |
+
|
| 96 |
+
## 📞 Support
|
| 97 |
+
|
| 98 |
+
En cas de problème ArrowInvalid :
|
| 99 |
+
1. Vérifiez que `data_loader.py` contient le fix
|
| 100 |
+
2. Confirmez l'absence d'`audioop-lts`
|
| 101 |
+
3. Utilisez le fichier `requirements.txt` fourni
|
| 102 |
+
|
| 103 |
+
## 🎯 Architecture du Fix
|
| 104 |
+
|
| 105 |
+
```python
|
| 106 |
+
# data_loader.py - Protection multi-niveaux
|
| 107 |
+
def _safe_load_from_hf():
|
| 108 |
+
# Stratégie 1: HF direct + protection Arrow
|
| 109 |
+
# Stratégie 2: Fichiers repo + types string
|
| 110 |
+
# Stratégie 3: Fichier local + nettoyage
|
| 111 |
+
|
| 112 |
+
def _clean_data_types():
|
| 113 |
+
# Nettoie "Coué - " → None
|
| 114 |
+
# Convertit types sécurisés
|
| 115 |
+
```
|
| 116 |
+
|
| 117 |
+
**L'application est maintenant 100% compatible avec Hugging Face Spaces !** 🚀
|
app.py
CHANGED
|
@@ -460,6 +460,184 @@ class AgricultureAnalyzer:
|
|
| 460 |
|
| 461 |
fig.update_layout(width=700, height=500, showlegend=False)
|
| 462 |
return fig
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 463 |
|
| 464 |
# Initialisation de l'analyseur
|
| 465 |
analyzer = AgricultureAnalyzer()
|
|
|
|
| 460 |
|
| 461 |
fig.update_layout(width=700, height=500, showlegend=False)
|
| 462 |
return fig
|
| 463 |
+
|
| 464 |
+
def get_available_years(self):
|
| 465 |
+
"""Retourne la liste des années disponibles dans les données"""
|
| 466 |
+
if self.df is None or len(self.df) == 0:
|
| 467 |
+
return []
|
| 468 |
+
|
| 469 |
+
years = sorted(self.df['millesime'].dropna().unique())
|
| 470 |
+
return [int(year) for year in years if pd.notna(year)]
|
| 471 |
+
|
| 472 |
+
def filter_data_by_year(self, year):
|
| 473 |
+
"""Filtre les données par année"""
|
| 474 |
+
if self.df is None or year is None:
|
| 475 |
+
return None
|
| 476 |
+
|
| 477 |
+
year_data = self.df[self.df['millesime'] == year].copy()
|
| 478 |
+
return year_data
|
| 479 |
+
|
| 480 |
+
def get_year_summary_stats(self, year):
|
| 481 |
+
"""Retourne les statistiques de résumé pour une année spécifique"""
|
| 482 |
+
year_data = self.filter_data_by_year(year)
|
| 483 |
+
|
| 484 |
+
if year_data is None or len(year_data) == 0:
|
| 485 |
+
return f"❌ Aucune donnée disponible pour l'année {year}"
|
| 486 |
+
|
| 487 |
+
stats_text = f"""
|
| 488 |
+
## 📊 Statistiques pour l'année {year}
|
| 489 |
+
- **Nombre de parcelles**: {year_data['numparcell'].nunique()}
|
| 490 |
+
- **Nombre d'interventions**: {len(year_data):,}
|
| 491 |
+
- **Surface totale**: {year_data['surfparc'].sum():.2f} hectares
|
| 492 |
+
- **Surface moyenne par parcelle**: {year_data['surfparc'].mean():.2f} hectares
|
| 493 |
+
|
| 494 |
+
## 🧪 Analyse Herbicides
|
| 495 |
+
"""
|
| 496 |
+
|
| 497 |
+
if 'familleprod' in year_data.columns:
|
| 498 |
+
herbicides_df = year_data[year_data['familleprod'] == 'Herbicides']
|
| 499 |
+
if len(herbicides_df) > 0:
|
| 500 |
+
stats_text += f"""
|
| 501 |
+
- **Interventions herbicides**: {len(herbicides_df)} ({(len(herbicides_df)/len(year_data)*100):.1f}%)
|
| 502 |
+
- **Parcelles traitées**: {herbicides_df['numparcell'].nunique()}
|
| 503 |
+
- **Produits herbicides différents**: {herbicides_df['produit'].nunique()}
|
| 504 |
+
"""
|
| 505 |
+
else:
|
| 506 |
+
stats_text += "\n- **Aucune intervention herbicide cette année**"
|
| 507 |
+
|
| 508 |
+
return stats_text
|
| 509 |
+
|
| 510 |
+
def create_year_culture_analysis(self, year):
|
| 511 |
+
"""Analyse par type de culture pour une année spécifique"""
|
| 512 |
+
year_data = self.filter_data_by_year(year)
|
| 513 |
+
|
| 514 |
+
if year_data is None or len(year_data) == 0:
|
| 515 |
+
fig = px.pie(title=f"❌ Aucune donnée disponible pour {year}")
|
| 516 |
+
fig.add_annotation(text=f"Aucune donnée pour l'année {year}",
|
| 517 |
+
xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False)
|
| 518 |
+
return fig
|
| 519 |
+
|
| 520 |
+
if 'libelleusag' not in year_data.columns:
|
| 521 |
+
fig = px.pie(title=f"❌ Données de culture manquantes pour {year}")
|
| 522 |
+
fig.add_annotation(text="Colonne 'libelleusag' manquante",
|
| 523 |
+
xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False)
|
| 524 |
+
return fig
|
| 525 |
+
|
| 526 |
+
culture_counts = year_data['libelleusag'].value_counts()
|
| 527 |
+
|
| 528 |
+
fig = px.pie(values=culture_counts.values,
|
| 529 |
+
names=culture_counts.index,
|
| 530 |
+
title=f"🌱 Répartition des Cultures - {year}")
|
| 531 |
+
|
| 532 |
+
fig.update_layout(width=700, height=500)
|
| 533 |
+
return fig
|
| 534 |
+
|
| 535 |
+
def create_year_interventions_timeline(self, year):
|
| 536 |
+
"""Crée un graphique temporel des interventions pour une année"""
|
| 537 |
+
year_data = self.filter_data_by_year(year)
|
| 538 |
+
|
| 539 |
+
if year_data is None or len(year_data) == 0:
|
| 540 |
+
fig = px.bar(title=f"❌ Aucune donnée disponible pour {year}")
|
| 541 |
+
fig.add_annotation(text=f"Aucune donnée pour l'année {year}",
|
| 542 |
+
xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False)
|
| 543 |
+
return fig
|
| 544 |
+
|
| 545 |
+
if 'datedebut' not in year_data.columns:
|
| 546 |
+
# Fallback: graphique des types d'événements
|
| 547 |
+
if 'libevenem' in year_data.columns:
|
| 548 |
+
event_counts = year_data['libevenem'].value_counts()
|
| 549 |
+
fig = px.bar(x=event_counts.index,
|
| 550 |
+
y=event_counts.values,
|
| 551 |
+
title=f"📊 Types d'Interventions - {year}",
|
| 552 |
+
labels={'x': 'Type d\'intervention', 'y': 'Nombre'})
|
| 553 |
+
fig.update_layout(width=800, height=500)
|
| 554 |
+
fig.update_xaxis(tickangle=45)
|
| 555 |
+
return fig
|
| 556 |
+
else:
|
| 557 |
+
fig = px.bar(title=f"❌ Données d'intervention manquantes pour {year}")
|
| 558 |
+
fig.add_annotation(text="Colonnes de dates manquantes",
|
| 559 |
+
xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False)
|
| 560 |
+
return fig
|
| 561 |
+
|
| 562 |
+
# Convertir les dates et créer le graphique temporel
|
| 563 |
+
try:
|
| 564 |
+
year_data['datedebut_parsed'] = pd.to_datetime(year_data['datedebut'],
|
| 565 |
+
format='%d/%m/%y', errors='coerce')
|
| 566 |
+
year_data['mois'] = year_data['datedebut_parsed'].dt.month
|
| 567 |
+
|
| 568 |
+
monthly_counts = year_data.groupby('mois').size().reset_index()
|
| 569 |
+
monthly_counts.columns = ['mois', 'nb_interventions']
|
| 570 |
+
|
| 571 |
+
# Ajouter les noms des mois
|
| 572 |
+
month_names = {1: 'Jan', 2: 'Fév', 3: 'Mar', 4: 'Avr', 5: 'Mai', 6: 'Jun',
|
| 573 |
+
7: 'Jul', 8: 'Aoû', 9: 'Sep', 10: 'Oct', 11: 'Nov', 12: 'Déc'}
|
| 574 |
+
monthly_counts['mois_nom'] = monthly_counts['mois'].map(month_names)
|
| 575 |
+
|
| 576 |
+
fig = px.bar(monthly_counts,
|
| 577 |
+
x='mois_nom',
|
| 578 |
+
y='nb_interventions',
|
| 579 |
+
title=f"📅 Répartition Mensuelle des Interventions - {year}",
|
| 580 |
+
labels={'mois_nom': 'Mois', 'nb_interventions': 'Nombre d\'interventions'})
|
| 581 |
+
|
| 582 |
+
fig.update_layout(width=800, height=500)
|
| 583 |
+
return fig
|
| 584 |
+
|
| 585 |
+
except Exception as e:
|
| 586 |
+
print(f"❌ Erreur lors de la création du graphique temporel: {e}")
|
| 587 |
+
# Fallback vers le graphique des événements
|
| 588 |
+
if 'libevenem' in year_data.columns:
|
| 589 |
+
event_counts = year_data['libevenem'].value_counts()
|
| 590 |
+
fig = px.bar(x=event_counts.index,
|
| 591 |
+
y=event_counts.values,
|
| 592 |
+
title=f"📊 Types d'Interventions - {year}",
|
| 593 |
+
labels={'x': 'Type d\'intervention', 'y': 'Nombre'})
|
| 594 |
+
fig.update_layout(width=800, height=500)
|
| 595 |
+
fig.update_xaxis(tickangle=45)
|
| 596 |
+
return fig
|
| 597 |
+
else:
|
| 598 |
+
fig = px.bar(title=f"❌ Erreur lors du traitement des données {year}")
|
| 599 |
+
return fig
|
| 600 |
+
|
| 601 |
+
def create_year_herbicide_analysis(self, year):
|
| 602 |
+
"""Analyse des herbicides pour une année spécifique"""
|
| 603 |
+
year_data = self.filter_data_by_year(year)
|
| 604 |
+
|
| 605 |
+
if year_data is None or len(year_data) == 0:
|
| 606 |
+
fig = px.bar(title=f"❌ Aucune donnée disponible pour {year}")
|
| 607 |
+
fig.add_annotation(text=f"Aucune donnée pour l'année {year}",
|
| 608 |
+
xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False)
|
| 609 |
+
return fig
|
| 610 |
+
|
| 611 |
+
if 'familleprod' not in year_data.columns:
|
| 612 |
+
fig = px.bar(title=f"❌ Données de produits manquantes pour {year}")
|
| 613 |
+
fig.add_annotation(text="Colonne 'familleprod' manquante",
|
| 614 |
+
xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False)
|
| 615 |
+
return fig
|
| 616 |
+
|
| 617 |
+
herbicides_df = year_data[year_data['familleprod'] == 'Herbicides']
|
| 618 |
+
|
| 619 |
+
if len(herbicides_df) == 0:
|
| 620 |
+
fig = px.bar(title=f"✅ Aucun herbicide utilisé en {year}")
|
| 621 |
+
fig.add_annotation(text=f"Aucune intervention herbicide en {year}",
|
| 622 |
+
xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False)
|
| 623 |
+
return fig
|
| 624 |
+
|
| 625 |
+
if 'produit' in herbicides_df.columns:
|
| 626 |
+
herbicide_counts = herbicides_df['produit'].value_counts().head(10)
|
| 627 |
+
|
| 628 |
+
fig = px.bar(x=herbicide_counts.values,
|
| 629 |
+
y=herbicide_counts.index,
|
| 630 |
+
orientation='h',
|
| 631 |
+
title=f"🧪 Top 10 Herbicides Utilisés - {year}",
|
| 632 |
+
labels={'x': 'Nombre d\'utilisations', 'y': 'Produit'})
|
| 633 |
+
|
| 634 |
+
fig.update_layout(width=800, height=500)
|
| 635 |
+
return fig
|
| 636 |
+
else:
|
| 637 |
+
fig = px.bar(title=f"❌ Détails des produits manquants pour {year}")
|
| 638 |
+
fig.add_annotation(text="Colonne 'produit' manquante",
|
| 639 |
+
xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False)
|
| 640 |
+
return fig
|
| 641 |
|
| 642 |
# Initialisation de l'analyseur
|
| 643 |
analyzer = AgricultureAnalyzer()
|
requirements.txt
CHANGED
|
@@ -1,11 +1,11 @@
|
|
| 1 |
-
gradio>=4.
|
| 2 |
-
pandas>=2.0.0
|
| 3 |
-
numpy>=1.21.0
|
| 4 |
-
matplotlib>=3.5.0
|
| 5 |
-
seaborn>=0.11.0
|
| 6 |
-
plotly>=5.0.0
|
| 7 |
-
scipy>=1.7.0
|
| 8 |
-
scikit-learn>=1.0.0
|
| 9 |
-
datasets>=2.0.0
|
| 10 |
-
huggingface_hub>=0.16.0
|
| 11 |
-
pyarrow>=14.0.0
|
|
|
|
| 1 |
+
gradio>=4.44.0,<5.0.0
|
| 2 |
+
pandas>=2.0.0,<3.0.0
|
| 3 |
+
numpy>=1.21.0,<2.0.0
|
| 4 |
+
matplotlib>=3.5.0,<4.0.0
|
| 5 |
+
seaborn>=0.11.0,<1.0.0
|
| 6 |
+
plotly>=5.0.0,<6.0.0
|
| 7 |
+
scipy>=1.7.0,<2.0.0
|
| 8 |
+
scikit-learn>=1.0.0,<2.0.0
|
| 9 |
+
datasets>=2.0.0,<5.0.0
|
| 10 |
+
huggingface_hub>=0.16.0,<1.0.0
|
| 11 |
+
pyarrow>=14.0.0,<22.0.0
|