import streamlit as st import pandas as pd import numpy as np import time import os import tempfile from ucimlrepo import fetch_ucirepo from sklearn.preprocessing import LabelEncoder, StandardScaler from sklearn.metrics.pairwise import euclidean_distances from sklearn.neighbors import KDTree # Define the path for save case base to the CSV file DATA_FILE = os.path.join(tempfile.gettempdir(), 'case_base_data.csv') #DATA_FILE = 'case_base_data.csv' # # -------------- Load Dataset -------------- # @st.cache_data # def load_data(): # dataset = fetch_ucirepo(id=544) # X = dataset.data.features # y = dataset.data.targets # df = pd.concat([X, y], axis=1) # return df # # -------------- Load Existing Data from CSV -------------- # @st.cache_data # def load_existing_data(): # if os.path.exists(DATA_FILE): # return pd.read_csv(DATA_FILE) # else: # return pd.DataFrame() # # ------------------- Init Session State ------------------- # if 'case_base' not in st.session_state: # df_original = load_data() # st.session_state.case_base = load_existing_data() if not df_original.empty else df_original.copy() # # ------------------- Preprocessing ------------------- # df = st.session_state.case_base # df_encoded = df.copy() # label_encoders = {} # for col in df_encoded.select_dtypes(include='object').columns: # le = LabelEncoder() # df_encoded[col] = le.fit_transform(df_encoded[col]) # label_encoders[col] = le # features = df_encoded.drop(columns=['NObeyesdad']) # target = df_encoded['NObeyesdad'] # scaler = StandardScaler() # features_scaled = scaler.fit_transform(features) # -------------- Load Dataset -------------- @st.cache_data def load_data(): dataset = fetch_ucirepo(id=544) X = dataset.data.features y = dataset.data.targets df = pd.concat([X, y], axis=1) try : df.to_csv(DATA_FILE, index=False) print(f"succesfull write data to '{DATA_FILE}'") print(f"File saved at: {os.path.abspath(DATA_FILE)}") except Exception as e: print(f"An error occurred while writing the CSV file: {e}") return df # -------------- Load Existing Data from CSV -------------- @st.cache_data def load_existing_data(): if os.path.exists(DATA_FILE): return pd.read_csv(DATA_FILE) else: return pd.DataFrame() # ------------------- Init Session State ------------------- if 'case_base' not in st.session_state: df_original = load_data() st.write("Original DataFrame after loading:", df_original.head()) # Debugging line st.session_state.case_base = load_existing_data() if not df_original.empty else df_original.copy() # ------------------- Preprocessing ------------------- df = st.session_state.case_base df_encoded = df.copy() label_encoders = {} # Check if the DataFrame is empty if df_encoded.empty: st.error("The dataset is empty. Please check the data loading process.") else: for col in df_encoded.select_dtypes(include='object').columns: le = LabelEncoder() df_encoded[col] = le.fit_transform(df_encoded[col]) label_encoders[col] = le # Check if 'NObeyesdad' exists in the DataFrame if 'NObeyesdad' in df_encoded.columns: features = df_encoded.drop(columns=['NObeyesdad']) st.write("cek isi featur atas:", features.tail()) # Debugging line target = df_encoded['NObeyesdad'] st.write("cek target:", target.tail()) # Debugging line else: st.error("Column 'NObeyesdad' not found in the dataset.") features = df_encoded # or handle it as needed target = None # or handle it as needed # Proceed only if features are not empty if not features.empty: scaler = StandardScaler() features_scaled = scaler.fit_transform(features) st.write("cek isi featur bawh:", features.tail()) # Debugging line else: st.error("Features DataFrame is empty. Cannot proceed with scaling.") # ... [rest of your existing code remains unchanged] ... # ------------------- HEOM Function ------------------- def heom_distance(x1, x2, numerical_cols, categorical_cols, ranges): dist = 0 for col in numerical_cols: r = ranges[col] if r > 0: d = ((x1[col] - x2[col]) / r) ** 2 dist += d for col in categorical_cols: dist += 0 if x1[col] == x2[col] else 1 return np.sqrt(dist) # ------------------- Adaptability Score ------------------- def calculate_adaptability_score(new_case_df, case_base_df): info_score = 0 epsilon = 1e-9 for col in new_case_df.columns: freq = case_base_df[col].value_counts(normalize=True) p = freq.get(new_case_df.iloc[0][col], epsilon) info_score += -np.log2(p) return info_score # ------------------- Threshold Adaptif ------------------- @st.cache_data def get_adaptive_threshold(case_base_df, percentile=50): all_scores = [ calculate_adaptability_score(pd.DataFrame([row]), case_base_df) for _, row in case_base_df.iterrows() ] return np.percentile(all_scores, percentile) # ------------------- Retain Case ------------------- def retain_case(new_case_dict, case_base_df, distance_threshold=0.5, adaptive_threshold=30, force=False): new_case_df = pd.DataFrame([new_case_dict]) for col in new_case_df.select_dtypes(include='object').columns: if col in label_encoders: new_case_df[col] = label_encoders[col].transform(new_case_df[col]) new_case_encoded = new_case_df.copy() new_case_scaled = scaler.transform(new_case_encoded) numerical_cols = features.select_dtypes(include=np.number).columns.tolist() categorical_cols = [col for col in features.columns if col not in numerical_cols] feature_ranges = {col: df_encoded[col].max() - df_encoded[col].min() for col in numerical_cols} raw_input = new_case_encoded.iloc[0] heom_distances = [ heom_distance(raw_input, row, numerical_cols, categorical_cols, feature_ranges) for _, row in features.iterrows() ] min_dist = min(heom_distances) adaptability_score = calculate_adaptability_score(new_case_encoded, df_encoded.drop(columns=['NObeyesdad'])) retain_flag = (min_dist > distance_threshold and adaptability_score < adaptive_threshold) or force if retain_flag: try: # Save the new case to the CSV file new_case_with_label = new_case_dict.copy() new_case_with_label['NObeyesdad'] = predicted_label # Assuming predicted_label is available new_case_df = pd.DataFrame([new_case_with_label]) print(f"cek new casebase {new_case_df}, ") new_case_df.to_csv(DATA_FILE, mode='a', header=not os.path.exists(DATA_FILE), index=False) st.session_state.case_base = pd.read_csv(DATA_FILE) st.success(f"โœ… Kasus berhasil disimpan. Jumlah kasus sekarang: {len(st.session_state.case_base)}") except PermissionError: st.error("Permission denied: Unable to save the case to the CSV file.") except Exception as e: st.error(f"An error occurred while saving the case: {e}") return retain_flag, min_dist, adaptability_score, adaptive_threshold # ... [rest of your existing code remains unchanged] ... # ------------------- CBR ------------------- def case_based_reasoning(new_input_dict): new_input_df = pd.DataFrame([new_input_dict]) for col in new_input_df.select_dtypes(include='object').columns: if col in label_encoders: new_input_df[col] = label_encoders[col].transform(new_input_df[col]) new_input_scaled = scaler.transform(new_input_df) # Euclidean eucl_distances = euclidean_distances(new_input_scaled, features_scaled) eucl_closest_index = eucl_distances.argmin() # KD-Tree tree = KDTree(features_scaled) kd_dist, kd_idx = tree.query(new_input_scaled, k=1) kd_index = kd_idx[0][0] # HEOM raw_input = new_input_df.iloc[0] numerical_cols = features.select_dtypes(include=np.number).columns.tolist() categorical_cols = [col for col in features.columns if col not in numerical_cols] feature_ranges = {col: df_encoded[col].max() - df_encoded[col].min() for col in numerical_cols} heom_distances = [ heom_distance(raw_input, row, numerical_cols, categorical_cols, feature_ranges) for _, row in features.iterrows() ] heom_closest_index = int(np.argmin(heom_distances)) # Hybrid KD-Tree + HEOM k_candidates = 50 kd_tree_indices = tree.query(new_input_scaled, k=k_candidates)[1][0] heom_candidate_dists = [] for idx in kd_tree_indices: row = features.iloc[idx] dist = heom_distance(raw_input, row, numerical_cols, categorical_cols, feature_ranges) heom_candidate_dists.append(dist) heom_kdtree_index = int(kd_tree_indices[int(np.argmin(heom_candidate_dists))]) return { "euclidean": { "index": int(eucl_closest_index), "distance": float(eucl_distances[0][eucl_closest_index]), }, "kdtree": { "index": int(kd_index), "distance": float(kd_dist[0][0]), }, "heom": { "index": heom_closest_index, "case": df.iloc[heom_closest_index].to_dict() }, "heom_kdtree": { "index": heom_kdtree_index, "case": df.iloc[heom_kdtree_index].to_dict() } } # ------------------- Streamlit UI ------------------- st.title("๐Ÿง  CBR Obesitas + Retain Adaptif (Live Session)") st.markdown(f"Jumlah kasus dalam database saat ini: **{len(st.session_state.case_base)} kasus**") user_input = { 'Gender': st.selectbox("Gender", ['Male', 'Female']), 'Age': st.number_input("Age", 10, 100, 25), 'Height': st.number_input("Height (in meters)", 1.0, 2.5, 1.70), 'Weight': st.number_input("Weight (in kg)", 30.0, 200.0, 70.0), 'family_history_with_overweight': st.selectbox("Family history with overweight", ['yes', 'no']), 'FAVC': st.selectbox("Frequent consumption of high caloric food", ['yes', 'no']), 'FCVC': st.slider("Vegetable consumption (0โ€“3)", 0.0, 3.0, 2.0), 'NCP': st.slider("Number of main meals", 1.0, 5.0, 3.0), 'CAEC': st.selectbox("Food between meals", ['no', 'Sometimes', 'Frequently', 'Always']), 'SMOKE': st.selectbox("Do you smoke?", ['yes', 'no']), 'CH2O': st.slider("Daily water intake", 0.0, 3.0, 2.0), 'SCC': st.selectbox("Calories monitoring", ['yes', 'no']), 'FAF': st.slider("Physical activity (hrs/week)", 0.0, 5.0, 1.0), 'TUE': st.slider("Tech usage (hrs/day)", 0.0, 5.0, 1.0), 'CALC': st.selectbox("Alcohol consumption", ['no', 'Sometimes', 'Frequently', 'Always']), 'MTRANS': st.selectbox("Transport", ['Automobile', 'Motorbike', 'Bike', 'Public_Transportation', 'Walking']) } force_save = st.checkbox("๐Ÿ’พ Simpan paksa jika ditolak?", value=False) # ... [rest of your existing Streamlit UI code remains unchanged] ... if st.button("๐Ÿ” Prediksi dan Evaluasi Retain"): result = case_based_reasoning(user_input) st.markdown("### ๐Ÿ”ข Kasus Paling Mirip") st.json(result['heom_kdtree']['case']) # Ambil prediksi dan tambahkan ke input predicted_label = result['heom_kdtree']['case']['NObeyesdad'] user_input_with_label = user_input.copy() user_input_with_label['NObeyesdad'] = predicted_label # Tampilkan label prediksi st.markdown("### ๐Ÿงพ Prediksi Kategori Obesitas") st.write(f"๐ŸŽฏ **{predicted_label}** (berdasarkan kasus paling mirip)") # Evaluasi Retain adaptive_threshold = get_adaptive_threshold(df_encoded.drop(columns=['NObeyesdad']), percentile=50) retain_flag, min_dist, adaptability_score, threshold_used = retain_case( user_input, df_encoded, distance_threshold=0.5, adaptive_threshold=adaptive_threshold, force=force_save ) st.markdown("### ๐Ÿ“ฅ Evaluasi Retain") st.write(f"HEOM Distance Terdekat: `{min_dist:.3f}`") st.write(f"Skor Adaptabilitas: `{adaptability_score:.2f}` (Threshold adaptif: `{threshold_used:.2f}`)") if retain_flag: latest_data = pd.read_csv(DATA_FILE) st.write("cek data baru:", latest_data.tail()) # Debugging line st.success(f"โœ… Kasus berhasil disimpan. Jumlah kasus sekarang: {len(latest_data)}") else: st.info("โŒ Kasus tidak disimpan otomatis (tidak adaptif & terlalu mirip).") if force_save: st.session_state.case_base = pd.concat([st.session_state.case_base, pd.DataFrame([user_input_with_label])], ignore_index=True) st.warning(f"โš ๏ธ Simpan paksa dilakukan. Jumlah kasus sekarang: {len(st.session_state.case_base)}")