Novità

Python - Addestramento rete neurale

Lo sò che l'argomento di questo thread non sarebbe per novizi
della programmazione (è evidente), ma credo di non essere il solo quindi
ho azzardato la domanda, scusatemi ancora....
siamo tutti novizi qui nessuno ne sa di piu ,solo la passione puo' accumunarci ,e la voglia di migliorare .
ciao a presto
 
Grazie ancora LottoPython per la tua disponibilità e velocità di risposta.
Proverò ancora, anche se la vedo molto dura per me...
Comunque seguo. Grazie ancora.
 
giusto

Screenshot-11.png

questo è gia leggermente migliore come loss ed epoche tende alla discesa in questo caso aumento le epoche e vediamo come si comporta

Screenshot-12.png



sembra che il 48 36 sia atteso su FI CA
36 fi :unsure:
 
Buon giorno a tutti , premetto che ero completamente un neofita su python e grazie a questo sezione del forum ho imparato molte cose su questo ambiente e già questo per me è entusiasmante . come dicevo non sono molto pratico con questa programmazione ma grazie hai Big che girano qui e agli esempi che sono stati postati ho avuto modo di migliorare , e solo per questo vi ringrazio molto, e mi piace l'entusiasmo ..la passione e la disponibilità e la condivisione che avete dimostrato . sicuramente non riuscirò a dare il mio contributo al programma che state portando avanti perche non sono al vostro livello , però posso dirvi una cosa che ho imparato negli anni passati testando centinaia e centinaia di modelli di rete neuro con il programma neurosolution di neuro dimension . quando la previsioni dei vari modelli si aggira attorno al numero 45 , che sia più o meno in quel range vuol dire che la rete non sta facendo altro che avvicinarsi alla media dei 90 numeri , e credo che sarete d'accordo con mè che questa non è sicuramente una previsione valida. la rosa dei numeri è da 1 a 90 quindi cambiando ruote e set di dati prima ho poi come output questi numeri si devono alternare.
 
Buon giorno a tutti , premetto che ero completamente un neofita su python e grazie a questo sezione del forum ho imparato molte cose su questo ambiente e già questo per me è entusiasmante . come dicevo non sono molto pratico con questa programmazione ma grazie hai Big che girano qui e agli esempi che sono stati postati ho avuto modo di migliorare , e solo per questo vi ringrazio molto, e mi piace l'entusiasmo ..la passione e la disponibilità e la condivisione che avete dimostrato . sicuramente non riuscirò a dare il mio contributo al programma che state portando avanti perche non sono al vostro livello , però posso dirvi una cosa che ho imparato negli anni passati testando centinaia e centinaia di modelli di rete neuro con il programma neurosolution di neuro dimension . quando la previsioni dei vari modelli si aggira attorno al numero 45 , che sia più o meno in quel range vuol dire che la rete non sta facendo altro che avvicinarsi alla media dei 90 numeri , e credo che sarete d'accordo con mè che questa non è sicuramente una previsione valida. la rosa dei numeri è da 1 a 90 quindi cambiando ruote e set di dati prima ho poi come output questi numeri si devono alternare.
buongiorno Batty , ti ringrazio per le tue osservazioni interessanti è un problema in effetti che sto verificando e cercando di risolvere "tempo permettendo" sei il benvenuto che tu abbia o meno esperienza ,l'idea puo sempre evolversi e crescere allo stesso tempo e sempre un buon motivo per apprendere qualcosa in piu a noi sconosciuto.
Mia uguro di riuscire a risolvere queste ed altre anomalie assieme e ricordo che si tratta ancora di un progetto nuovo ,il codice ampiamente ampliato da max puo' essere scaricato e modificato con o senza l'aiuto della Ia.
Buona giornata e buona domenica a tutti voi
Presumo e credo che questo sia uno dei pochi forum se non l'unico che abbia questo tipo di argomento , c'è anche il sito di lotto_tom dove documenta magistralmente il progetto Susanna avanti anni luce decisamente.
rinnovo i saluti
 
Buona domenica a tutti
Si hai ragione @batty, a me succede quasi sempre che la rete dia come previsioni i numeri intorno a 45, perché come hai giustamente detto anche tu la rete si avvicina alla media. Dopotutto infatti la rete utilizza calcoli matematici per imparare. Al momento non ho ancora capito come risolvere questo problema purtroppo.
 
Buona domenica a tutti
Si hai ragione @batty, a me succede quasi sempre che la rete dia come previsioni i numeri intorno a 45, perché come hai giustamente detto anche tu la rete si avvicina alla media. Dopotutto infatti la rete utilizza calcoli matematici per imparare. Al momento non ho ancora capito come risolvere questo problema purtroppo.
io non ci sono ancora riuscito ,o meglio ho notato che più il range di estrazioni dato in pasto alla rete è vasto più il range di output si restringe avvicinandosi al 45 Quindi per far si che vengano generati tutti i numeri io ho provato a ridurre il range di estrazioni passo passo, e ho visto che vengono generati una palette di numeri molto più ampia quando il range dato si limita alle 5,6,7 estrazioni . E qui ho il forte dubbio che con cosi poche estrazioni la rete sia in grado di trovare pattern affidabili . trovo MOLTO INTERESSANTE il progetto Simpatici e quindi stavo pensando a una formula per far si che la rete quando restituisce la previsione tenga in considerazione in percentuale anche quei numeri . BUONA DOMENICA A TUTTI ..è una bella giornata ...passate anche qualche ora al sole non solo incollati allo schermo dei vostri PC 😂 😂 :ROFLMAO:
 
investigando un po con l'ia ha portato ad una serie di riflessioni fredde senza patos ..solo possibili soluzioni ..ma tribolate...


L'aggiunta di rumore è un trucco comune per migliorare la generalizzazione e la robustezza del modello, ma può avere un impatto diverso a seconda della distribuzione dei dati. In questo caso, il tuo obiettivo è assicurarti che il modello gestisca bene i numeri anche al di sopra della media (45). Ci sono diverse strategie che puoi provare per migliorare ulteriormente questo aspetto:

1. Adattamento dell'ampiezza del rumore:

  • Problema: L'aggiunta di rumore con una deviazione standard fissa (es. 0.01) potrebbe non essere ottimale per tutti i valori. Potrebbe essere troppo per i numeri più piccoli e insufficiente per quelli più grandi, influenzando la capacità del modello di generalizzare bene.
  • Soluzione: Considera di rendere l'ampiezza del rumore dinamica, collegandola alla grandezza dei valori dei dati. Ad esempio:

  • Codice:
    noise = np.random.normal(0, 0.01 * np.abs(X_train), X_train.shape)  # Rumore proporzionale all'ampiezza
    X_train_noisy = X_train + noise
  • In questo modo, il rumore sarà più grande per i numeri più grandi e più piccolo per i numeri più piccoli, preservando meglio le relazioni tra i dati. Puoi anche sperimentare con diversi fattori (es. 0.01 potrebbe essere troppo, prova con 0.05 o 0.1).
2. Rimozione del rumore nei valori di test:

  • Problema: Stai aggiungendo rumore solo durante l'addestramento. Durante la fase di test o di previsione, i dati sono "puliti". Questo può creare una discrepanza tra l'ambiente di addestramento e quello di test, portando a prestazioni peggiori.

Soluzione:
Non aggiungere rumore ai dati di test. La rimozione del rumore nei dati di test si farà togliendo
noise = np.random.normal(0, 0.01, X_train.shape) e X_train_noisy = X_train + noise dalla funzione.

Codice:
# Early Stopping
early_stopping = EarlyStopping(monitor='val_loss', patience=patience, min_delta=min_delta, restore_best_weights=True)

# Allenamento del modello
history = model.fit(X_train, y_train, epochs=epochs, batch_size=64, validation_data=(X_test, y_test),
                    callbacks=[early_stopping], verbose=0)

3. Data Augmentation avanzata:

  • Problema: L'aggiunta di rumore è una forma di data augmentation, ma potresti aver bisogno di tecniche più sofisticate per migliorare la generalizzazione, soprattutto se hai dati limitati.
  • Soluzione:Considera le seguenti strategie:
    • Mixup: Combina casualmente coppie di esempi di addestramento e le loro etichette con una ponderazione. Questo aiuta il modello a imparare relazioni più robuste.
    • Speciale attenzione ai numeri sopra la media: Se i numeri sopra la media sono meno frequenti, puoi dare loro una ponderazione maggiore durante l'addestramento, o generare nuovi dati sintetici (es. campionando dalla distribuzione dei numeri esistenti ma con valori più alti).
4. Funzione di perdita:

  • Problema: La funzione di perdita (MSE o MAE) potrebbe non essere la più adatta.
  • Soluzione:
    • MSE (Mean Squared Error): Penalizza maggiormente gli errori grandi. Potrebbe non essere la scelta migliore se vuoi dare la stessa importanza agli errori piccoli e grandi.
    • MAE (Mean Absolute Error): È meno sensibile agli outlier, ma potrebbe non dare la giusta enfasi ai numeri sopra la media.
    • Huber Loss: Una combinazione di MSE e MAE. È meno sensibile agli outlier rispetto al MSE, ma quadratico vicino allo zero. Potrebbe essere una buona via di mezzo.
Codice Modificato (con ampiezza del rumore dinamica):
 
.. vi presento

Screenshot-10.png

Codice:
Empathix è un'applicazione GUI (Graphical User Interface) realizzata in Python utilizzando le librerie pandas, numpy, scikit-learn, tensorflow, tkinter e matplotlib. L'applicazione è progettata per analizzare i dati delle estrazioni del lotto, costruire modelli di reti neurali e prevedere i numeri.

**Funzioni principali:**

1.  **Caricamento dei dati:**
    *   Scarica i dati delle estrazioni del lotto da repository GitHub per diverse ruote (es. Bari, Cagliari, Milano).
    *   Permette di specificare un intervallo di date per filtrare i dati.
    *   Normalizza i dati numerici usando `MinMaxScaler`.

2.  **Costruzione del modello di rete neurale:**
    *   Utilizza TensorFlow per costruire un modello sequenziale (`Sequential`).
    *   Definisce una funzione `build_model` che crea una rete neurale con:
        *   Layer di input.
        *   Layer densi (`Dense`) con attivazione `Swish` e `BatchNormalization` e `Dropout`.
        *   Layer di output (`Dense`) con attivazione `relu`.
    *   Permette di configurare il numero di layer densi, il numero di neuroni per layer e i tassi di dropout.
    *   Supporta diversi ottimizzatori (`Adam`, `RMSprop`, `SGD`) e funzioni di perdita (`MSE`, `MAE`).

3.  **Addestramento del modello:**
    *   Utilizza la validazione incrociata K-Fold (`KFold`) per valutare le prestazioni del modello in modo più robusto.
    *   Applica `EarlyStopping` per interrompere l'addestramento se le prestazioni del modello non migliorano per un certo numero di epoche (pazienza).
    *   Aggiunge rumore ai dati di addestramento per migliorare la generalizzazione.
    *   Monitora la perdita durante l'addestramento e la validazione.

4.  **Previsione:**
    *   Utilizza il modello addestrato per fare previsioni sui numeri del lotto.
    *   Normalizza i dati di output (previsioni) e li converte in numeri interi.

5.  **Visualizzazione e analisi dei risultati:**
    *   Mostra grafici dell'andamento della perdita durante l'addestramento e la validazione.
    *   Calcola e mostra la "prevedibilità" delle ruote, basata sulla media della perdita di validazione.
    *   Mostra i numeri previsti.
    *   Permette di salvare i grafici e i risultati in file.

6.  **Interfaccia grafica (GUI):**
    *   Utilizza `tkinter` per creare un'interfaccia utente.
    *   Include elementi come pulsanti per selezionare le ruote, campi di input per le date, i parametri del modello (epoche, neuroni, dropout, ottimizzatori, funzione di perdita), una barra di avanzamento e un box di testo per visualizzare i risultati e messaggi di log.

7.  **Logging:**
    *   Utilizza il modulo `logging` per registrare eventi, errori e informazioni utili durante l'esecuzione del programma.
    *   Un handler di logging personalizzato (`TextHandler`) indirizza i messaggi di log al box di testo della GUI.

**Implementazioni specifiche:**

*   **Funzione di attivazione Swish:** `swish(x)`.
*   **Funzioni per la selezione e configurazione:**
    *   `select_optimizer`, `select_loss_function`, `select_patience`, `select_min_delta`.
*   **Funzioni per l'aggiornamento dell'interfaccia:**
    *   `update_progress`, `update_textbox`, `mostra_numeri_predetti`, `mostra_migliori_risultati`, `mostra_grafico`, `mostra_prevedibilita`.
*   **Funzioni per il salvataggio:**
    *   `salva_grafico`, `salva_risultati`.
*   **Semina per la riproducibilità:** `set_seed()`.
*   **Gestione dei pulsanti e delle selezioni:** `on_seleziona_ruota()`.
*   **Organizzazione dell'interfaccia:** utilizzo di `Frame` per raggruppare i widget.

In sostanza, Empathix è uno strumento completo per l'analisi dei dati del lotto e la sperimentazione con modelli di reti neurali per la previsione dei numeri, con un'interfaccia utente che ne facilita l'uso e la comprensione.



Codice:
import pandas as pd  # Importa la libreria pandas per la gestione dei dati.
import numpy as np  # Importa la libreria numpy per operazioni numeriche.
from sklearn.model_selection import KFold  # Importa KFold per la validazione incrociata.
from sklearn.preprocessing import MinMaxScaler  # Importa MinMaxScaler per la normalizzazione dei dati.
import tensorflow as tf  # Importa tensorflow per la costruzione del modello di rete neurale.
from tensorflow.keras.models import Sequential  # Importa Sequential per la costruzione del modello sequenziale.
from tensorflow.keras.layers import Dense, Dropout, BatchNormalization  # Importa le classi di layer.
from tensorflow.keras.callbacks import EarlyStopping  # Importa EarlyStopping per l'arresto anticipato.
import tkinter as tk  # Importa tkinter per l'interfaccia grafica.
from tkinter import messagebox, ttk, scrolledtext, filedialog  # Importa messagebox e ttk per widget specifici.
import os  # Importa os per interazioni con il sistema operativo.
import random  # Importa random per generare numeri casuali.
import matplotlib.pyplot as plt  # Importa matplotlib per la visualizzazione dei grafici.
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg  # Importa FigureCanvasTkAgg per integrare matplotlib con tkinter.
import logging  # Importa logging per il logging degli eventi.
import requests  # Importa requests per effettuare richieste HTTP.
from io import StringIO  # Importa StringIO per la gestione di stringhe come file.
import time  # Importa time per la gestione del tempo.

# Configurazione del logger
logger = logging.getLogger(__name__)  # Ottiene un logger con il nome del modulo.
logger.setLevel(logging.INFO)  # Imposta il livello di logging a INFO.

# Crea un handler per il box di testo
class TextHandler(logging.Handler):  # Definisce una classe per gestire il logging nel box di testo.
    def __init__(self, text_widget):  # Costruttore della classe.
        logging.Handler.__init__(self)  # Chiama il costruttore della classe base.
        self.text_widget = text_widget  # Memorizza il widget di testo.

    def emit(self, record):  # Metodo per emettere un messaggio di log.
        msg = self.format(record)  # Formatta il record di log.
        self.text_widget.config(state=tk.NORMAL)  # Abilita la modifica del widget di testo.
        self.text_widget.insert(tk.END, msg + '\n')  # Inserisce il messaggio di log.
        self.text_widget.config(state=tk.DISABLED)  # Disabilita la modifica del widget di testo.
        self.text_widget.see(tk.END)  # Fa scorrere il widget di testo fino alla fine.

# Imposta il seme per la riproducibilità
def set_seed(seed_value=42):  # Definisce una funzione per impostare il seme per la riproducibilità.
    os.environ['PYTHONHASHSEED'] = str(seed_value)  # Imposta una variabile d'ambiente per il seme di hash.
    random.seed(seed_value)  # Imposta il seme per il generatore di numeri casuali di Python.
    np.random.seed(seed_value)  # Imposta il seme per il generatore di numeri casuali di numpy.
    tf.random.set_seed(seed_value)  # Imposta il seme per il generatore di numeri casuali di tensorflow.

set_seed()  # Chiama la funzione per impostare il seme.

# Definizione dei link delle ruote
file_ruote = {  # Definisce un dizionario con i link dei file delle ruote.
    'BA': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/BARI.txt',  # Link per BARI.
    'CA': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/CAGLIARI.txt',  # Link per CAGLIARI.
    'FI': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/FIRENZE.txt',  # Link per FIRENZE.
    'GE': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/GENOVA.txt',  # Link per GENOVA.
    'MI': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/MILANO.txt',  # Link per MILANO.
    'NA': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/NAPOLI.txt',  # Link per NAPOLI.
    'PA': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/PALERMO.txt',  # Link per PALERMO.
    'RM': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/ROMA.txt',  # Link per ROMA.
    'TO': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/TORINO.txt',  # Link per TORINO.
    'VE': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/VENEZIA.txt',  # Link per VENEZIA.
    'NZ': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/NAZIONALE.txt'  # Link per NAZIONALE.
}

# Funzione per caricare i dati della ruota selezionata
def carica_dati(ruota, start_date=None, end_date=None):  # Definisce una funzione per caricare i dati.
    file_name = file_ruote.get(ruota)  # Ottiene il nome del file dalla ruota.
    if not file_name:  # Se il nome del file non esiste.
        logger.error("Ruota non trovata.")  # Logga un errore.
        messagebox.showerror("Errore", "Ruota non trovata.")  # Mostra un messaggio di errore.
        return None, None, None, None  # Restituisce None.

    try:  # Inizia un blocco try-except per la gestione degli errori.
        response = requests.get(file_name)  # Effettua una richiesta GET al link.
        response.raise_for_status()  # Solleva un'eccezione per errori HTTP.

        data = pd.read_csv(StringIO(response.text), header=None, sep="\t", encoding='utf-8')  # Legge i dati CSV.
        data.iloc[:, 0] = pd.to_datetime(data.iloc[:, 0], format='%Y/%m/%d')  # Converte la prima colonna in datetime.

        if start_date and end_date:  # Se sono state specificate date di inizio e fine.
            mask = (data.iloc[:, 0] >= start_date) & (data.iloc[:, 0] <= end_date)  # Crea una maschera per filtrare i dati.
            data = data.loc[mask]  # Applica la maschera ai dati.

        if data.empty:  # Se i dati sono vuoti.
            logger.error("Nessun dato trovato nell'intervallo di date specificato.")  # Logga un errore.
            messagebox.showerror("Errore", "Nessun dato trovato nell'intervallo di date specificato. Controlla le date di inizio e fine.")  # Mostra un messaggio di errore.
            return None, None, None, None  # Restituisce None.

        numeri = data.iloc[:, 2:].values  # Estrae i numeri dalle colonne.
        if numeri.size == 0:  # Se non ci sono numeri.
            logger.error("Nessun numero trovato nel file.")  # Logga un errore.
            return None, None, None, None  # Restituisce None.

        scaler = MinMaxScaler(feature_range=(0, 1))  # Crea un oggetto MinMaxScaler.
        numeri_normalizzati = scaler.fit_transform(numeri.astype(float))  # Normalizza i numeri.
        X = numeri_normalizzati[:-1]  # Crea i dati di input (X).
        y = numeri[:-1]  # Crea i dati di output (y).

        return X, y, scaler, data  # Restituisce X, y, scaler e data.
    except requests.exceptions.RequestException as e:  # Cattura le eccezioni di richiesta.
        logger.error(f"Errore durante il download dei dati dal link: {e}")  # Logga un errore.
        messagebox.showerror("Errore", f"Errore nel download dei dati dal link: {e}")  # Mostra un messaggio di errore.
        return None, None, None, None  # Restituisce None.
    except Exception as e:  # Cattura tutte le altre eccezioni.
        logger.error(f"Errore durante il caricamento dei dati: {e}")  # Logga un errore.
        messagebox.showerror("Errore", f"Errore durante il caricamento dei dati: {e}")  # Mostra un messaggio di errore.
        return None, None, None, None  # Restituisce None.

# Funzione di attivazione Swish
def swish(x):  # Definisce la funzione di attivazione Swish.
    return x * tf.keras.backend.sigmoid(x)  # Calcola e restituisce il valore di Swish.

# Variabili globali
optimizer_choice = None  # Inizializza la variabile per la scelta dell'ottimizzatore.
loss_function_choice = None  # Inizializza la variabile per la scelta della funzione di perdita.
patience = 10  # Imposta il valore di patience per EarlyStopping.
min_delta = 0.01  # Imposta il valore di min_delta per EarlyStopping.
progress_bar = None  # Inizializza la variabile per la barra di avanzamento.
fig = None  # Inizializza la variabile per la figura del grafico.
ax = None  # Inizializza la variabile per l'asse del grafico.
pulsanti_ruote = {}  # Inizializza il dizionario per i pulsanti delle ruote
entry_info = None  # Inizializza la variabile per il campo di input info
textbox = None  # Inizializza la variabile per il textbox di output

# Funzione per costruire il modello
def build_model(X_shape, y_shape, dense_layers, dropout_rates):  # Definisce la funzione per costruire il modello.
    model = Sequential()  # Crea un modello sequenziale.
    model.add(tf.keras.Input(shape=(X_shape,)))  # Aggiunge un layer di input.

    for i, (neurons, dropout) in enumerate(zip(dense_layers, dropout_rates)):  # Itera sui layer densi e sui dropout.
        logger.info(f"Layer {i+1}: Neuroni = {neurons}, Dropout = {dropout}")  # Logga le informazioni sul layer.

    for neurons, dropout in zip(dense_layers, dropout_rates):  # Itera sui layer densi e sui dropout.
        model.add(Dense(neurons, activation=swish))  # Aggiunge un layer denso con attivazione Swish.
        model.add(BatchNormalization())  # Aggiunge un layer di BatchNormalization.
        model.add(Dropout(dropout))  # Aggiunge un layer di Dropout.

    model.add(Dense(y_shape, activation=tf.keras.activations.relu))  # Aggiunge un layer denso di output.
    return model  # Restituisce il modello.

# Funzione per calcolare la prevedibilità delle ruote
def calcola_prevedibilita(history):  # Definisce la funzione per calcolare la prevedibilità.
    prevedibilita = {}  # Inizializza un dizionario per la prevedibilità.
    for ruota in history.keys():  # Itera sulle ruote.
        data_perdita = history[ruota]['val_loss']  # Ottiene i dati di perdita di validazione.
        if data_perdita:  # Se ci sono dati di perdita.
            prevedibilita[ruota] = np.mean(data_perdita)  # Calcola la media della perdita.
        else:  # Altrimenti.
            prevedibilita[ruota] = float('inf')  # Imposta la prevedibilità a infinito.
    return prevedibilita  # Restituisce il dizionario della prevedibilità.

# Funzione per mostrare la prevedibilità delle ruote
def mostra_prevedibilita(prevedibilita):  # Definisce la funzione per mostrare la prevedibilità.
    for child in frame_prevedibilita.winfo_children():  # Itera sui figli del frame della prevedibilità.
        child.destroy()  # Distrugge i figli.

    migliori_ruote = sorted(prevedibilita.items(), key=lambda x: x[1])[:2]  # Ordina le ruote per prevedibilità.
    ruote_string = ", ".join([ruota for ruota, _ in migliori_ruote])  # Crea una stringa con le migliori ruote.

    textbox.insert(tk.END, f"Le migliori ruote per la previsione sono: {ruote_string}\n")  # Inserisce il testo nel box di testo.

    fig, ax = plt.subplots(figsize=(10, 5))  # Crea un grafico.
    ax.bar(prevedibilita.keys(), prevedibilita.values())  # Crea un grafico a barre.
    ax.set_title('Prevedibilità delle Ruote (media perdite)')  # Imposta il titolo.
    ax.set_xlabel('Ruota')  # Imposta l'etichetta dell'asse x.
    ax.set_ylabel('Prevedibilità (Media Perdita)')  # Imposta l'etichetta dell'asse y.
    ax.grid(True)  # Aggiunge una griglia.

    canvas = FigureCanvasTkAgg(fig, master=frame_prevedibilita)  # Crea un canvas per il grafico.
    canvas.draw()  # Disegna il grafico.
    canvas.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH, expand=True)  # Inserisce il canvas nel frame.

# Funzione per aggiornare la barra di avanzamento e mostrare ogni selezione nel box di testo
def update_progress(value, max_value, text_to_append=None):  # Definisce la funzione per aggiornare la barra di avanzamento.
    if progress_bar:  # Se la barra di avanzamento esiste.
        progress_bar["maximum"] = max_value  # Imposta il valore massimo della barra.
        progress_bar["value"] = value  # Imposta il valore corrente della barra.
        root.update_idletasks()  # Aggiorna l'interfaccia grafica.
        
    if text_to_append and textbox:  # Se c'è del testo da aggiungere e il textbox esiste.
        textbox.insert(tk.END, text_to_append + "\n")  # Inserisce il testo nel box di testo.

# Funzione per gestire la selezione della ruota
def on_seleziona_ruota(ruota):  # Definisce la funzione per la selezione della ruota.
    global optimizer_choice, loss_function_choice, progress_bar, fig, ax  # Dichiara le variabili globali.
    start_date_str = entry_start_date.get()  # Ottiene la data di inizio.
    end_date_str = entry_end_date.get()  # Ottiene la data di fine.

    try:  # Inizia un blocco try-except.
        epochs = int(entry_epochs.get())  # Ottiene il numero di epoche.
        dense_layers = [  # Ottiene i layer densi.
            int(entry_neurons_layer1.get()),
            int(entry_neurons_layer2.get()),
            int(entry_neurons_layer3.get())
        ]
        dropout_rates = [  # Ottiene i tassi di dropout.
            float(entry_dropout_layer1.get()),
            float(entry_dropout_layer2.get()),
            float(entry_dropout_layer3.get())
        ]
    except ValueError:  # Cattura le eccezioni di valore.
        messagebox.showerror("Errore", "Inserisci valori validi per epoche, neuroni o dropout.")  # Mostra un messaggio di errore.
        return  # Esce dalla funzione.

    try:  # Inizia un blocco try-except.
        start_date = pd.to_datetime(start_date_str, format='%Y/%m/%d')  # Converte la data di inizio.
        end_date = pd.to_datetime(end_date_str, format='%Y/%m/%d')  # Converte la data di fine.
    except ValueError:  # Cattura le eccezioni di valore.
        messagebox.showerror("Errore", "Formato data non valido. Usa YYYY/MM/DD.")  # Mostra un messaggio di errore.
        return  # Esce dalla funzione.

    X, y, scaler, data = carica_dati(ruota, start_date, end_date)  # Carica i dati.

    if X is None or y is None or scaler is None or data is None:  # Se c'è un errore nel caricamento dei dati.
        return  # Esce dalla funzione.

    if X.size <= 0 or y.size <= 0:  # Se i dati sono vuoti.
        messagebox.showerror("Errore", "I dati caricati non contengono informazioni sufficienti per l'addestramento.")  # Mostra un messaggio di errore.
        return  # Esce dalla funzione.

    for btn in pulsanti_ruote.values():  # Itera sui pulsanti delle ruote.
        btn["bg"] = "SystemButtonFace"  # Imposta il colore di sfondo.
    pulsanti_ruote[ruota]["bg"] = "lightgreen"  # Imposta il colore di sfondo del pulsante selezionato.
    entry_info.delete(0, tk.END)  # Cancella il testo nel campo di input.
    entry_info.insert(0, f"Ruota: {ruota}, Periodo: {start_date.date()} - {end_date.date()}")  # Inserisce il testo nel campo di input.

    logger.info(f"Dimensione X: {X.shape}, Dimensione y: {y.shape}")  # Logga le dimensioni dei dati.

    k = min(5, len(X))  # Calcola il numero di fold per la validazione incrociata.
    kf = KFold(n_splits=k, shuffle=True, random_state=42)  # Crea un oggetto KFold.

    # Mostra informazioni di caricamento
    textbox.insert(tk.END, f"Caricamento dati per {ruota}...\n")  # Inserisce il testo nel box di testo.

    model = build_model(X.shape[1], y.shape[1], dense_layers, dropout_rates)  # Costruisce il modello.

    logger.info(f"Configurazione del Modello: Epoche = {epochs}, Ottimizzatore = {optimizer_choice}, Funzione di perdita = {loss_function_choice}")  # Logga la configurazione del modello.

    # Modifica qui per correggere l'errore di sintassi
    if optimizer_choice == '1':
        optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
    elif optimizer_choice == '2':
        optimizer = tf.keras.optimizers.RMSprop(learning_rate=0.001)
    else:
        optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)
    # Fine modifica

    model.compile(optimizer=optimizer, loss=loss_function_choice, metrics=["mae"])  # Compila il modello.

    all_hist_loss = []  # Inizializza la lista per la perdita.
    all_hist_val_loss = []  # Inizializza la lista per la perdita di validazione.
    history_per_ruota = {}  # Inizializza il dizionario per la cronologia.

    # Mostra informazioni di addestramento
    textbox.insert(tk.END, f"Addestramento per {ruota}...\n")  # Inserisce il testo nel box di testo.

    progress_bar = ttk.Progressbar(root, orient="horizontal", length=200, mode="determinate", style="blue.Horizontal.TProgressbar")  # Crea la barra di avanzamento.
    progress_bar.pack(pady=10)  # Inserisce la barra di avanzamento.

    style = ttk.Style()  # Crea un oggetto Style.
    style.theme_use('clam')  # Usa il tema clam.
    style.configure("blue.Horizontal.TProgressbar", foreground='blue', background='lightblue')  # Configura la barra di avanzamento.
    style.map("blue.Horizontal.TProgressbar",
              background=[('active', 'blue')])

    fold_count = k  # Ottiene il numero di fold.
    epoch_count = epochs  # Ottiene il numero di epoche.
    total_steps = fold_count * epoch_count  # Calcola il numero totale di passi.
    current_step = 0  # Inizializza il passo corrente.

    for train_index, test_index in kf.split(X):  # Itera sui fold.
        X_train, X_test = X[train_index], X[test_index]  # Ottiene i dati di addestramento e test.
        y_train, y_test = y[train_index], y[test_index]

        noise = np.random.normal(0, 0.01 * np.abs(X_train), X_train.shape)  # Aggiunge rumore ai dati di addestramento.
        X_train_noisy = X_train + noise

        early_stopping = EarlyStopping(monitor='val_loss', patience=patience, min_delta=min_delta, restore_best_weights=True)  # Crea un oggetto EarlyStopping.

        history = model.fit(X_train_noisy, y_train, epochs=epochs, batch_size=64, validation_data=(X_test, y_test),
                            callbacks=[early_stopping], verbose=0)  # Addestra il modello.

        all_hist_loss.append(history.history['loss'])  # Aggiunge la perdita alla lista.
        all_hist_val_loss.append(history.history['val_loss'])  # Aggiunge la perdita di validazione alla lista.
        history_per_ruota[ruota] = history.history  # Memorizza la cronologia.

        logger.info(f"Fine addestramento fold, Con perdita train: {history.history['loss'][-1]}, Val: {history.history['val_loss'][-1]}")  # Logga la fine dell'addestramento.

        current_step += epoch_count  # Incrementa il passo corrente.
        update_progress(current_step, total_steps, f"Addestramento completato {current_step}/{total_steps}")  # Aggiorna la barra di avanzamento.

    y_pred = model.predict(X_test[-len(X_test):])  # Effettua la predizione.
    scaler_output = MinMaxScaler(feature_range=(1, 90))  # Crea un oggetto MinMaxScaler.
    y_pred_scaled = scaler_output.fit_transform(y_pred)  # Scala la predizione.
    numeri_interi = np.round(y_pred_scaled).astype(int)  # Arrotonda i numeri.

    # Mostra informazioni di predizione
    textbox.insert(tk.END, f"Predizione per {ruota}...\n")  # Inserisce il testo nel box di testo.

    update_textbox(numeri_interi)  # Aggiorna il box di testo con i numeri predetti.
    mostra_numeri_predetti(numeri_interi)  # Mostra i numeri predetti.
    mostra_migliori_risultati(history)  # Mostra i migliori risultati.

    prevedibilita = calcola_prevedibilita(history_per_ruota)  # Calcola la prevedibilità.
    mostra_prevedibilita(prevedibilita)  # Mostra la prevedibilità.

    mostra_grafico(all_hist_loss, all_hist_val_loss)  # Mostra il grafico.

    if 'history' in locals():  # Controlla se la variabile history esiste.
        loss_to_val_loss_ratio = [val_train / val_val if val_val != 0 else float('inf')  # Calcola il rapporto tra perdita e perdita di validazione.
                                   for val_train, val_val in zip(history.history['loss'], history.history['val_loss'])]
        min_ratio_epoch = np.argmin(loss_to_val_loss_ratio)  # Trova l'epoca con il rapporto minimo.

        plt.figure(figsize=(12, 6))  # Crea un grafico.
        plt.plot(history.history['loss'], label='Loss Train')  # Disegna la perdita di addestramento.
        plt.plot(history.history['val_loss'], label='Loss Val')  # Disegna la perdita di validazione.
        plt.plot(loss_to_val_loss_ratio, label='Ratio Loss/Val')  # Disegna il rapporto tra perdita e perdita di validazione.
        plt.scatter(min_ratio_epoch, loss_to_val_loss_ratio[min_ratio_epoch],
                    label='Optimal Solution (Early Stopping)', color='red')  # Disegna il punto ottimale.
        plt.title('Andamento della Perdita durante l\'Addestramento e Rapporto')  # Imposta il titolo del grafico.
        plt.xlabel('Epoche')  # Imposta l'etichetta dell'asse x.
        plt.ylabel('Perdita')  # Imposta l'etichetta dell'asse y.
        plt.axhline(0, color='black', linewidth=0.8, linestyle='--')  # Disegna una linea orizzontale.
        plt.legend()  # Aggiunge una legenda.
        plt.grid(True)  # Aggiunge una griglia.
        plt.show()  # Mostra il grafico.

    if progress_bar:  # Se la barra di avanzamento esiste.
        progress_bar.destroy()  # Distrugge la barra di avanzamento.
        progress_bar = None  # Imposta la barra di avanzamento a None.

    logger.info(f"Fine addestramento modello per {ruota}.")  # Logga la fine dell'addestramento.

def mostra_grafico(all_hist_loss, all_hist_val_loss):  # Definisce la funzione per mostrare il grafico.
    global fig, ax  # Dichiara le variabili globali.
    for child in frame_grafico.winfo_children():  # Itera sui figli del frame del grafico.
        child.destroy()  # Distrugge i figli.

    fig, ax = plt.subplots(figsize=(12, 10))  # Crea un grafico.

    for fold_idx in range(len(all_hist_loss)):  # Itera sui fold.
        ax.plot(all_hist_loss[fold_idx], label=f'Fold {fold_idx + 1} - Loss Train', linestyle='-')  # Disegna la perdita di addestramento.
        ax.plot(all_hist_val_loss[fold_idx], label=f'Fold {fold_idx + 1} - Loss Val', linestyle='--')  # Disegna la perdita di validazione.

    ax.set_title('Andamento della Perdita durante l\'Addestramento (K-Fold)')  # Imposta il titolo del grafico.
    ax.set_xlabel('Epoche')  # Imposta l'etichetta dell'asse x.
    ax.set_ylabel('Perdita')  # Imposta l'etichetta dell'asse y.
    ax.grid(True)  # Aggiunge una griglia.

    canvas = FigureCanvasTkAgg(fig, master=frame_grafico)  # Crea un canvas per il grafico.
    canvas.draw()  # Disegna il grafico.
    canvas.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH, expand=True)  # Inserisce il canvas nel frame.
    legend = ax.legend(loc='center left', bbox_to_anchor=(1, 0.5), fontsize='small')  # Aggiunge una legenda.
    fig.canvas.draw()  # Disegna il grafico.
    plt.close(fig)  # Chiude la figura.

def select_optimizer(opt):  # Definisce la funzione per selezionare l'ottimizzatore.
    global optimizer_choice  # Dichiara la variabile globale.
    optimizer_choice = opt  # Imposta l'ottimizzatore.
    logger.info(f"Ottimizzatore selezionato: {optimizer_choice}")  # Logga l'ottimizzatore selezionato.

def select_loss_function(loss_func):  # Definisce la funzione per selezionare la funzione di perdita.
    global loss_function_choice  # Dichiara la variabile globale.
    loss_function_choice = 'mean_squared_error' if loss_func == 'mse' else 'mean_absolute_error'  # Imposta la funzione di perdita.
    logger.info(f"Funzione di perdita selezionata: {loss_function_choice}")  # Logga la funzione di perdita selezionata.

def select_patience(patience_value):  # Definisce la funzione per selezionare il valore di patience.
    global patience  # Dichiara la variabile globale.
    patience = patience_value  # Imposta il valore di patience.
    logger.info(f"Patience impostato a: {patience}")  # Logga il valore di patience.

def select_min_delta(min_delta_value):  # Definisce la funzione per selezionare il valore di min_delta.
    global min_delta  # Dichiara la variabile globale.
    min_delta = min_delta_value  # Imposta il valore di min_delta.
    logger.info(f"Min Delta impostato a: {min_delta}")  # Logga il valore di min_delta.

def update_textbox(numeri):  # Definisce la funzione per aggiornare il box di testo con i numeri predetti.
    textbox.insert(tk.END, "Numeri Predetti:\n")  # Inserisce il testo nel box di testo.
    numeri_limitati = numeri.flatten()[:5]  # Estrae i primi 5 numeri.
    textbox.insert(tk.END, ", ".join(map(str, numeri_limitati)) + "\n")  # Inserisce i numeri nel box di testo.

def mostra_numeri_predetti(numeri):  # Definisce la funzione per mostrare i numeri predetti.
    textbox.insert(tk.END, "Numeri Predetti:\n")  # Inserisce il testo nel box di testo.
    numeri_limitati = numeri.flatten()[:5]  # Estrae i primi 5 numeri.
    textbox.insert(tk.END, ", ".join(map(str, numeri_limitati)) + "\n")  # Inserisce i numeri nel box di testo.

def mostra_migliori_risultati(history):  # Definisce la funzione per mostrare i migliori risultati.
    final_train_loss = history.history['loss'][-1]  # Ottiene la perdita di addestramento finale.
    final_val_loss = history.history['val_loss'][-1]  # Ottiene la perdita di validazione finale.
    textbox.insert(tk.END, f"Ultima Loss Train: {final_train_loss:.4f}\n")  # Inserisce il testo nel box di testo.
    textbox.insert(tk.END, f"Ultima Loss Val: {final_val_loss:.4f}\n")  # Inserisce il testo nel box di testo.

def salva_grafico(nome_file='grafico.png'):  # Definisce la funzione per salvare il grafico.
    if fig:  # Se la figura esiste.
        fig.savefig(nome_file)  # Salva la figura.
        messagebox.showinfo("Successo", f"Grafico salvato come {nome_file}")  # Mostra un messaggio di successo.
    else:  # Altrimenti.
        messagebox.showerror("Errore", "Nessun grafico disponibile per il salvataggio.")  # Mostra un messaggio di errore.

def salva_risultati(nome_file='risultati.txt'):  # Definisce la funzione per salvare i risultati.
    try:  # Inizia un blocco try-except.
        # Apre il file dialog per selezionare la posizione di salvataggio
        file_path = filedialog.asksaveasfilename(defaultextension=".txt",
                                                   filetypes=[("Text files", "*.txt"), ("All files", "*.*")])

        if file_path:  # Se l'utente ha selezionato un file.
            with open(file_path, 'w') as file:  # Apre il file.
                file.write(textbox.get(1.0, tk.END))  # Scrive il contenuto del box di testo nel file.
            messagebox.showinfo("Successo", f"Risultati salvati in {file_path}")  # Mostra un messaggio di successo.
        else:
            messagebox.showinfo("Info", "Salvataggio cancellato dall'utente.")

    except Exception as e:  # Cattura tutte le eccezioni.
        messagebox.showerror("Errore", f"Errore nel salvataggio dei risultati: {e}")  # Mostra un messaggio di errore.

# Creazione della finestra principale
root = tk.Tk()  # Crea la finestra principale.
root.geometry("1200x1250")  # Imposta la dimensione della finestra.

# Crea una cornice per contenere l'etichetta del titolo e altri elementi, se necessario.
title_frame = tk.Frame(root)
title_frame.pack(pady=10)  # Aggiunge un po' di spazio verticale attorno alla cornice

# Crea l'etichetta del titolo con il colore desiderato.
title_label = tk.Label(title_frame, text="𝔼𝕞𝕡𝕒𝕥𝕙𝕚𝕩", fg="blue", font=("Arial", 24))  # Scegli il colore e il font
title_label.pack() # Posiziona l'etichetta all'interno della cornice


# Frame per i pulsanti di salvataggio
frame_salvataggio = tk.Frame(root)  # Crea un frame per i pulsanti di salvat  # Crea un frame per i pulsanti di salvataggio.
frame_salvataggio.pack(pady=10)  # Inserisce il frame nella finestra.

# Pulsanti per il salvataggio
btn_salva_grafico = tk.Button(frame_salvataggio, text="Salva Grafico", command=lambda: salva_grafico('grafico.png'), bg="#FFDDC1")  # Crea il pulsante per salvare il grafico.
btn_salva_grafico.pack(side=tk.LEFT, padx=10)  # Inserisce il pulsante nel frame.

btn_salva_risultati = tk.Button(frame_salvataggio, text="Salva Risultati", command=lambda: salva_risultati('risultati.txt'), bg="#FFDDC1") 
btn_salva_risultati.pack(side=tk.LEFT, padx=10)

# Creazione del frame per i pulsanti
frame_pulsanti = tk.Frame(root)
frame_pulsanti.pack(pady=20)

# Creazione dei pulsanti per la selezione delle ruote
pulsanti_ruote = {}
for ruota in file_ruote.keys():
    btn = tk.Button(frame_pulsanti, text=ruota, command=lambda r=ruota: on_seleziona_ruota(r), bg="#ADD8E6", fg="black")
    btn.pack(side=tk.LEFT, padx=5)
    pulsanti_ruote[ruota] = btn

# Campo di input per la ruota selezionata e il periodo
entry_info = tk.Entry(frame_pulsanti, width=100, bg="#F0F0F0", fg="black")
entry_info.pack(pady=10)

# Campi di input per le date
frame_date = tk.Frame(frame_pulsanti)
frame_date.pack(pady=20)

label_start_date = tk.Label(frame_date, text="Data di Inizio (YYYY/MM/DD):", bg="#ADD8E6", fg="black")
label_start_date.pack(side=tk.LEFT)
entry_start_date = tk.Entry(frame_date, width=15, bg="#F0F0F0", fg="black")
entry_start_date.pack(side=tk.LEFT)

label_end_date = tk.Label(frame_date, text="Data di Fine (YYYY/MM/DD):", bg="#ADD8E6", fg="black")
label_end_date.pack(side=tk.LEFT)
entry_end_date = tk.Entry(frame_date, width=15, bg="#F0F0F0", fg="black")
entry_end_date.pack(side=tk.LEFT)

# Campo per specificare il numero di epoche
frame_epochs = tk.Frame(frame_pulsanti)
frame_epochs.pack(pady=20)

label_epochs = tk.Label(frame_epochs, text="Numero di Epoche:", bg="#ADD8E6", fg="black")
label_epochs.pack(side=tk.LEFT)
entry_epochs = tk.Entry(frame_epochs, width=5, bg="#F0F0F0", fg="black")
entry_epochs.insert(0, "100")
entry_epochs.pack(side=tk.LEFT)

# Sezione per le opzioni di Early Stopping
frame_early_stopping = tk.Frame(root)
frame_early_stopping.pack(pady=20)

label_early_stopping = tk.Label(frame_early_stopping, text="Opzioni Early Stopping:", bg="#ADD8E6", fg="black")
label_early_stopping.pack(side=tk.TOP)

btn_patience_5 = tk.Button(frame_early_stopping, text="Patience: 5", command=lambda: select_patience(5), bg="#C9E4CA")
btn_patience_5.pack(side=tk.LEFT, padx=5)

btn_patience_10 = tk.Button(frame_early_stopping, text="Patience: 10", command=lambda: select_patience(10), bg="#C9E4CA")
btn_patience_10.pack(side=tk.LEFT, padx=5)

btn_patience_15 = tk.Button(frame_early_stopping, text="Patience: 15", command=lambda: select_patience(15), bg="#C9E4CA")
btn_patience_15.pack(side=tk.LEFT, padx=5)

btn_min_delta_01 = tk.Button(frame_early_stopping, text="Min Delta: 0.01", command=lambda: select_min_delta(0.01), bg="#C9E4CA")
btn_min_delta_01.pack(side=tk.LEFT, padx=5)

btn_min_delta_05 = tk.Button(frame_early_stopping, text="Min Delta: 0.05", command=lambda: select_min_delta(0.05), bg="#C9E4CA")
btn_min_delta_05.pack(side=tk.LEFT, padx=5)

btn_min_delta_1 = tk.Button(frame_early_stopping, text="Min Delta: 0.10", command=lambda: select_min_delta(0.1), bg="#C9E4CA")
btn_min_delta_1.pack(side=tk.LEFT, padx=5)

# Aggiunta di campi per i neuroni e i tassi di dropout
frame_model_parameters = tk.Frame(frame_pulsanti)
frame_model_parameters.pack(pady=20)

label_layer1 = tk.Label(frame_model_parameters, text="Layer 1 Neuroni:", bg="#ADD8E6", fg="black")
label_layer1.pack(side=tk.LEFT)
entry_neurons_layer1 = tk.Entry(frame_model_parameters, width=5, bg="#F0F0F0", fg="black")
entry_neurons_layer1.insert(0, "512")
entry_neurons_layer1.pack(side=tk.LEFT)

label_dropout_layer1 = tk.Label(frame_model_parameters, text="Layer 1 Dropout:", bg="#ADD8E6", fg="black")
label_dropout_layer1.pack(side=tk.LEFT)
entry_dropout_layer1 = tk.Entry(frame_model_parameters, width=5, bg="#F0F0F0", fg="black")
entry_dropout_layer1.insert(0, "0.2")
entry_dropout_layer1.pack(side=tk.LEFT)

label_layer2 = tk.Label(frame_model_parameters, text="Layer 2 Neuroni:", bg="#ADD8E6", fg="black")
label_layer2.pack(side=tk.LEFT)
entry_neurons_layer2 = tk.Entry(frame_model_parameters, width=5, bg="#F0F0F0", fg="black")
entry_neurons_layer2.insert(0, "256")
entry_neurons_layer2.pack(side=tk.LEFT)

label_dropout_layer2 = tk.Label(frame_model_parameters, text="Layer 2 Dropout:", bg="#ADD8E6", fg="black")
label_dropout_layer2.pack(side=tk.LEFT)
entry_dropout_layer2 = tk.Entry(frame_model_parameters, width=5, bg="#F0F0F0", fg="black")
entry_dropout_layer2.insert(0, "0.2")  # Valore predefinito
entry_dropout_layer2.pack(side=tk.LEFT)

label_layer3 = tk.Label(frame_model_parameters, text="Layer 3 Neuroni:", bg="#ADD8E6", fg="black")
label_layer3.pack(side=tk.LEFT)
entry_neurons_layer3 = tk.Entry(frame_model_parameters, width=5, bg="#F0F0F0", fg="black")
entry_neurons_layer3.insert(0, "128")  # Valore predefinito
entry_neurons_layer3.pack(side=tk.LEFT)

label_dropout_layer3 = tk.Label(frame_model_parameters, text="Layer 3 Dropout:", bg="#ADD8E6", fg="black")
label_dropout_layer3.pack(side=tk.LEFT)
entry_dropout_layer3 = tk.Entry(frame_model_parameters, width=5, bg="#F0F0F0", fg="black")
entry_dropout_layer3.insert(0, "0.2")  # Valore predefinito
entry_dropout_layer3.pack(side=tk.LEFT)

# Frame per i pulsanti di ottimizzazione
frame_optimizer = tk.Frame(frame_pulsanti)
frame_optimizer.pack(side=tk.RIGHT, padx=20)

btn_optimizer_1 = tk.Button(frame_optimizer, text=" 1 (Adam)", command=lambda: select_optimizer('1'), bg="#C9E4CA", fg="black")
btn_optimizer_1.pack(pady=5)

btn_optimizer_2 = tk.Button(frame_optimizer, text=" 2 (RMSprop)", command=lambda: select_optimizer('2'), bg="#C9E4CA", fg="black")
btn_optimizer_2.pack(pady=5)

btn_optimizer_3 = tk.Button(frame_optimizer, text=" 3 (SGD)", command=lambda: select_optimizer('3'), bg="#C9E4CA", fg="black")
btn_optimizer_3.pack(pady=5)

# Frame per i pulsanti della funzione di perdita
frame_loss_function = tk.Frame(frame_pulsanti)
frame_loss_function.pack(side=tk.RIGHT, padx=20)

btn_loss_mse = tk.Button(frame_loss_function, text="Loss: MSE", command=lambda: select_loss_function('mse'), bg="#C9E4CA", fg="black")
btn_loss_mse.pack(pady=5)

btn_loss_mae = tk.Button(frame_loss_function, text="Loss: MAE", command=lambda: select_loss_function('mae'), bg="#C9E4CA", fg="black")
btn_loss_mae.pack(pady=5)

# Frame principale per il box di testo e la scrollbar
text_frame = tk.Frame(root)
text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=10)

# Scrollbar verticale
scrollbar = ttk.Scrollbar(text_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

# Scrollbar orizzontale
scrollbar_x = ttk.Scrollbar(text_frame, orient=tk.HORIZONTAL)
scrollbar_x.pack(side=tk.BOTTOM, fill=tk.X)

# Creazione del box di testo con scrollbar
textbox = tk.Text(text_frame, height=10, width=90, wrap=tk.NONE, yscrollcommand=scrollbar.set, xscrollcommand=scrollbar_x.set)
textbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

# Configura la scrollbar
scrollbar.config(command=textbox.yview)
scrollbar_x.config(command=textbox.xview)

# Frame per il grafico
frame_grafico = tk.Frame(root)
frame_grafico.pack(pady=10)

# Frame per la prevedibilità
frame_prevedibilita = tk.Frame(root)
frame_prevedibilita.pack(pady=10)

# Creazione e visualizzazione della finestra
root.mainloop()

# Apre il file di log alla chiusura
open_log_file()

Codice:
Ecco le librerie necessarie in un'unica riga di installazione:

CopyReplit
pip install pandas numpy scikit-learn tensorflow matplotlib tkinter requests wheel


💥
Download Empathix.py
 
.. vi presento

Screenshot-10.png

Codice:
Empathix è un'applicazione GUI (Graphical User Interface) realizzata in Python utilizzando le librerie pandas, numpy, scikit-learn, tensorflow, tkinter e matplotlib. L'applicazione è progettata per analizzare i dati delle estrazioni del lotto, costruire modelli di reti neurali e prevedere i numeri.

**Funzioni principali:**

1.  **Caricamento dei dati:**
    *   Scarica i dati delle estrazioni del lotto da repository GitHub per diverse ruote (es. Bari, Cagliari, Milano).
    *   Permette di specificare un intervallo di date per filtrare i dati.
    *   Normalizza i dati numerici usando `MinMaxScaler`.

2.  **Costruzione del modello di rete neurale:**
    *   Utilizza TensorFlow per costruire un modello sequenziale (`Sequential`).
    *   Definisce una funzione `build_model` che crea una rete neurale con:
        *   Layer di input.
        *   Layer densi (`Dense`) con attivazione `Swish` e `BatchNormalization` e `Dropout`.
        *   Layer di output (`Dense`) con attivazione `relu`.
    *   Permette di configurare il numero di layer densi, il numero di neuroni per layer e i tassi di dropout.
    *   Supporta diversi ottimizzatori (`Adam`, `RMSprop`, `SGD`) e funzioni di perdita (`MSE`, `MAE`).

3.  **Addestramento del modello:**
    *   Utilizza la validazione incrociata K-Fold (`KFold`) per valutare le prestazioni del modello in modo più robusto.
    *   Applica `EarlyStopping` per interrompere l'addestramento se le prestazioni del modello non migliorano per un certo numero di epoche (pazienza).
    *   Aggiunge rumore ai dati di addestramento per migliorare la generalizzazione.
    *   Monitora la perdita durante l'addestramento e la validazione.

4.  **Previsione:**
    *   Utilizza il modello addestrato per fare previsioni sui numeri del lotto.
    *   Normalizza i dati di output (previsioni) e li converte in numeri interi.

5.  **Visualizzazione e analisi dei risultati:**
    *   Mostra grafici dell'andamento della perdita durante l'addestramento e la validazione.
    *   Calcola e mostra la "prevedibilità" delle ruote, basata sulla media della perdita di validazione.
    *   Mostra i numeri previsti.
    *   Permette di salvare i grafici e i risultati in file.

6.  **Interfaccia grafica (GUI):**
    *   Utilizza `tkinter` per creare un'interfaccia utente.
    *   Include elementi come pulsanti per selezionare le ruote, campi di input per le date, i parametri del modello (epoche, neuroni, dropout, ottimizzatori, funzione di perdita), una barra di avanzamento e un box di testo per visualizzare i risultati e messaggi di log.

7.  **Logging:**
    *   Utilizza il modulo `logging` per registrare eventi, errori e informazioni utili durante l'esecuzione del programma.
    *   Un handler di logging personalizzato (`TextHandler`) indirizza i messaggi di log al box di testo della GUI.

**Implementazioni specifiche:**

*   **Funzione di attivazione Swish:** `swish(x)`.
*   **Funzioni per la selezione e configurazione:**
    *   `select_optimizer`, `select_loss_function`, `select_patience`, `select_min_delta`.
*   **Funzioni per l'aggiornamento dell'interfaccia:**
    *   `update_progress`, `update_textbox`, `mostra_numeri_predetti`, `mostra_migliori_risultati`, `mostra_grafico`, `mostra_prevedibilita`.
*   **Funzioni per il salvataggio:**
    *   `salva_grafico`, `salva_risultati`.
*   **Semina per la riproducibilità:** `set_seed()`.
*   **Gestione dei pulsanti e delle selezioni:** `on_seleziona_ruota()`.
*   **Organizzazione dell'interfaccia:** utilizzo di `Frame` per raggruppare i widget.

In sostanza, Empathix è uno strumento completo per l'analisi dei dati del lotto e la sperimentazione con modelli di reti neurali per la previsione dei numeri, con un'interfaccia utente che ne facilita l'uso e la comprensione.



Codice:
import pandas as pd  # Importa la libreria pandas per la gestione dei dati.
import numpy as np  # Importa la libreria numpy per operazioni numeriche.
from sklearn.model_selection import KFold  # Importa KFold per la validazione incrociata.
from sklearn.preprocessing import MinMaxScaler  # Importa MinMaxScaler per la normalizzazione dei dati.
import tensorflow as tf  # Importa tensorflow per la costruzione del modello di rete neurale.
from tensorflow.keras.models import Sequential  # Importa Sequential per la costruzione del modello sequenziale.
from tensorflow.keras.layers import Dense, Dropout, BatchNormalization  # Importa le classi di layer.
from tensorflow.keras.callbacks import EarlyStopping  # Importa EarlyStopping per l'arresto anticipato.
import tkinter as tk  # Importa tkinter per l'interfaccia grafica.
from tkinter import messagebox, ttk, scrolledtext, filedialog  # Importa messagebox e ttk per widget specifici.
import os  # Importa os per interazioni con il sistema operativo.
import random  # Importa random per generare numeri casuali.
import matplotlib.pyplot as plt  # Importa matplotlib per la visualizzazione dei grafici.
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg  # Importa FigureCanvasTkAgg per integrare matplotlib con tkinter.
import logging  # Importa logging per il logging degli eventi.
import requests  # Importa requests per effettuare richieste HTTP.
from io import StringIO  # Importa StringIO per la gestione di stringhe come file.
import time  # Importa time per la gestione del tempo.

# Configurazione del logger
logger = logging.getLogger(__name__)  # Ottiene un logger con il nome del modulo.
logger.setLevel(logging.INFO)  # Imposta il livello di logging a INFO.

# Crea un handler per il box di testo
class TextHandler(logging.Handler):  # Definisce una classe per gestire il logging nel box di testo.
    def __init__(self, text_widget):  # Costruttore della classe.
        logging.Handler.__init__(self)  # Chiama il costruttore della classe base.
        self.text_widget = text_widget  # Memorizza il widget di testo.

    def emit(self, record):  # Metodo per emettere un messaggio di log.
        msg = self.format(record)  # Formatta il record di log.
        self.text_widget.config(state=tk.NORMAL)  # Abilita la modifica del widget di testo.
        self.text_widget.insert(tk.END, msg + '\n')  # Inserisce il messaggio di log.
        self.text_widget.config(state=tk.DISABLED)  # Disabilita la modifica del widget di testo.
        self.text_widget.see(tk.END)  # Fa scorrere il widget di testo fino alla fine.

# Imposta il seme per la riproducibilità
def set_seed(seed_value=42):  # Definisce una funzione per impostare il seme per la riproducibilità.
    os.environ['PYTHONHASHSEED'] = str(seed_value)  # Imposta una variabile d'ambiente per il seme di hash.
    random.seed(seed_value)  # Imposta il seme per il generatore di numeri casuali di Python.
    np.random.seed(seed_value)  # Imposta il seme per il generatore di numeri casuali di numpy.
    tf.random.set_seed(seed_value)  # Imposta il seme per il generatore di numeri casuali di tensorflow.

set_seed()  # Chiama la funzione per impostare il seme.

# Definizione dei link delle ruote
file_ruote = {  # Definisce un dizionario con i link dei file delle ruote.
    'BA': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/BARI.txt',  # Link per BARI.
    'CA': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/CAGLIARI.txt',  # Link per CAGLIARI.
    'FI': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/FIRENZE.txt',  # Link per FIRENZE.
    'GE': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/GENOVA.txt',  # Link per GENOVA.
    'MI': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/MILANO.txt',  # Link per MILANO.
    'NA': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/NAPOLI.txt',  # Link per NAPOLI.
    'PA': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/PALERMO.txt',  # Link per PALERMO.
    'RM': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/ROMA.txt',  # Link per ROMA.
    'TO': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/TORINO.txt',  # Link per TORINO.
    'VE': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/VENEZIA.txt',  # Link per VENEZIA.
    'NZ': 'https://raw.githubusercontent.com/Lottopython/estrazioni/refs/heads/main/NAZIONALE.txt'  # Link per NAZIONALE.
}

# Funzione per caricare i dati della ruota selezionata
def carica_dati(ruota, start_date=None, end_date=None):  # Definisce una funzione per caricare i dati.
    file_name = file_ruote.get(ruota)  # Ottiene il nome del file dalla ruota.
    if not file_name:  # Se il nome del file non esiste.
        logger.error("Ruota non trovata.")  # Logga un errore.
        messagebox.showerror("Errore", "Ruota non trovata.")  # Mostra un messaggio di errore.
        return None, None, None, None  # Restituisce None.

    try:  # Inizia un blocco try-except per la gestione degli errori.
        response = requests.get(file_name)  # Effettua una richiesta GET al link.
        response.raise_for_status()  # Solleva un'eccezione per errori HTTP.

        data = pd.read_csv(StringIO(response.text), header=None, sep="\t", encoding='utf-8')  # Legge i dati CSV.
        data.iloc[:, 0] = pd.to_datetime(data.iloc[:, 0], format='%Y/%m/%d')  # Converte la prima colonna in datetime.

        if start_date and end_date:  # Se sono state specificate date di inizio e fine.
            mask = (data.iloc[:, 0] >= start_date) & (data.iloc[:, 0] <= end_date)  # Crea una maschera per filtrare i dati.
            data = data.loc[mask]  # Applica la maschera ai dati.

        if data.empty:  # Se i dati sono vuoti.
            logger.error("Nessun dato trovato nell'intervallo di date specificato.")  # Logga un errore.
            messagebox.showerror("Errore", "Nessun dato trovato nell'intervallo di date specificato. Controlla le date di inizio e fine.")  # Mostra un messaggio di errore.
            return None, None, None, None  # Restituisce None.

        numeri = data.iloc[:, 2:].values  # Estrae i numeri dalle colonne.
        if numeri.size == 0:  # Se non ci sono numeri.
            logger.error("Nessun numero trovato nel file.")  # Logga un errore.
            return None, None, None, None  # Restituisce None.

        scaler = MinMaxScaler(feature_range=(0, 1))  # Crea un oggetto MinMaxScaler.
        numeri_normalizzati = scaler.fit_transform(numeri.astype(float))  # Normalizza i numeri.
        X = numeri_normalizzati[:-1]  # Crea i dati di input (X).
        y = numeri[:-1]  # Crea i dati di output (y).

        return X, y, scaler, data  # Restituisce X, y, scaler e data.
    except requests.exceptions.RequestException as e:  # Cattura le eccezioni di richiesta.
        logger.error(f"Errore durante il download dei dati dal link: {e}")  # Logga un errore.
        messagebox.showerror("Errore", f"Errore nel download dei dati dal link: {e}")  # Mostra un messaggio di errore.
        return None, None, None, None  # Restituisce None.
    except Exception as e:  # Cattura tutte le altre eccezioni.
        logger.error(f"Errore durante il caricamento dei dati: {e}")  # Logga un errore.
        messagebox.showerror("Errore", f"Errore durante il caricamento dei dati: {e}")  # Mostra un messaggio di errore.
        return None, None, None, None  # Restituisce None.

# Funzione di attivazione Swish
def swish(x):  # Definisce la funzione di attivazione Swish.
    return x * tf.keras.backend.sigmoid(x)  # Calcola e restituisce il valore di Swish.

# Variabili globali
optimizer_choice = None  # Inizializza la variabile per la scelta dell'ottimizzatore.
loss_function_choice = None  # Inizializza la variabile per la scelta della funzione di perdita.
patience = 10  # Imposta il valore di patience per EarlyStopping.
min_delta = 0.01  # Imposta il valore di min_delta per EarlyStopping.
progress_bar = None  # Inizializza la variabile per la barra di avanzamento.
fig = None  # Inizializza la variabile per la figura del grafico.
ax = None  # Inizializza la variabile per l'asse del grafico.
pulsanti_ruote = {}  # Inizializza il dizionario per i pulsanti delle ruote
entry_info = None  # Inizializza la variabile per il campo di input info
textbox = None  # Inizializza la variabile per il textbox di output

# Funzione per costruire il modello
def build_model(X_shape, y_shape, dense_layers, dropout_rates):  # Definisce la funzione per costruire il modello.
    model = Sequential()  # Crea un modello sequenziale.
    model.add(tf.keras.Input(shape=(X_shape,)))  # Aggiunge un layer di input.

    for i, (neurons, dropout) in enumerate(zip(dense_layers, dropout_rates)):  # Itera sui layer densi e sui dropout.
        logger.info(f"Layer {i+1}: Neuroni = {neurons}, Dropout = {dropout}")  # Logga le informazioni sul layer.

    for neurons, dropout in zip(dense_layers, dropout_rates):  # Itera sui layer densi e sui dropout.
        model.add(Dense(neurons, activation=swish))  # Aggiunge un layer denso con attivazione Swish.
        model.add(BatchNormalization())  # Aggiunge un layer di BatchNormalization.
        model.add(Dropout(dropout))  # Aggiunge un layer di Dropout.

    model.add(Dense(y_shape, activation=tf.keras.activations.relu))  # Aggiunge un layer denso di output.
    return model  # Restituisce il modello.

# Funzione per calcolare la prevedibilità delle ruote
def calcola_prevedibilita(history):  # Definisce la funzione per calcolare la prevedibilità.
    prevedibilita = {}  # Inizializza un dizionario per la prevedibilità.
    for ruota in history.keys():  # Itera sulle ruote.
        data_perdita = history[ruota]['val_loss']  # Ottiene i dati di perdita di validazione.
        if data_perdita:  # Se ci sono dati di perdita.
            prevedibilita[ruota] = np.mean(data_perdita)  # Calcola la media della perdita.
        else:  # Altrimenti.
            prevedibilita[ruota] = float('inf')  # Imposta la prevedibilità a infinito.
    return prevedibilita  # Restituisce il dizionario della prevedibilità.

# Funzione per mostrare la prevedibilità delle ruote
def mostra_prevedibilita(prevedibilita):  # Definisce la funzione per mostrare la prevedibilità.
    for child in frame_prevedibilita.winfo_children():  # Itera sui figli del frame della prevedibilità.
        child.destroy()  # Distrugge i figli.

    migliori_ruote = sorted(prevedibilita.items(), key=lambda x: x[1])[:2]  # Ordina le ruote per prevedibilità.
    ruote_string = ", ".join([ruota for ruota, _ in migliori_ruote])  # Crea una stringa con le migliori ruote.

    textbox.insert(tk.END, f"Le migliori ruote per la previsione sono: {ruote_string}\n")  # Inserisce il testo nel box di testo.

    fig, ax = plt.subplots(figsize=(10, 5))  # Crea un grafico.
    ax.bar(prevedibilita.keys(), prevedibilita.values())  # Crea un grafico a barre.
    ax.set_title('Prevedibilità delle Ruote (media perdite)')  # Imposta il titolo.
    ax.set_xlabel('Ruota')  # Imposta l'etichetta dell'asse x.
    ax.set_ylabel('Prevedibilità (Media Perdita)')  # Imposta l'etichetta dell'asse y.
    ax.grid(True)  # Aggiunge una griglia.

    canvas = FigureCanvasTkAgg(fig, master=frame_prevedibilita)  # Crea un canvas per il grafico.
    canvas.draw()  # Disegna il grafico.
    canvas.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH, expand=True)  # Inserisce il canvas nel frame.

# Funzione per aggiornare la barra di avanzamento e mostrare ogni selezione nel box di testo
def update_progress(value, max_value, text_to_append=None):  # Definisce la funzione per aggiornare la barra di avanzamento.
    if progress_bar:  # Se la barra di avanzamento esiste.
        progress_bar["maximum"] = max_value  # Imposta il valore massimo della barra.
        progress_bar["value"] = value  # Imposta il valore corrente della barra.
        root.update_idletasks()  # Aggiorna l'interfaccia grafica.
       
    if text_to_append and textbox:  # Se c'è del testo da aggiungere e il textbox esiste.
        textbox.insert(tk.END, text_to_append + "\n")  # Inserisce il testo nel box di testo.

# Funzione per gestire la selezione della ruota
def on_seleziona_ruota(ruota):  # Definisce la funzione per la selezione della ruota.
    global optimizer_choice, loss_function_choice, progress_bar, fig, ax  # Dichiara le variabili globali.
    start_date_str = entry_start_date.get()  # Ottiene la data di inizio.
    end_date_str = entry_end_date.get()  # Ottiene la data di fine.

    try:  # Inizia un blocco try-except.
        epochs = int(entry_epochs.get())  # Ottiene il numero di epoche.
        dense_layers = [  # Ottiene i layer densi.
            int(entry_neurons_layer1.get()),
            int(entry_neurons_layer2.get()),
            int(entry_neurons_layer3.get())
        ]
        dropout_rates = [  # Ottiene i tassi di dropout.
            float(entry_dropout_layer1.get()),
            float(entry_dropout_layer2.get()),
            float(entry_dropout_layer3.get())
        ]
    except ValueError:  # Cattura le eccezioni di valore.
        messagebox.showerror("Errore", "Inserisci valori validi per epoche, neuroni o dropout.")  # Mostra un messaggio di errore.
        return  # Esce dalla funzione.

    try:  # Inizia un blocco try-except.
        start_date = pd.to_datetime(start_date_str, format='%Y/%m/%d')  # Converte la data di inizio.
        end_date = pd.to_datetime(end_date_str, format='%Y/%m/%d')  # Converte la data di fine.
    except ValueError:  # Cattura le eccezioni di valore.
        messagebox.showerror("Errore", "Formato data non valido. Usa YYYY/MM/DD.")  # Mostra un messaggio di errore.
        return  # Esce dalla funzione.

    X, y, scaler, data = carica_dati(ruota, start_date, end_date)  # Carica i dati.

    if X is None or y is None or scaler is None or data is None:  # Se c'è un errore nel caricamento dei dati.
        return  # Esce dalla funzione.

    if X.size <= 0 or y.size <= 0:  # Se i dati sono vuoti.
        messagebox.showerror("Errore", "I dati caricati non contengono informazioni sufficienti per l'addestramento.")  # Mostra un messaggio di errore.
        return  # Esce dalla funzione.

    for btn in pulsanti_ruote.values():  # Itera sui pulsanti delle ruote.
        btn["bg"] = "SystemButtonFace"  # Imposta il colore di sfondo.
    pulsanti_ruote[ruota]["bg"] = "lightgreen"  # Imposta il colore di sfondo del pulsante selezionato.
    entry_info.delete(0, tk.END)  # Cancella il testo nel campo di input.
    entry_info.insert(0, f"Ruota: {ruota}, Periodo: {start_date.date()} - {end_date.date()}")  # Inserisce il testo nel campo di input.

    logger.info(f"Dimensione X: {X.shape}, Dimensione y: {y.shape}")  # Logga le dimensioni dei dati.

    k = min(5, len(X))  # Calcola il numero di fold per la validazione incrociata.
    kf = KFold(n_splits=k, shuffle=True, random_state=42)  # Crea un oggetto KFold.

    # Mostra informazioni di caricamento
    textbox.insert(tk.END, f"Caricamento dati per {ruota}...\n")  # Inserisce il testo nel box di testo.

    model = build_model(X.shape[1], y.shape[1], dense_layers, dropout_rates)  # Costruisce il modello.

    logger.info(f"Configurazione del Modello: Epoche = {epochs}, Ottimizzatore = {optimizer_choice}, Funzione di perdita = {loss_function_choice}")  # Logga la configurazione del modello.

    # Modifica qui per correggere l'errore di sintassi
    if optimizer_choice == '1':
        optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
    elif optimizer_choice == '2':
        optimizer = tf.keras.optimizers.RMSprop(learning_rate=0.001)
    else:
        optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)
    # Fine modifica

    model.compile(optimizer=optimizer, loss=loss_function_choice, metrics=["mae"])  # Compila il modello.

    all_hist_loss = []  # Inizializza la lista per la perdita.
    all_hist_val_loss = []  # Inizializza la lista per la perdita di validazione.
    history_per_ruota = {}  # Inizializza il dizionario per la cronologia.

    # Mostra informazioni di addestramento
    textbox.insert(tk.END, f"Addestramento per {ruota}...\n")  # Inserisce il testo nel box di testo.

    progress_bar = ttk.Progressbar(root, orient="horizontal", length=200, mode="determinate", style="blue.Horizontal.TProgressbar")  # Crea la barra di avanzamento.
    progress_bar.pack(pady=10)  # Inserisce la barra di avanzamento.

    style = ttk.Style()  # Crea un oggetto Style.
    style.theme_use('clam')  # Usa il tema clam.
    style.configure("blue.Horizontal.TProgressbar", foreground='blue', background='lightblue')  # Configura la barra di avanzamento.
    style.map("blue.Horizontal.TProgressbar",
              background=[('active', 'blue')])

    fold_count = k  # Ottiene il numero di fold.
    epoch_count = epochs  # Ottiene il numero di epoche.
    total_steps = fold_count * epoch_count  # Calcola il numero totale di passi.
    current_step = 0  # Inizializza il passo corrente.

    for train_index, test_index in kf.split(X):  # Itera sui fold.
        X_train, X_test = X[train_index], X[test_index]  # Ottiene i dati di addestramento e test.
        y_train, y_test = y[train_index], y[test_index]

        noise = np.random.normal(0, 0.01 * np.abs(X_train), X_train.shape)  # Aggiunge rumore ai dati di addestramento.
        X_train_noisy = X_train + noise

        early_stopping = EarlyStopping(monitor='val_loss', patience=patience, min_delta=min_delta, restore_best_weights=True)  # Crea un oggetto EarlyStopping.

        history = model.fit(X_train_noisy, y_train, epochs=epochs, batch_size=64, validation_data=(X_test, y_test),
                            callbacks=[early_stopping], verbose=0)  # Addestra il modello.

        all_hist_loss.append(history.history['loss'])  # Aggiunge la perdita alla lista.
        all_hist_val_loss.append(history.history['val_loss'])  # Aggiunge la perdita di validazione alla lista.
        history_per_ruota[ruota] = history.history  # Memorizza la cronologia.

        logger.info(f"Fine addestramento fold, Con perdita train: {history.history['loss'][-1]}, Val: {history.history['val_loss'][-1]}")  # Logga la fine dell'addestramento.

        current_step += epoch_count  # Incrementa il passo corrente.
        update_progress(current_step, total_steps, f"Addestramento completato {current_step}/{total_steps}")  # Aggiorna la barra di avanzamento.

    y_pred = model.predict(X_test[-len(X_test):])  # Effettua la predizione.
    scaler_output = MinMaxScaler(feature_range=(1, 90))  # Crea un oggetto MinMaxScaler.
    y_pred_scaled = scaler_output.fit_transform(y_pred)  # Scala la predizione.
    numeri_interi = np.round(y_pred_scaled).astype(int)  # Arrotonda i numeri.

    # Mostra informazioni di predizione
    textbox.insert(tk.END, f"Predizione per {ruota}...\n")  # Inserisce il testo nel box di testo.

    update_textbox(numeri_interi)  # Aggiorna il box di testo con i numeri predetti.
    mostra_numeri_predetti(numeri_interi)  # Mostra i numeri predetti.
    mostra_migliori_risultati(history)  # Mostra i migliori risultati.

    prevedibilita = calcola_prevedibilita(history_per_ruota)  # Calcola la prevedibilità.
    mostra_prevedibilita(prevedibilita)  # Mostra la prevedibilità.

    mostra_grafico(all_hist_loss, all_hist_val_loss)  # Mostra il grafico.

    if 'history' in locals():  # Controlla se la variabile history esiste.
        loss_to_val_loss_ratio = [val_train / val_val if val_val != 0 else float('inf')  # Calcola il rapporto tra perdita e perdita di validazione.
                                   for val_train, val_val in zip(history.history['loss'], history.history['val_loss'])]
        min_ratio_epoch = np.argmin(loss_to_val_loss_ratio)  # Trova l'epoca con il rapporto minimo.

        plt.figure(figsize=(12, 6))  # Crea un grafico.
        plt.plot(history.history['loss'], label='Loss Train')  # Disegna la perdita di addestramento.
        plt.plot(history.history['val_loss'], label='Loss Val')  # Disegna la perdita di validazione.
        plt.plot(loss_to_val_loss_ratio, label='Ratio Loss/Val')  # Disegna il rapporto tra perdita e perdita di validazione.
        plt.scatter(min_ratio_epoch, loss_to_val_loss_ratio[min_ratio_epoch],
                    label='Optimal Solution (Early Stopping)', color='red')  # Disegna il punto ottimale.
        plt.title('Andamento della Perdita durante l\'Addestramento e Rapporto')  # Imposta il titolo del grafico.
        plt.xlabel('Epoche')  # Imposta l'etichetta dell'asse x.
        plt.ylabel('Perdita')  # Imposta l'etichetta dell'asse y.
        plt.axhline(0, color='black', linewidth=0.8, linestyle='--')  # Disegna una linea orizzontale.
        plt.legend()  # Aggiunge una legenda.
        plt.grid(True)  # Aggiunge una griglia.
        plt.show()  # Mostra il grafico.

    if progress_bar:  # Se la barra di avanzamento esiste.
        progress_bar.destroy()  # Distrugge la barra di avanzamento.
        progress_bar = None  # Imposta la barra di avanzamento a None.

    logger.info(f"Fine addestramento modello per {ruota}.")  # Logga la fine dell'addestramento.

def mostra_grafico(all_hist_loss, all_hist_val_loss):  # Definisce la funzione per mostrare il grafico.
    global fig, ax  # Dichiara le variabili globali.
    for child in frame_grafico.winfo_children():  # Itera sui figli del frame del grafico.
        child.destroy()  # Distrugge i figli.

    fig, ax = plt.subplots(figsize=(12, 10))  # Crea un grafico.

    for fold_idx in range(len(all_hist_loss)):  # Itera sui fold.
        ax.plot(all_hist_loss[fold_idx], label=f'Fold {fold_idx + 1} - Loss Train', linestyle='-')  # Disegna la perdita di addestramento.
        ax.plot(all_hist_val_loss[fold_idx], label=f'Fold {fold_idx + 1} - Loss Val', linestyle='--')  # Disegna la perdita di validazione.

    ax.set_title('Andamento della Perdita durante l\'Addestramento (K-Fold)')  # Imposta il titolo del grafico.
    ax.set_xlabel('Epoche')  # Imposta l'etichetta dell'asse x.
    ax.set_ylabel('Perdita')  # Imposta l'etichetta dell'asse y.
    ax.grid(True)  # Aggiunge una griglia.

    canvas = FigureCanvasTkAgg(fig, master=frame_grafico)  # Crea un canvas per il grafico.
    canvas.draw()  # Disegna il grafico.
    canvas.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH, expand=True)  # Inserisce il canvas nel frame.
    legend = ax.legend(loc='center left', bbox_to_anchor=(1, 0.5), fontsize='small')  # Aggiunge una legenda.
    fig.canvas.draw()  # Disegna il grafico.
    plt.close(fig)  # Chiude la figura.

def select_optimizer(opt):  # Definisce la funzione per selezionare l'ottimizzatore.
    global optimizer_choice  # Dichiara la variabile globale.
    optimizer_choice = opt  # Imposta l'ottimizzatore.
    logger.info(f"Ottimizzatore selezionato: {optimizer_choice}")  # Logga l'ottimizzatore selezionato.

def select_loss_function(loss_func):  # Definisce la funzione per selezionare la funzione di perdita.
    global loss_function_choice  # Dichiara la variabile globale.
    loss_function_choice = 'mean_squared_error' if loss_func == 'mse' else 'mean_absolute_error'  # Imposta la funzione di perdita.
    logger.info(f"Funzione di perdita selezionata: {loss_function_choice}")  # Logga la funzione di perdita selezionata.

def select_patience(patience_value):  # Definisce la funzione per selezionare il valore di patience.
    global patience  # Dichiara la variabile globale.
    patience = patience_value  # Imposta il valore di patience.
    logger.info(f"Patience impostato a: {patience}")  # Logga il valore di patience.

def select_min_delta(min_delta_value):  # Definisce la funzione per selezionare il valore di min_delta.
    global min_delta  # Dichiara la variabile globale.
    min_delta = min_delta_value  # Imposta il valore di min_delta.
    logger.info(f"Min Delta impostato a: {min_delta}")  # Logga il valore di min_delta.

def update_textbox(numeri):  # Definisce la funzione per aggiornare il box di testo con i numeri predetti.
    textbox.insert(tk.END, "Numeri Predetti:\n")  # Inserisce il testo nel box di testo.
    numeri_limitati = numeri.flatten()[:5]  # Estrae i primi 5 numeri.
    textbox.insert(tk.END, ", ".join(map(str, numeri_limitati)) + "\n")  # Inserisce i numeri nel box di testo.

def mostra_numeri_predetti(numeri):  # Definisce la funzione per mostrare i numeri predetti.
    textbox.insert(tk.END, "Numeri Predetti:\n")  # Inserisce il testo nel box di testo.
    numeri_limitati = numeri.flatten()[:5]  # Estrae i primi 5 numeri.
    textbox.insert(tk.END, ", ".join(map(str, numeri_limitati)) + "\n")  # Inserisce i numeri nel box di testo.

def mostra_migliori_risultati(history):  # Definisce la funzione per mostrare i migliori risultati.
    final_train_loss = history.history['loss'][-1]  # Ottiene la perdita di addestramento finale.
    final_val_loss = history.history['val_loss'][-1]  # Ottiene la perdita di validazione finale.
    textbox.insert(tk.END, f"Ultima Loss Train: {final_train_loss:.4f}\n")  # Inserisce il testo nel box di testo.
    textbox.insert(tk.END, f"Ultima Loss Val: {final_val_loss:.4f}\n")  # Inserisce il testo nel box di testo.

def salva_grafico(nome_file='grafico.png'):  # Definisce la funzione per salvare il grafico.
    if fig:  # Se la figura esiste.
        fig.savefig(nome_file)  # Salva la figura.
        messagebox.showinfo("Successo", f"Grafico salvato come {nome_file}")  # Mostra un messaggio di successo.
    else:  # Altrimenti.
        messagebox.showerror("Errore", "Nessun grafico disponibile per il salvataggio.")  # Mostra un messaggio di errore.

def salva_risultati(nome_file='risultati.txt'):  # Definisce la funzione per salvare i risultati.
    try:  # Inizia un blocco try-except.
        # Apre il file dialog per selezionare la posizione di salvataggio
        file_path = filedialog.asksaveasfilename(defaultextension=".txt",
                                                   filetypes=[("Text files", "*.txt"), ("All files", "*.*")])

        if file_path:  # Se l'utente ha selezionato un file.
            with open(file_path, 'w') as file:  # Apre il file.
                file.write(textbox.get(1.0, tk.END))  # Scrive il contenuto del box di testo nel file.
            messagebox.showinfo("Successo", f"Risultati salvati in {file_path}")  # Mostra un messaggio di successo.
        else:
            messagebox.showinfo("Info", "Salvataggio cancellato dall'utente.")

    except Exception as e:  # Cattura tutte le eccezioni.
        messagebox.showerror("Errore", f"Errore nel salvataggio dei risultati: {e}")  # Mostra un messaggio di errore.

# Creazione della finestra principale
root = tk.Tk()  # Crea la finestra principale.
root.geometry("1200x1250")  # Imposta la dimensione della finestra.

# Crea una cornice per contenere l'etichetta del titolo e altri elementi, se necessario.
title_frame = tk.Frame(root)
title_frame.pack(pady=10)  # Aggiunge un po' di spazio verticale attorno alla cornice

# Crea l'etichetta del titolo con il colore desiderato.
title_label = tk.Label(title_frame, text="𝔼𝕞𝕡𝕒𝕥𝕙𝕚𝕩", fg="blue", font=("Arial", 24))  # Scegli il colore e il font
title_label.pack() # Posiziona l'etichetta all'interno della cornice


# Frame per i pulsanti di salvataggio
frame_salvataggio = tk.Frame(root)  # Crea un frame per i pulsanti di salvat  # Crea un frame per i pulsanti di salvataggio.
frame_salvataggio.pack(pady=10)  # Inserisce il frame nella finestra.

# Pulsanti per il salvataggio
btn_salva_grafico = tk.Button(frame_salvataggio, text="Salva Grafico", command=lambda: salva_grafico('grafico.png'), bg="#FFDDC1")  # Crea il pulsante per salvare il grafico.
btn_salva_grafico.pack(side=tk.LEFT, padx=10)  # Inserisce il pulsante nel frame.

btn_salva_risultati = tk.Button(frame_salvataggio, text="Salva Risultati", command=lambda: salva_risultati('risultati.txt'), bg="#FFDDC1")
btn_salva_risultati.pack(side=tk.LEFT, padx=10)

# Creazione del frame per i pulsanti
frame_pulsanti = tk.Frame(root)
frame_pulsanti.pack(pady=20)

# Creazione dei pulsanti per la selezione delle ruote
pulsanti_ruote = {}
for ruota in file_ruote.keys():
    btn = tk.Button(frame_pulsanti, text=ruota, command=lambda r=ruota: on_seleziona_ruota(r), bg="#ADD8E6", fg="black")
    btn.pack(side=tk.LEFT, padx=5)
    pulsanti_ruote[ruota] = btn

# Campo di input per la ruota selezionata e il periodo
entry_info = tk.Entry(frame_pulsanti, width=100, bg="#F0F0F0", fg="black")
entry_info.pack(pady=10)

# Campi di input per le date
frame_date = tk.Frame(frame_pulsanti)
frame_date.pack(pady=20)

label_start_date = tk.Label(frame_date, text="Data di Inizio (YYYY/MM/DD):", bg="#ADD8E6", fg="black")
label_start_date.pack(side=tk.LEFT)
entry_start_date = tk.Entry(frame_date, width=15, bg="#F0F0F0", fg="black")
entry_start_date.pack(side=tk.LEFT)

label_end_date = tk.Label(frame_date, text="Data di Fine (YYYY/MM/DD):", bg="#ADD8E6", fg="black")
label_end_date.pack(side=tk.LEFT)
entry_end_date = tk.Entry(frame_date, width=15, bg="#F0F0F0", fg="black")
entry_end_date.pack(side=tk.LEFT)

# Campo per specificare il numero di epoche
frame_epochs = tk.Frame(frame_pulsanti)
frame_epochs.pack(pady=20)

label_epochs = tk.Label(frame_epochs, text="Numero di Epoche:", bg="#ADD8E6", fg="black")
label_epochs.pack(side=tk.LEFT)
entry_epochs = tk.Entry(frame_epochs, width=5, bg="#F0F0F0", fg="black")
entry_epochs.insert(0, "100")
entry_epochs.pack(side=tk.LEFT)

# Sezione per le opzioni di Early Stopping
frame_early_stopping = tk.Frame(root)
frame_early_stopping.pack(pady=20)

label_early_stopping = tk.Label(frame_early_stopping, text="Opzioni Early Stopping:", bg="#ADD8E6", fg="black")
label_early_stopping.pack(side=tk.TOP)

btn_patience_5 = tk.Button(frame_early_stopping, text="Patience: 5", command=lambda: select_patience(5), bg="#C9E4CA")
btn_patience_5.pack(side=tk.LEFT, padx=5)

btn_patience_10 = tk.Button(frame_early_stopping, text="Patience: 10", command=lambda: select_patience(10), bg="#C9E4CA")
btn_patience_10.pack(side=tk.LEFT, padx=5)

btn_patience_15 = tk.Button(frame_early_stopping, text="Patience: 15", command=lambda: select_patience(15), bg="#C9E4CA")
btn_patience_15.pack(side=tk.LEFT, padx=5)

btn_min_delta_01 = tk.Button(frame_early_stopping, text="Min Delta: 0.01", command=lambda: select_min_delta(0.01), bg="#C9E4CA")
btn_min_delta_01.pack(side=tk.LEFT, padx=5)

btn_min_delta_05 = tk.Button(frame_early_stopping, text="Min Delta: 0.05", command=lambda: select_min_delta(0.05), bg="#C9E4CA")
btn_min_delta_05.pack(side=tk.LEFT, padx=5)

btn_min_delta_1 = tk.Button(frame_early_stopping, text="Min Delta: 0.10", command=lambda: select_min_delta(0.1), bg="#C9E4CA")
btn_min_delta_1.pack(side=tk.LEFT, padx=5)

# Aggiunta di campi per i neuroni e i tassi di dropout
frame_model_parameters = tk.Frame(frame_pulsanti)
frame_model_parameters.pack(pady=20)

label_layer1 = tk.Label(frame_model_parameters, text="Layer 1 Neuroni:", bg="#ADD8E6", fg="black")
label_layer1.pack(side=tk.LEFT)
entry_neurons_layer1 = tk.Entry(frame_model_parameters, width=5, bg="#F0F0F0", fg="black")
entry_neurons_layer1.insert(0, "512")
entry_neurons_layer1.pack(side=tk.LEFT)

label_dropout_layer1 = tk.Label(frame_model_parameters, text="Layer 1 Dropout:", bg="#ADD8E6", fg="black")
label_dropout_layer1.pack(side=tk.LEFT)
entry_dropout_layer1 = tk.Entry(frame_model_parameters, width=5, bg="#F0F0F0", fg="black")
entry_dropout_layer1.insert(0, "0.2")
entry_dropout_layer1.pack(side=tk.LEFT)

label_layer2 = tk.Label(frame_model_parameters, text="Layer 2 Neuroni:", bg="#ADD8E6", fg="black")
label_layer2.pack(side=tk.LEFT)
entry_neurons_layer2 = tk.Entry(frame_model_parameters, width=5, bg="#F0F0F0", fg="black")
entry_neurons_layer2.insert(0, "256")
entry_neurons_layer2.pack(side=tk.LEFT)

label_dropout_layer2 = tk.Label(frame_model_parameters, text="Layer 2 Dropout:", bg="#ADD8E6", fg="black")
label_dropout_layer2.pack(side=tk.LEFT)
entry_dropout_layer2 = tk.Entry(frame_model_parameters, width=5, bg="#F0F0F0", fg="black")
entry_dropout_layer2.insert(0, "0.2")  # Valore predefinito
entry_dropout_layer2.pack(side=tk.LEFT)

label_layer3 = tk.Label(frame_model_parameters, text="Layer 3 Neuroni:", bg="#ADD8E6", fg="black")
label_layer3.pack(side=tk.LEFT)
entry_neurons_layer3 = tk.Entry(frame_model_parameters, width=5, bg="#F0F0F0", fg="black")
entry_neurons_layer3.insert(0, "128")  # Valore predefinito
entry_neurons_layer3.pack(side=tk.LEFT)

label_dropout_layer3 = tk.Label(frame_model_parameters, text="Layer 3 Dropout:", bg="#ADD8E6", fg="black")
label_dropout_layer3.pack(side=tk.LEFT)
entry_dropout_layer3 = tk.Entry(frame_model_parameters, width=5, bg="#F0F0F0", fg="black")
entry_dropout_layer3.insert(0, "0.2")  # Valore predefinito
entry_dropout_layer3.pack(side=tk.LEFT)

# Frame per i pulsanti di ottimizzazione
frame_optimizer = tk.Frame(frame_pulsanti)
frame_optimizer.pack(side=tk.RIGHT, padx=20)

btn_optimizer_1 = tk.Button(frame_optimizer, text=" 1 (Adam)", command=lambda: select_optimizer('1'), bg="#C9E4CA", fg="black")
btn_optimizer_1.pack(pady=5)

btn_optimizer_2 = tk.Button(frame_optimizer, text=" 2 (RMSprop)", command=lambda: select_optimizer('2'), bg="#C9E4CA", fg="black")
btn_optimizer_2.pack(pady=5)

btn_optimizer_3 = tk.Button(frame_optimizer, text=" 3 (SGD)", command=lambda: select_optimizer('3'), bg="#C9E4CA", fg="black")
btn_optimizer_3.pack(pady=5)

# Frame per i pulsanti della funzione di perdita
frame_loss_function = tk.Frame(frame_pulsanti)
frame_loss_function.pack(side=tk.RIGHT, padx=20)

btn_loss_mse = tk.Button(frame_loss_function, text="Loss: MSE", command=lambda: select_loss_function('mse'), bg="#C9E4CA", fg="black")
btn_loss_mse.pack(pady=5)

btn_loss_mae = tk.Button(frame_loss_function, text="Loss: MAE", command=lambda: select_loss_function('mae'), bg="#C9E4CA", fg="black")
btn_loss_mae.pack(pady=5)

# Frame principale per il box di testo e la scrollbar
text_frame = tk.Frame(root)
text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=10)

# Scrollbar verticale
scrollbar = ttk.Scrollbar(text_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

# Scrollbar orizzontale
scrollbar_x = ttk.Scrollbar(text_frame, orient=tk.HORIZONTAL)
scrollbar_x.pack(side=tk.BOTTOM, fill=tk.X)

# Creazione del box di testo con scrollbar
textbox = tk.Text(text_frame, height=10, width=90, wrap=tk.NONE, yscrollcommand=scrollbar.set, xscrollcommand=scrollbar_x.set)
textbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

# Configura la scrollbar
scrollbar.config(command=textbox.yview)
scrollbar_x.config(command=textbox.xview)

# Frame per il grafico
frame_grafico = tk.Frame(root)
frame_grafico.pack(pady=10)

# Frame per la prevedibilità
frame_prevedibilita = tk.Frame(root)
frame_prevedibilita.pack(pady=10)

# Creazione e visualizzazione della finestra
root.mainloop()

# Apre il file di log alla chiusura
open_log_file()

Codice:
Ecco le librerie necessarie in un'unica riga di installazione:

CopyReplit
pip install pandas numpy scikit-learn tensorflow matplotlib tkinter requests wheel


💥Download Empathix.py
grande! , ...dal canto mio ho provato ha chiedere una modifica a deePSeek ma data la mia inesperienza col codice faccio una gran fatica quando restituisce codice con errori , all'ora ti espongo la mia idea. da quello che ho capito la rete prende in input un'estrazione e come output di confronto per l'aggiornamento dei pesi l'estrazione secessiva, volevo dargli un più ampio respiro esempio input una data estrazione e output un range di estrazioni definito dall'utente nel pannello di settaggio ,in modo che la rete per la modifica dei suoi pesi consideri più estrazioni , facendo attenzione che quando si raggiunge la fine della data impostata diminuisca proporzionalmente il range per non sforare dall'ultima estrazione definita. ... è solo un'idea valuta tu se sia il caso o meno di implementarne una versione . ciao e complimenti per questa versione di Emphathxs. 🎯
 
Complimenti Lottopython per la tua creaturina... 🐍🤖 Anche solo per l'interfaccia... la trovo spettacolare... 💪👌👍:) Non vedo l'ora di testarla...
🤞

grande! , ...dal canto mio ho provato ha chiedere una modifica a deePSeek ma data la mia inesperienza col codice faccio una gran fatica quando restituisce codice con errori , all'ora ti espongo la mia idea. da quello che ho capito la rete prende in input un'estrazione e come output di confronto per l'aggiornamento dei pesi l'estrazione secessiva, volevo dargli un più ampio respiro esempio input una data estrazione e output un range di estrazioni definito dall'utente nel pannello di settaggio ,in modo che la rete per la modifica dei suoi pesi consideri più estrazioni , facendo attenzione che quando si raggiunge la fine della data impostata diminuisca proporzionalmente il range per non sforare dall'ultima estrazione definita. ... è solo un'idea valuta tu se sia il caso o meno di implementarne una versione . ciao e complimenti per questa versione di Emphathxs. 🎯
interessante ma bisognerebbe stravolgere il codice Empathix del tutto ,al momento cerrco di migliorare quello pubblicato .grazie batty
 
Ciao lottopython e ciao a tutti. Ho testato la tua creaturina ed è davvero interessante. Se posso suggerisco di implementare se e quando vuoi queste due cose: tabella numerica per poter selezionare il gruppo base numerico voluto e un sistema (magari una semplice text area) per vedere le impostazioni "cliccate nei bottoni verdi" che al momento mi sembra rimangano "nascoste". Infine ho provato a impostare lo stesso tuo screen shot ma mi sono venuti fuor dati diversi. Probabilmente proprio per le impostazioni dei bottoni verdi cliccati magari in modo diverso... rispetto al tuo esempio. Un'altra cosa che mi è piaciuta moltissimo sono le estrazioni che immagino siano aggiornate in modo automatico sulla tua relativa sezione github. 💪👌👋🤖👽:)
 
Ciao lottopython e ciao a tutti. Ho testato la tua creaturina ed è davvero interessante. Se posso suggerisco di implementare se e quando vuoi queste due cose: tabella numerica per poter selezionare il gruppo base numerico voluto e un sistema (magari una semplice text area) per vedere le impostazioni "cliccate nei bottoni verdi" che al momento mi sembra rimangano "nascoste". Infine ho provato a impostare lo stesso tuo screen shot ma mi sono venuti fuor dati diversi. Probabilmente proprio per le impostazioni dei bottoni verdi cliccati magari in modo diverso... rispetto al tuo esempio. Un'altra cosa che mi è piaciuta moltissimo sono le estrazioni che immagino siano aggiornate in modo automatico sulla tua relativa sezione github. 💪👌👋🤖👽:)
grazie Tom per il tuo feed lo apprezzo
buona giornata
 

Ultima estrazione Lotto

  • Estrazione del lotto
    martedì 18 febbraio 2025
    Bari
    72
    83
    64
    59
    43
    Cagliari
    60
    55
    49
    07
    14
    Firenze
    11
    68
    62
    04
    21
    Genova
    86
    23
    44
    85
    71
    Milano
    11
    82
    42
    09
    14
    Napoli
    89
    86
    65
    49
    14
    Palermo
    79
    34
    80
    78
    20
    Roma
    26
    21
    13
    64
    57
    Torino
    64
    11
    17
    02
    58
    Venezia
    64
    82
    30
    04
    19
    Nazionale
    26
    37
    68
    54
    87
    Estrazione Simbolotto
    Cagliari
    39
    30
    04
    11
    14

Ultimi Messaggi

Indietro
Alto