import numpy as np import pandas as pd import torch import json import time import firebase_admin from firebase_admin import credentials, db from sklearn.preprocessing import MinMaxScaler, LabelEncoder import pickle import io import base64 def prepare_input_data(data, sequence_length, input_size): """ เตรียมข้อมูลนำเข้าให้อยู่ในรูปแบบที่เหมาะสมสำหรับโมเดล GRU Args: data (numpy.ndarray): ข้อมูลนำเข้า sequence_length (int): ความยาวของลำดับเวลา input_size (int): จำนวนคุณลักษณะนำเข้า Returns: tensor: ข้อมูลในรูปแบบ [batch_size, sequence_length, input_size] """ # ตรวจสอบรูปร่างของข้อมูล if len(data.shape) == 1: # ถ้าเป็น 1D array # สมมติว่ามี input_size features ในแต่ละ timestep data = data.reshape(-1, input_size) # ตรวจสอบว่ามีข้อมูลพอสำหรับ sequence_length หรือไม่ if data.shape[0] < sequence_length: # ถ้าไม่พอ ให้เพิ่มข้อมูลโดยการทำซ้ำข้อมูลแรก repeats_needed = sequence_length - data.shape[0] first_row = np.tile(data[0:1], (repeats_needed, 1)) data = np.vstack([first_row, data]) # ถ้ามีข้อมูลมากกว่า sequence_length ให้ใช้แค่ sequence_length ล่าสุด if data.shape[0] > sequence_length: data = data[-sequence_length:] # เพิ่มมิติ batch_size (=1) data = data.reshape(1, sequence_length, -1) return torch.FloatTensor(data) def create_sequences(data, seq_length): """ สร้างลำดับ (sequences) จากข้อมูล Args: data (numpy.ndarray): ข้อมูลต้นฉบับ seq_length (int): ความยาวของลำดับเวลา Returns: numpy.ndarray: ข้อมูลในรูปแบบลำดับเวลา """ xs = [] for i in range(len(data) - seq_length + 1): x = data[i:(i + seq_length)] xs.append(x) return np.array(xs) def init_firebase(credentials_json, database_url): """ เริ่มต้นการเชื่อมต่อกับ Firebase Args: credentials_json (str): ข้อมูล JSON ของ Firebase credentials database_url (str): URL ของ Firebase Realtime Database Returns: bool: True ถ้าเชื่อมต่อสำเร็จ, False ถ้าไม่สำเร็จ """ if not firebase_admin._apps: try: # แปลง JSON string เป็น dictionary cred_dict = json.loads(credentials_json) cred = credentials.Certificate(cred_dict) firebase_admin.initialize_app(cred, { 'databaseURL': database_url }) return True except Exception as e: print(f"เกิดข้อผิดพลาดในการเชื่อมต่อกับ Firebase: {str(e)}") return False return True def get_data_from_firebase(ref_path='input_data'): """ ดึงข้อมูลจาก Firebase Realtime Database Args: ref_path (str): พาธสำหรับดึงข้อมูลจาก Firebase Returns: dict/list: ข้อมูลที่ดึงมาจาก Firebase """ try: ref = db.reference(ref_path) data = ref.get() return data except Exception as e: print(f"เกิดข้อผิดพลาดในการดึงข้อมูลจาก Firebase: {str(e)}") return None def save_data_to_firebase(data, ref_path='prediction_results'): """ บันทึกข้อมูลลงใน Firebase Realtime Database Args: data (dict/list): ข้อมูลที่ต้องการบันทึก ref_path (str): พาธสำหรับบันทึกข้อมูลลงใน Firebase Returns: bool: True ถ้าบันทึกสำเร็จ, False ถ้าไม่สำเร็จ """ try: ref = db.reference(ref_path) ref.set(data) return True except Exception as e: print(f"เกิดข้อผิดพลาดในการบันทึกข้อมูลลงใน Firebase: {str(e)}") return False def load_scalers_and_encoders(model_path): """ โหลด scalers และ encoders จากไฟล์โมเดล Args: model_path (str): พาธไปยังไฟล์โมเดล Returns: tuple: (numeric_scaler, label_encoders, y_scaler) """ try: checkpoint = torch.load(model_path, map_location='cpu') # ตรวจสอบแต่ละกรณี numeric_scaler = None label_encoders = None y_scaler = None if isinstance(checkpoint, dict): # กรณีที่มี key โดยตรง numeric_scaler = checkpoint.get('numeric_scaler') label_encoders = checkpoint.get('label_encoders') y_scaler = checkpoint.get('y_scaler') # กรณีที่เก็บไว้ใน key อื่น if numeric_scaler is None and 'scalers' in checkpoint: numeric_scaler = checkpoint['scalers'].get('numeric_scaler') if y_scaler is None and 'scalers' in checkpoint: y_scaler = checkpoint['scalers'].get('y_scaler') if label_encoders is None and 'encoders' in checkpoint: label_encoders = checkpoint['encoders'].get('label_encoders') return numeric_scaler, label_encoders, y_scaler except Exception as e: print(f"เกิดข้อผิดพลาดในการโหลด scalers และ encoders: {str(e)}") return None, None, None def create_default_scaler(): """ สร้าง MinMaxScaler เริ่มต้น """ scaler = MinMaxScaler(feature_range=(0, 1)) # กำหนดค่า min และ max เริ่มต้น scaler.min_ = np.zeros(1) scaler.scale_ = np.ones(1) scaler.data_min_ = np.zeros(1) scaler.data_max_ = np.ones(1) scaler.data_range_ = np.ones(1) scaler.n_samples_seen_ = 1 return scaler def create_default_encoders(n_categories=2): """ สร้าง LabelEncoder เริ่มต้น """ encoders = [] for i in range(n_categories): le = LabelEncoder() # กำหนดค่าเริ่มต้น le.classes_ = np.array(['class0', 'class1']) encoders.append(le) return encoders def preprocess_data(data, numeric_features, categorical_features, numeric_scaler, label_encoders): """ ประมวลผลข้อมูลก่อนการทำนาย Args: data (dict/list): ข้อมูลนำเข้า numeric_features (list): รายชื่อคุณลักษณะตัวเลข categorical_features (list): รายชื่อคุณลักษณะเชิงกลุ่ม numeric_scaler (MinMaxScaler): scaler สำหรับข้อมูลตัวเลข label_encoders (list): encoders สำหรับข้อมูลเชิงกลุ่ม Returns: numpy.ndarray: ข้อมูลที่ผ่านการประมวลผลแล้ว """ try: # ตรวจสอบรูปแบบข้อมูล if isinstance(data, list) and all(isinstance(item, dict) for item in data): # กรณีที่ข้อมูลเป็นลิสต์ของ dict (หลาย timestep) X_numeric = np.array([[item[feature] for feature in numeric_features] for item in data]) X_categorical = np.array([[item[feature] for feature in categorical_features] for item in data]) elif isinstance(data, dict): # กรณีที่ข้อมูลเป็น dict เดียว (single timestep) X_numeric = np.array([[data[feature] for feature in numeric_features]]) X_categorical = np.array([[data[feature] for feature in categorical_features]]) else: raise ValueError("รูปแบบข้อมูลไม่ถูกต้อง ต้องเป็น dict หรือ list ของ dict") # ตรวจสอบ scaler และ encoders if numeric_scaler is None: print("Warning: ไม่พบ numeric_scaler จะสร้างใหม่") numeric_scaler = create_default_scaler() if label_encoders is None or len(label_encoders) != len(categorical_features): print("Warning: label_encoders ไม่ถูกต้อง จะสร้างใหม่") label_encoders = create_default_encoders(len(categorical_features)) # ปรับสเกลข้อมูลตัวเลข X_numeric_scaled = numeric_scaler.transform(X_numeric) # Encode ข้อมูลเชิงกลุ่ม X_categorical_encoded = [] for i, encoder in enumerate(label_encoders): try: # พยายาม transform ข้อมูล encoded_col = encoder.transform(X_categorical[:, i]) except (ValueError, IndexError) as e: # ถ้าเกิดข้อผิดพลาด (เช่น พบค่าที่ไม่เคยเห็น) print(f"Warning: เกิดข้อผิดพลาดในการ encode คุณลักษณะที่ {i}: {str(e)}") print(f"จะใช้ค่า 0 แทน") # ใช้ค่า 0 แทน encoded_col = np.zeros(X_categorical.shape[0], dtype=np.int64) X_categorical_encoded.append(encoded_col) # รวมข้อมูล X_categorical_encoded = np.column_stack(X_categorical_encoded) if X_categorical_encoded else np.array([]) if X_categorical_encoded.size > 0: # ถ้ามีข้อมูลเชิงกลุ่ม ให้รวมกับข้อมูลตัวเลข X_encoded = np.concatenate([X_numeric_scaled, X_categorical_encoded], axis=1) else: # ถ้าไม่มีข้อมูลเชิงกลุ่ม ใช้เฉพาะข้อมูลตัวเลข X_encoded = X_numeric_scaled return X_encoded except Exception as e: print(f"เกิดข้อผิดพลาดในการประมวลผลข้อมูล: {str(e)}") raise e def get_file_download_link(data, filename, text="Download File"): """ สร้างลิงก์สำหรับดาวน์โหลดไฟล์ Args: data: ข้อมูลที่ต้องการให้ดาวน์โหลด filename (str): ชื่อไฟล์ text (str): ข้อความที่แสดงบนลิงก์ Returns: str: HTML ลิงก์สำหรับดาวน์โหลด """ b64 = base64.b64encode(data).decode() href = f'{text}' return href def save_scaler_to_bytes(scaler): """ แปลง scaler เป็น bytes สำหรับดาวน์โหลด """ bytes_io = io.BytesIO() pickle.dump(scaler, bytes_io) bytes_io.seek(0) return bytes_io.read() def save_encoders_to_bytes(encoders): """ แปลง encoders เป็น bytes สำหรับดาวน์โหลด """ bytes_io = io.BytesIO() pickle.dump(encoders, bytes_io) bytes_io.seek(0) return bytes_io.read()