wip train dataset

This commit is contained in:
Giuseppe Nucifora 2024-11-19 07:51:15 +01:00
parent 3758c7467b
commit ca5e1ddbc0
7 changed files with 384 additions and 167 deletions

View File

@ -1 +1 @@
python -m model_train.create_train_dataset --random-seed 42 --num-simulations 100000 --batch-size 10000 --max-workers 7 python -m olive_oil_train_dataset.create_train_dataset --random-seed 42 --num-simulations 100000 --batch-size 10000 --max-workers 7

Binary file not shown.

View File

@ -7,6 +7,10 @@ from tqdm import tqdm
import os import os
import argparse import argparse
import sys import sys
import gc
from utils.helpers import clean_column_name, get_growth_phase, calculate_weather_effect, calculate_water_need, \
create_technique_mapping, preprocess_weather_data
def get_optimal_workers(): def get_optimal_workers():
"""Calcola il numero ottimale di workers basato sulle risorse del sistema""" """Calcola il numero ottimale di workers basato sulle risorse del sistema"""
@ -26,150 +30,224 @@ def get_optimal_workers():
return max(1, optimal_workers) return max(1, optimal_workers)
def simulate_single_year(params): def simulate_zone(base_weather, olive_varieties, year, zone, all_varieties, variety_techniques):
""" """
Simula un singolo anno di produzione. Simula la produzione di olive per una singola zona.
Args: Args:
params: dict contenente: base_weather: DataFrame con dati meteo di base per l'anno selezionato
- weather_annual: dati meteo annuali olive_varieties: DataFrame con le informazioni sulle varietà di olive
- olive_varieties: informazioni sulle varietà zone: ID della zona
- sim_id: ID simulazione all_varieties: Array con tutte le varietà disponibili
- random_seed: seed per riproducibilità variety_techniques: Dict con le tecniche disponibili per ogni varietà
Returns:
Dict con i risultati della simulazione per la zona
""" """
np.random.seed(params['random_seed'] + params['sim_id']) # Crea una copia dei dati meteo per questa zona specifica
zone_weather = base_weather.copy()
# Seleziona anno base e applica variazioni # Genera variazioni meteorologiche specifiche per questa zona
weather = params['weather_annual'].sample(n=1, random_state=params['random_seed'] + params['sim_id']).iloc[0].copy() zone_weather['temp_mean'] *= np.random.uniform(0.95, 1.05, len(zone_weather))
zone_weather['precip_sum'] *= np.random.uniform(0.9, 1.1, len(zone_weather))
zone_weather['solarenergy_sum'] *= np.random.uniform(0.95, 1.05, len(zone_weather))
# Applica variazioni meteorologiche (±20%) # Genera caratteristiche specifiche della zona
for col in weather.index: num_varieties = np.random.randint(1, 4) # 1-3 varietà per zona
if col != 'year': selected_varieties = np.random.choice(all_varieties, size=num_varieties, replace=False)
weather[col] *= np.random.uniform(0.8, 1.2) hectares = np.random.uniform(1, 10) # Dimensione del terreno
percentages = np.random.dirichlet(np.ones(num_varieties)) # Distribuzione delle varietà
# Genera caratteristiche dell'oliveto # Inizializzazione contatori annuali
num_varieties = np.random.randint(1, 4) annual_production = 0
selected_varieties = np.random.choice( annual_min_oil = 0
params['olive_varieties']['Varietà di Olive'].unique(), annual_max_oil = 0
size=num_varieties, annual_avg_oil = 0
replace=False annual_water_need = 0
)
hectares = np.random.uniform(1, 10)
percentages = np.random.dirichlet(np.ones(num_varieties))
annual_results = { # Inizializzazione dizionario dati varietà
'simulation_id': params['sim_id'], variety_data = {clean_column_name(variety): {
'year': weather['year'], 'tech': '',
'hectares': hectares, 'pct': 0,
'num_varieties': num_varieties, 'prod_t_ha': 0,
'total_olive_production': 0, 'oil_prod_t_ha': 0,
'total_oil_production': 0, 'oil_prod_l_ha': 0,
'total_water_need': 0 'min_yield_pct': 0,
} 'max_yield_pct': 0,
'min_oil_prod_l_ha': 0,
'max_oil_prod_l_ha': 0,
'avg_oil_prod_l_ha': 0,
'l_per_t': 0,
'min_l_per_t': 0,
'max_l_per_t': 0,
'avg_l_per_t': 0,
'olive_prod': 0,
'min_oil_prod': 0,
'max_oil_prod': 0,
'avg_oil_prod': 0,
'water_need': 0
} for variety in all_varieties}
# Aggiungi dati meteorologici # Simula produzione per ogni varietà selezionata
for col in weather.index:
if col != 'year':
annual_results[f'weather_{col}'] = weather[col]
variety_details = []
for i, variety in enumerate(selected_varieties): for i, variety in enumerate(selected_varieties):
variety_data = params['olive_varieties'][ # Seleziona tecnica di coltivazione casuale per questa varietà
params['olive_varieties']['Varietà di Olive'] == variety technique = np.random.choice(variety_techniques[variety])
]
technique = np.random.choice(variety_data['Tecnica di Coltivazione'].unique())
percentage = percentages[i] percentage = percentages[i]
variety_info = variety_data[ # Ottieni informazioni specifiche della varietà
variety_data['Tecnica di Coltivazione'] == technique variety_info = olive_varieties[
(olive_varieties['Varietà di Olive'] == variety) &
(olive_varieties['Tecnica di Coltivazione'] == technique)
].iloc[0] ].iloc[0]
# Calcoli produzione con variabilità # Calcola produzione base con variabilità
production_data = calculate_production( base_production = variety_info['Produzione (tonnellate/ettaro)'] * 1000 * percentage * hectares / 12
variety_info, weather, percentage, hectares, base_production *= np.random.uniform(0.9, 1.1)
params['sim_id'] + i
# Calcola effetti meteo sulla produzione
weather_effect = zone_weather.apply(
lambda row: calculate_weather_effect(row, variety_info['Temperatura Ottimale']),
axis=1
) )
monthly_production = base_production * (1 + weather_effect / 10000)
monthly_production *= np.random.uniform(0.95, 1.05, len(zone_weather))
variety_details.append(production_data) # Calcola produzione annuale per questa varietà
annual_variety_production = monthly_production.sum()
# Aggiorna totali # Calcola rese di olio con variabilità
annual_results['total_olive_production'] += production_data['production'] min_yield_factor = np.random.uniform(0.95, 1.05)
annual_results['total_oil_production'] += production_data['oil_production'] max_yield_factor = np.random.uniform(0.95, 1.05)
annual_results['total_water_need'] += production_data['water_need'] avg_yield_factor = (min_yield_factor + max_yield_factor) / 2
# Aggiungi dettagli varietà min_oil_production = annual_variety_production * variety_info[
for i, detail in enumerate(variety_details): 'Min Litri per Tonnellata'] / 1000 * min_yield_factor
prefix = f'variety_{i + 1}' max_oil_production = annual_variety_production * variety_info[
for key, value in detail.items(): 'Max Litri per Tonnellata'] / 1000 * max_yield_factor
annual_results[f'{prefix}_{key}'] = value avg_oil_production = annual_variety_production * variety_info[
'Media Litri per Tonnellata'] / 1000 * avg_yield_factor
# Calcola metriche per ettaro e KPI # Calcola fabbisogno idrico
annual_results['olive_production_ha'] = annual_results['total_olive_production'] / hectares base_water_need = (
annual_results['oil_production_ha'] = annual_results['total_oil_production'] / hectares variety_info['Fabbisogno Acqua Primavera (m³/ettaro)'] +
annual_results['water_need_ha'] = annual_results['total_water_need'] / hectares variety_info['Fabbisogno Acqua Estate (m³/ettaro)'] +
variety_info['Fabbisogno Acqua Autunno (m³/ettaro)'] +
variety_info['Fabbisogno Acqua Inverno (m³/ettaro)']
) / 4
# Calcola efficienze monthly_water_need = zone_weather.apply(
if annual_results['total_olive_production'] > 0: lambda row: calculate_water_need(row, base_water_need, variety_info['Temperatura Ottimale']),
annual_results['yield_efficiency'] = annual_results['total_oil_production'] / annual_results[ axis=1
'total_olive_production'] )
else: monthly_water_need *= np.random.uniform(0.95, 1.05, len(monthly_water_need))
annual_results['yield_efficiency'] = 0 annual_variety_water_need = monthly_water_need.sum() * percentage * hectares
if annual_results['total_water_need'] > 0: # Aggiorna totali annuali
annual_results['water_efficiency'] = annual_results['total_olive_production'] / annual_results[ annual_production += annual_variety_production
'total_water_need'] annual_min_oil += min_oil_production
else: annual_max_oil += max_oil_production
annual_results['water_efficiency'] = 0 annual_avg_oil += avg_oil_production
annual_water_need += annual_variety_water_need
return annual_results # Aggiorna dati varietà
clean_variety = clean_column_name(variety)
variety_data[clean_variety].update({
'tech': clean_column_name(technique),
'pct': percentage,
'prod_t_ha': variety_info['Produzione (tonnellate/ettaro)'] * np.random.uniform(0.95, 1.05),
'oil_prod_t_ha': variety_info['Produzione Olio (tonnellate/ettaro)'] * np.random.uniform(0.95, 1.05),
'oil_prod_l_ha': variety_info['Produzione Olio (litri/ettaro)'] * np.random.uniform(0.95, 1.05),
'min_yield_pct': variety_info['Min % Resa'] * min_yield_factor,
'max_yield_pct': variety_info['Max % Resa'] * max_yield_factor,
'min_oil_prod_l_ha': variety_info['Min Produzione Olio (litri/ettaro)'] * min_yield_factor,
'max_oil_prod_l_ha': variety_info['Max Produzione Olio (litri/ettaro)'] * max_yield_factor,
'avg_oil_prod_l_ha': variety_info['Media Produzione Olio (litri/ettaro)'] * avg_yield_factor,
'l_per_t': variety_info['Litri per Tonnellata'] * np.random.uniform(0.98, 1.02),
'min_l_per_t': variety_info['Min Litri per Tonnellata'] * min_yield_factor,
'max_l_per_t': variety_info['Max Litri per Tonnellata'] * max_yield_factor,
'avg_l_per_t': variety_info['Media Litri per Tonnellata'] * avg_yield_factor,
'olive_prod': annual_variety_production,
'min_oil_prod': min_oil_production,
'max_oil_prod': max_oil_production,
'avg_oil_prod': avg_oil_production,
'water_need': annual_variety_water_need
})
# Appiattisci i dati delle varietà
flattened_variety_data = {
f'{variety}_{key}': value
for variety, data in variety_data.items()
for key, value in data.items()
}
# Restituisci il risultato della zona
return {
'year': year,
'zone_id': zone + 1,
'temp_mean': zone_weather['temp_mean'].mean(),
'precip_sum': zone_weather['precip_sum'].sum(),
'solar_energy_sum': zone_weather['solarenergy_sum'].sum(),
'ha': hectares,
'zone': f"zone_{zone + 1}",
'olive_prod': annual_production,
'min_oil_prod': annual_min_oil,
'max_oil_prod': annual_max_oil,
'avg_oil_prod': annual_avg_oil,
'total_water_need': annual_water_need,
**flattened_variety_data
}
def generate_training_dataset_parallel(weather_data, olive_varieties, num_simulations=1000, def simulate_olive_production_parallel(weather_data, olive_varieties, num_simulations=5, num_zones=None,
random_seed=42, max_workers=None, batch_size=500, random_seed=None,
output_path='olive_training_dataset.parquet'): max_workers=None, batch_size=500,
output_path='olive_simulation_dataset.parquet'):
""" """
Genera dataset di training utilizzando multiprocessing. Versione corretta della simulazione parallelizzata con gestione batch e salvataggio file
Args: Args:
weather_data: DataFrame dati meteo weather_data: DataFrame con dati meteo
olive_varieties: DataFrame varietà olive olive_varieties: DataFrame con varietà di olive
num_simulations: numero di simulazioni num_simulations: numero di simulazioni da eseguire (default: 5)
random_seed: seed per riproducibilità num_zones: numero di zone per simulazione (default: None, usa num_simulations se non specificato)
max_workers: numero massimo di workers random_seed: seed per riproducibilità (default: None)
batch_size: dimensione batch max_workers: numero massimo di workers (default: None, usa get_optimal_workers)
output_path: percorso file output batch_size: dimensione del batch per gestione memoria (default: 500)
output_path: percorso del file di output (default: 'olive_simulation_dataset.parquet')
Returns:
DataFrame con i risultati delle simulazioni
""" """
np.random.seed(random_seed) if random_seed is not None:
np.random.seed(random_seed)
# Prepara dati meteo annuali # Se num_zones non è specificato, usa num_simulations
weather_annual = weather_data.groupby('year').agg({ if num_zones is None:
'temp': ['mean', 'min', 'max', 'std'], num_zones = num_simulations
'humidity': ['mean', 'min', 'max'],
'precip': ['sum', 'mean', 'std'],
'solarradiation': ['mean', 'sum', 'std'],
'cloudcover': ['mean']
}).reset_index()
weather_annual.columns = ['year'] + [ # Preparazione dati
f'{col[0]}_{col[1]}' for col in weather_annual.columns[1:] create_technique_mapping(olive_varieties)
] monthly_weather = preprocess_weather_data(weather_data)
all_varieties = olive_varieties['Varietà di Olive'].unique()
variety_techniques = {
variety: olive_varieties[olive_varieties['Varietà di Olive'] == variety]['Tecnica di Coltivazione'].unique()
for variety in all_varieties
}
# Calcola workers ottimali # Calcolo workers ottimali usando get_optimal_workers
if max_workers is None: if max_workers is None:
max_workers = get_optimal_workers() max_workers = get_optimal_workers()
print(f"Utilizzando {max_workers} workers ottimali basati sulle risorse del sistema")
print(f"Utilizzando {max_workers} workers") # Calcolo numero di batch
# Calcola numero di batch
num_batches = (num_simulations + batch_size - 1) // batch_size num_batches = (num_simulations + batch_size - 1) // batch_size
print(f"Elaborazione di {num_simulations} simulazioni in {num_batches} batch") print(f"Elaborazione di {num_simulations} simulazioni con {num_zones} zone in {num_batches} batch")
print(f"Totale record attesi: {num_simulations * num_zones:,}")
# Crea directory output se necessario
os.makedirs(os.path.dirname(output_path) if os.path.dirname(output_path) else '.', exist_ok=True)
# Lista per contenere tutti i DataFrame dei batch # Lista per contenere tutti i DataFrame dei batch
all_batches = [] all_batches = []
# Elaborazione per batch
for batch_num in range(num_batches): for batch_num in range(num_batches):
start_sim = batch_num * batch_size start_sim = batch_num * batch_size
end_sim = min((batch_num + 1) * batch_size, num_simulations) end_sim = min((batch_num + 1) * batch_size, num_simulations)
@ -177,54 +255,79 @@ def generate_training_dataset_parallel(weather_data, olive_varieties, num_simula
batch_results = [] batch_results = []
# Preparazione parametri per ogni simulazione # Parallelizzazione usando ProcessPoolExecutor per il batch corrente
simulation_params = [
{
'weather_annual': weather_annual,
'olive_varieties': olive_varieties,
'sim_id': sim_id,
'random_seed': random_seed
}
for sim_id in range(start_sim, end_sim)
]
# Esegui simulazioni in parallelo
with ProcessPoolExecutor(max_workers=max_workers) as executor: with ProcessPoolExecutor(max_workers=max_workers) as executor:
futures = [executor.submit(simulate_single_year, params) # Calcola il numero totale di task per questo batch
for params in simulation_params] # Ogni simulazione nel batch corrente genererà num_zones zone
total_tasks = current_batch_size * num_zones
with tqdm(total=current_batch_size, with tqdm(total=total_tasks,
desc=f"Batch {batch_num + 1}/{num_batches}") as pbar: desc=f"Batch {batch_num + 1}/{num_batches}") as pbar:
for future in as_completed(futures): # Dizionario per tenere traccia delle futures e dei loro sim_id
future_to_sim_id = {}
# Sottometti i lavori per tutte le simulazioni e zone nel batch corrente
for sim in range(start_sim, end_sim):
selected_year = np.random.choice(monthly_weather['year'].unique())
base_weather = monthly_weather[monthly_weather['year'] == selected_year].copy()
base_weather.loc[:, 'growth_phase'] = base_weather['month'].apply(get_growth_phase)
# Sottometti i lavori per tutte le zone di questa simulazione
for zone in range(num_zones):
future = executor.submit(
simulate_zone,
base_weather=base_weather,
olive_varieties=olive_varieties,
year=selected_year,
zone=zone,
all_varieties=all_varieties,
variety_techniques=variety_techniques
)
future_to_sim_id[future] = (sim + 1, zone + 1)
# Raccogli i risultati man mano che vengono completati
for future in as_completed(future_to_sim_id.keys()):
sim_id, zone_id = future_to_sim_id[future]
try: try:
result = future.result() result = future.result()
result['simulation_id'] = sim_id
result['zone_id'] = zone_id
batch_results.append(result) batch_results.append(result)
pbar.update(1) pbar.update(1)
except Exception as e: except Exception as e:
print(f"Errore in simulazione: {str(e)}") print(f"Errore nella simulazione {sim_id}, zona {zone_id}: {str(e)}")
continue continue
# Converti risultati in DataFrame # Converti batch_results in DataFrame e aggiungi alla lista dei batch
batch_df = pd.DataFrame(batch_results) batch_df = pd.DataFrame(batch_results)
all_batches.append(batch_df) all_batches.append(batch_df)
# Stampa statistiche del batch
print(f"\nStatistiche Batch {batch_num + 1}:")
print(f"Righe processate: {len(batch_df):,}")
print(f"Memoria utilizzata: {batch_df.memory_usage(deep=True).sum() / 1024 ** 2:.2f} MB")
# Libera memoria # Libera memoria
del batch_results del batch_results
del batch_df
gc.collect() # Forza garbage collection
# Concatena tutti i batch e salva # Concatena tutti i batch e salva
print("\nConcatenazione dei batch e salvataggio...")
final_df = pd.concat(all_batches, ignore_index=True) final_df = pd.concat(all_batches, ignore_index=True)
# Crea directory output se necessario
os.makedirs(os.path.dirname(output_path) if os.path.dirname(output_path) else '.', exist_ok=True)
# Salva il dataset
final_df.to_parquet(output_path) final_df.to_parquet(output_path)
# Stampa statistiche finali
print("\nStatistiche Finali:")
print(f"Totale simulazioni completate: {len(final_df):,}")
print(f"Memoria totale utilizzata: {final_df.memory_usage(deep=True).sum() / 1024 ** 2:.2f} MB")
print(f"\nDataset salvato in: {output_path}") print(f"\nDataset salvato in: {output_path}")
# Statistiche finali
print("\nStatistiche finali:")
print(f"Righe totali: {len(final_df)}")
print("\nAnalisi variabilità:")
for col in ['olive_production_ha', 'oil_production_ha', 'water_need_ha']:
cv = final_df[col].std() / final_df[col].mean()
print(f"{col}: CV = {cv:.2%}")
return final_df return final_df
@ -319,7 +422,6 @@ def calculate_solar_effect(radiation):
return base_factor * np.random.uniform(0.8, 1.2) return base_factor * np.random.uniform(0.8, 1.2)
def parse_arguments(): def parse_arguments():
""" """
Configura e gestisce i parametri da riga di comando Configura e gestisce i parametri da riga di comando
@ -339,10 +441,17 @@ def parse_arguments():
parser.add_argument( parser.add_argument(
'--num-simulations', '--num-simulations',
type=int, type=int,
default=1000000, default=100000,
help='Numero totale di simulazioni da eseguire' help='Numero totale di simulazioni da eseguire'
) )
parser.add_argument(
'--num-zones',
type=int,
default=None,
help='Numero di zone per simulazione (default: uguale a num-simulations)'
)
parser.add_argument( parser.add_argument(
'--batch-size', '--batch-size',
type=int, type=int,
@ -360,25 +469,21 @@ def parse_arguments():
parser.add_argument( parser.add_argument(
'--max-workers', '--max-workers',
type=int, type=int,
default=2, default=None,
help='Quantità di workers' help='Quantità di workers (default: usa get_optimal_workers)'
) )
return parser.parse_args() return parser.parse_args()
# Esempio di utilizzo
if __name__ == "__main__": if __name__ == "__main__":
print("Generazione dataset di training...") print("Generazione dataset di training...")
# Parsing argomenti # Parsing argomenti
args = parse_arguments() args = parse_arguments()
# Carica dati # Carica dati
try: try:
# Carica dati
weather_data = pd.read_parquet('./sources/weather_data_complete.parquet') weather_data = pd.read_parquet('./sources/weather_data_complete.parquet')
olive_varieties = pd.read_parquet('./sources/olive_varieties.parquet') olive_varieties = pd.read_parquet('./sources/olive_varieties.parquet')
except Exception as e: except Exception as e:
@ -389,36 +494,23 @@ if __name__ == "__main__":
print("\nConfigurazione:") print("\nConfigurazione:")
print(f"Random seed: {args.random_seed}") print(f"Random seed: {args.random_seed}")
print(f"Numero simulazioni: {args.num_simulations:,}") print(f"Numero simulazioni: {args.num_simulations:,}")
print(f"Workers: {args.max_workers:,}") print(f"Numero zone per simulazione: {args.num_zones if args.num_zones is not None else args.num_simulations:,}")
print(f"Workers: {args.max_workers if args.max_workers is not None else 'auto'}")
print(f"Dimensione batch: {args.batch_size:,}") print(f"Dimensione batch: {args.batch_size:,}")
print(f"File output: {args.output_path}") print(f"File output: {args.output_path}")
# Genera dataset # Genera dataset
try: try:
df = generate_training_dataset_parallel( df = simulate_olive_production_parallel(
weather_data=weather_data, weather_data=weather_data,
olive_varieties=olive_varieties, olive_varieties=olive_varieties,
random_seed=args.random_seed,
num_simulations=args.num_simulations, num_simulations=args.num_simulations,
num_zones=args.num_zones,
random_seed=args.random_seed,
batch_size=args.batch_size, batch_size=args.batch_size,
output_path=args.output_path, output_path=args.output_path,
max_workers=args.max_workers max_workers=args.max_workers
) )
except Exception as e: except Exception as e:
print(f"Errore durante la generazione del dataset: {str(e)}") print(f"Errore durante la generazione del dataset: {str(e)}")
sys.exit(1) sys.exit(1)
print("\nShape dataset:", df.shape)
print("\nColonne disponibili:")
print(df.columns.tolist())
print("\nStatistiche di base:")
print(df.describe())
# Analisi variabilità
print("\nAnalisi coefficienti di variazione:")
for col in ['olive_production_ha', 'oil_production_ha', 'water_need_ha']:
cv = df[col].std() / df[col].mean()
print(f"{col}: {cv:.2%}")
print("\nDataset salvato './sources/olive_training_dataset.parquet'")

View File

@ -2,8 +2,10 @@ import psutil
import multiprocessing import multiprocessing
import re import re
import pandas as pd import pandas as pd
from typing import List, Dict
import numpy as np import numpy as np
from typing import List, Dict
import os
import joblib
def get_optimal_workers() -> int: def get_optimal_workers() -> int:
@ -215,12 +217,6 @@ def get_full_data(simulated_data: pd.DataFrame,
return full_data return full_data
import numpy as np
from typing import List, Dict
def prepare_static_features_multiple(varieties_info: List[Dict], def prepare_static_features_multiple(varieties_info: List[Dict],
percentages: List[float], percentages: List[float],
hectares: float, hectares: float,
@ -376,4 +372,133 @@ def add_controlled_variation(base_value: float, max_variation_pct: float = 0.20)
Valore con variazione applicata Valore con variazione applicata
""" """
variation = np.random.uniform(-max_variation_pct, max_variation_pct) variation = np.random.uniform(-max_variation_pct, max_variation_pct)
return base_value * (1 + variation) return base_value * (1 + variation)
def get_growth_phase(month):
if month in [12, 1, 2]:
return 'dormancy'
elif month in [3, 4, 5]:
return 'flowering'
elif month in [6, 7, 8]:
return 'fruit_set'
else:
return 'ripening'
def calculate_weather_effect(row, optimal_temp):
# Effetti base
temp_effect = -0.1 * (row['temp_mean'] - optimal_temp) ** 2
rain_effect = -0.05 * (row['precip_sum'] - 600) ** 2 / 10000
sun_effect = 0.1 * row['solarenergy_sum'] / 1000
# Fattori di scala basati sulla fase di crescita
if row['growth_phase'] == 'dormancy':
temp_scale = 0.5
rain_scale = 0.2
sun_scale = 0.1
elif row['growth_phase'] == 'flowering':
temp_scale = 2.0
rain_scale = 1.5
sun_scale = 1.0
elif row['growth_phase'] == 'fruit_set':
temp_scale = 1.5
rain_scale = 1.0
sun_scale = 0.8
else: # ripening
temp_scale = 1.0
rain_scale = 0.5
sun_scale = 1.2
# Calcolo dell'effetto combinato
combined_effect = (
temp_scale * temp_effect +
rain_scale * rain_effect +
sun_scale * sun_effect
)
# Aggiustamenti specifici per fase
if row['growth_phase'] == 'flowering':
combined_effect -= 0.5 * max(0, row['precip_sum'] - 50) # Penalità per pioggia eccessiva durante la fioritura
elif row['growth_phase'] == 'fruit_set':
combined_effect += 0.3 * max(0, row['temp_mean'] - (optimal_temp + 5)) # Bonus per temperature più alte durante la formazione dei frutti
return combined_effect
def calculate_water_need(weather_data, base_need, optimal_temp):
# Calcola il fabbisogno idrico basato su temperatura e precipitazioni
temp_factor = 1 + 0.05 * (weather_data['temp_mean'] - optimal_temp) # Aumenta del 5% per ogni grado sopra l'ottimale
rain_factor = 1 - 0.001 * weather_data['precip_sum'] # Diminuisce leggermente con l'aumentare delle precipitazioni
return base_need * temp_factor * rain_factor
def create_technique_mapping(olive_varieties, mapping_path='./kaggle/working/models/technique_mapping.joblib'):
# Estrai tutte le tecniche uniche dal dataset e convertile in lowercase
all_techniques = olive_varieties['Tecnica di Coltivazione'].str.lower().unique()
# Crea il mapping partendo da 1
technique_mapping = {tech: i + 1 for i, tech in enumerate(sorted(all_techniques))}
# Salva il mapping
os.makedirs(os.path.dirname(mapping_path), exist_ok=True)
joblib.dump(technique_mapping, mapping_path)
return technique_mapping
def encode_techniques(df, mapping_path='./kaggle/working/models/technique_mapping.joblib'):
if not os.path.exists(mapping_path):
raise FileNotFoundError(f"Mapping not found at {mapping_path}. Run create_technique_mapping first.")
technique_mapping = joblib.load(mapping_path)
# Trova tutte le colonne delle tecniche
tech_columns = [col for col in df.columns if col.endswith('_tech')]
# Applica il mapping a tutte le colonne delle tecniche
for col in tech_columns:
df[col] = df[col].str.lower().map(technique_mapping).fillna(0).astype(int)
return df
def decode_techniques(df, mapping_path='./kaggle/working/models/technique_mapping.joblib'):
if not os.path.exists(mapping_path):
raise FileNotFoundError(f"Mapping not found at {mapping_path}")
technique_mapping = joblib.load(mapping_path)
reverse_mapping = {v: k for k, v in technique_mapping.items()}
reverse_mapping[0] = '' # Aggiungi un mapping per 0 a stringa vuota
# Trova tutte le colonne delle tecniche
tech_columns = [col for col in df.columns if col.endswith('_tech')]
# Applica il reverse mapping a tutte le colonne delle tecniche
for col in tech_columns:
df[col] = df[col].map(reverse_mapping)
return df
def decode_single_technique(technique_value, mapping_path='./kaggle/working/models/technique_mapping.joblib'):
if not os.path.exists(mapping_path):
raise FileNotFoundError(f"Mapping not found at {mapping_path}")
technique_mapping = joblib.load(mapping_path)
reverse_mapping = {v: k for k, v in technique_mapping.items()}
reverse_mapping[0] = ''
return reverse_mapping.get(technique_value, '')
def preprocess_weather_data(weather_df):
# Calcola statistiche mensili per ogni anno
monthly_weather = weather_df.groupby(['year', 'month']).agg({
'temp': ['mean', 'min', 'max'],
'humidity': 'mean',
'precip': 'sum',
'windspeed': 'mean',
'cloudcover': 'mean',
'solarradiation': 'sum',
'solarenergy': 'sum',
'uvindex': 'max'
}).reset_index()
monthly_weather.columns = ['year', 'month'] + [f'{col[0]}_{col[1]}' for col in monthly_weather.columns[2:]]
return monthly_weather