wip train dataset
This commit is contained in:
parent
3758c7467b
commit
ca5e1ddbc0
@ -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
|
||||||
|
|||||||
BIN
src/kaggle/working/models/technique_mapping.joblib
Normal file
BIN
src/kaggle/working/models/technique_mapping.joblib
Normal file
Binary file not shown.
Binary file not shown.
@ -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'")
|
|
||||||
Binary file not shown.
Binary file not shown.
@ -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
|
||||||
Loading…
x
Reference in New Issue
Block a user