Καλησπέρα. Χρειάζομαι τη βοήθεια σου σχετικά με μια εργασία σε python notebooks. Έχω ολοκληρώσει τα στάδια 1,2,3 και βρίσκομαι στο 4. Ακολουθούν τα κομμάτια κώδικα που έχω χρησιμοποιήσει, οι εκφωνήσεις κάθε σταδίου και τα τεστ ορθής λειτουργίας κάθε σταδίου.

1) Διαβάστε το διαθέσιμο από την sklearn σύνολο δεδομένων diabetes που αφορά την πρόβλεψη της πορείας που θα έχει ένας ασθενής με διαβήτη ένα χρόνο μετά (εργασία παλινδρόμησης) και αποθηκεύστε το σε μια μεταβλητή με το όνομα diabetes. (0.5 μονάδες)

from sklearn.datasets import load_diabetes

diabetes = load_diabetes()

Τεστ 1) 
"""Τεστ ορθής ανάγνωσης του συνόλου δεδομένων"""
assert diabetes.feature_names == ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']

2) Χωρίστε το σύνολο που διαβάσατε σε υποσύνολο ελέγχου 20% και υποσύνολο εκπαίδευσης 80% με την μέθοδο train_test_split και σπόρο τυχαιότητας 42. Aποθηκεύστε το σύνολο εκπαίδευσης σε μεταβλητές X_train, y_train και το σύνολο ελέγχου σε μεταβλητές X_test, y_test. (0.5 μονάδες)

from sklearn.model_selection import train_test_split

# Υποθέτοντας ότι έχετε ήδη φορτώσει τα δεδομένα και τα έχετε αποθηκεύσει στη μεταβλητή diabetes
X = diabetes.data
y = diabetes.target

# Χωρισμός των δεδομένων σε σετ εκπαίδευσης και ελέγχου
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

Τεστ 2) 
"""Τεστ ορθού διαχωρισμού του συνόλου δεδομένων"""
assert round(X_train[0][8], 5) == 0.02736
assert round(X_test[0][8], 5) == 0.03243

3) Χρησιμοποιώντας τα δεδομένα εκπαίδευσης, εκπαιδεύστε δύο δένδρα παλινδρόμησης με κριτήρια διαχωρισμού τα squared_eror και poisson ([δείτε ενότητα 1.10.7.2](https://scikit-learn.org/stable/modules/tree.html#tree)) αντίστοιχα, θέτοντας τον σπόρο τυχαιότητας στην τιμή 42 και το μέγιστο βάθος στην τιμή 3. Υπολογίστε τη μετρική r2 με βάση τις προβλέψεις τους στα δεδομένα ελέγχου, τις οποίες αποθηκεύστε στις μεταβλητές y_pred1 (squared_error) και y_pred2 (poisson) αντίστοιχα.
Αποθηκεύστε τις μετρικές στις μεταβλητές r2_squared_error και r2_poisson αντίστοιχα. (1 μονάδα)

from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import r2_score

# Εκπαίδευση του πρώτου δένδρου με κριτήριο squared_error
tree_squared_error = DecisionTreeRegressor(criterion='squared_error', max_depth=3, random_state=42)
tree_squared_error.fit(X_train, y_train)
y_pred1 = tree_squared_error.predict(X_test)
r2_squared_error = r2_score(y_test, y_pred1)

# Εκπαίδευση του δεύτερου δένδρου με κριτήριο poisson
tree_poisson = DecisionTreeRegressor(criterion='poisson', max_depth=3, random_state=42)
tree_poisson.fit(X_train, y_train)
y_pred2 = tree_poisson.predict(X_test)
r2_poisson = r2_score(y_test, y_pred2)

Τεστ 3) 
"""Τεστ ορθού υπολογισμού των μετρικών"""
assert round(r2_squared_error, 3) == 0.329
assert round(r2_poisson, 3) == 0.391
assert len(y_pred1) == len(y_pred2)

4) Αναπτύξτε κώδικα με βάση την ιδιότητα [tree_](https://scikit-learn.org/stable/auto_examples/tree/plot_unveil_tree_structure.html#sphx-glr-auto-examples-tree-plot-unveil-tree-structure-py) του παρακάτω μοντέλου, προκειμένου να εμφανίσετε το μονοπάτι που ακολουθήθηκε στο δένδρο για την πρόβλεψη της πρώτης περίπτωσης του συνόλου ελέγχου. Σε περίπτωση που ελέγχεται δεύτερη φορά μια μεταβλητή στο μονοπάτι, θα πρέπει να διατηρούμε τον τελευταίο και στενότερο περιορισμό. Ακολουθούν παραδείγματα ορθής λειτουργίας, στα οποία φαίνεται και η μορφή αν-τότε που πρέπει να έχει το μονοπάτι. (4 μονάδες)

from sklearn.tree import export_text
model = DecisionTreeRegressor(max_depth=3, random_state=42)
model.fit(X_train, y_train)



def get_path(tree, instance, feature_names):
    node_indicator = tree.decision_path([instance])
    feature_index = tree.tree_.feature
    thresholds = tree.tree_.threshold
    children_left = tree.tree_.children_left
    children_right = tree.tree_.children_right
    
    node_index = node_indicator.indices[node_indicator.indptr[0]:node_indicator.indptr[1]]

    path = []
    for node_id in node_index:
        if children_left[node_id] == children_right[node_id]:  # Αν είναι φύλλο
            value = tree.tree_.value[node_id][0][0]  # Τιμή στο φύλλο
            path.append(f" τότε {value:.2f}")
            break
        else:
            feature_name = feature_names[feature_index[node_id]]
            threshold = thresholds[node_id]
            if instance[0, feature_index[node_id]] <= threshold:
                condition = f"και {feature_name} <= {threshold:.2f} "
            else:
                condition = f"και {feature_name} > {threshold:.2f} "
            
            # Έλεγχος για επαναλαμβανόμενη εμφάνιση χαρακτηριστικών
            existing_condition_indices = [i for i, cond in enumerate(path) if feature_name in cond]
            if not existing_condition_indices:
                path.append(condition)
            else:
                # Αντικατάσταση του παλιού όρου
                path[existing_condition_indices[0]] = condition

    # Επιστροφή του ολοκληρωμένου μονοπατιού
    return "αν " + " ".join(path)  # ενώνει τις συνθήκες με διαστήματα

Τεστ 4)
"""Τεστ ορθού υπολογισμού του μονοπατιού"""
assert get_path(model, X_test[0], diabetes.feature_names) == 'αν bmi <= 0.01 και s5 > 0.01 και s4 <= 0.09 τότε 159.57'
assert get_path(model, X_test[1], diabetes.feature_names) == 'αν bmi > 0.01 και bmi <= 0.07 και s6 <= 0.03 τότε 175.8'
assert get_path(model, X_test[6], diabetes.feature_names) == 'αν bmi > 0.07 και s2 > 0.02 τότε 225.75'