kawaiipeace commited on
Commit
0fa83e9
·
1 Parent(s): 0f952c8

Initialization

Browse files
.DS_Store ADDED
Binary file (6.15 kB). View file
 
.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ .env
2
+ models/__pycache__
3
+ utils/__pycache__
4
+ dataset/*
5
+ .python-version
.gradio/flagged/dataset1.csv ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ 📥 Load File (MW) CSV,🌡️ Temperature File (MaxTemp) CSV,ΔTemp (°C),📊 Simulated Output (MaxTemp ➜ Load MW),timestamp
2
+ ,,1,"{""headers"": [""1"", ""2"", ""3""], ""data"": [], ""metadata"": null}",2025-07-17 23:49:54.330002
app.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ import gradio as gr
3
+ import pandas as pd
4
+ from fastapi import FastAPI
5
+ from gradio.routes import mount_gradio_app
6
+ import uvicorn
7
+ import os
8
+ import numpy as np
9
+
10
+ from utils.preprocessing import preprocess_load_data, preprocess_temperature_data
11
+ from utils.merge_data import merge_load_temp
12
+ from models.forecast import TimeSeriesForecaster
13
+ from models.regression import LoadLimitRegressor
14
+ from utils.simulate import simulate_with_model
15
+
16
+ # --- FastAPI app ---
17
+ app = FastAPI()
18
+
19
+ # --- Globals ---
20
+ df_merged = None # เก็บข้อมูล merge เอาไว้
21
+ forecast_model = None
22
+ reg_model = None
23
+
24
+ # --- Model option lists ---
25
+ forecast_models = ['PatchTST', 'LSTM', 'BiLSTM', 'GRU', 'RNN', 'ARIMA', 'Prophet']
26
+ regression_models = ['linear', 'xgb', 'mlp']
27
+
28
+ def forecast_ui(load_file, temp_file,
29
+ forecast_model_type, regression_model_type,
30
+ forecast_horizon, delta_temp):
31
+ global df_merged, forecast_model, reg_model
32
+
33
+ # 1. อ่านไฟล์
34
+ df_load_raw = pd.read_csv(load_file.name)
35
+ df_temp_raw = pd.read_csv(temp_file.name)
36
+
37
+ # 2. preprocess
38
+ df_load = preprocess_load_data(df_load_raw)
39
+ df_temp = preprocess_temperature_data(df_temp_raw)
40
+
41
+ # 3. merge
42
+ df_merged = merge_load_temp(df_load, df_temp)
43
+
44
+ # 4. สร้างและ train forecasting model (forecast max load)
45
+ forecast_model = TimeSeriesForecaster(model_type=forecast_model_type, horizon=forecast_horizon)
46
+ forecast_model.fit(df_merged, value_col='mw_max')
47
+ forecast_pred = forecast_model.predict(future_steps=forecast_horizon)
48
+
49
+ # 5. train regression model (max temp -> max load limit)
50
+ reg_model = LoadLimitRegressor(model_type=regression_model_type)
51
+ reg_model.fit(df_merged, temp_col='MaxTemp', load_col='mw_max')
52
+
53
+ # 6. simulate load limit with delta_temp
54
+ df_simulated = simulate_with_model(df_merged, delta_temp=delta_temp, model=reg_model)
55
+
56
+ # 7. เตรียมผลลัพธ์สำหรับแสดง
57
+ # forecast results dataframe
58
+ last_date = df_merged['date'].max() if 'date' in df_merged.columns else pd.to_datetime('today')
59
+ forecast_df = pd.DataFrame({
60
+ 'Step': np.arange(1, forecast_horizon + 1),
61
+ 'Forecast Load MW': forecast_pred
62
+ })
63
+
64
+ # simulation dataframe (limit model)
65
+ # แสดงแค่ columns สำคัญ
66
+ sim_df_show = df_simulated[['MaxTemp', 'mw_max', 'MaxTemp_simulated', 'Load_simulated']]
67
+
68
+ return forecast_df, sim_df_show
69
+
70
+ # --- Gradio UI ---
71
+ demo = gr.Interface(
72
+ fn=forecast_ui,
73
+ inputs=[
74
+ gr.File(label="📥 Load File (MW) CSV"),
75
+ gr.File(label="🌡️ Temperature File (MaxTemp) CSV"),
76
+ gr.Dropdown(choices=forecast_models, label="Forecast Model", value='PatchTST'),
77
+ gr.Dropdown(choices=regression_models, label="Regression Model", value='xgb'),
78
+ gr.Slider(minimum=1, maximum=30, step=1, label="Forecast Horizon (days)", value=7),
79
+ gr.Slider(minimum=-5.0, maximum=5.0, step=0.5, label="ΔTemp (°C)", value=1.0)
80
+ ],
81
+ outputs=[
82
+ gr.Dataframe(label="📈 Forecast Load MW"),
83
+ gr.Dataframe(label="📊 Simulated Load Limit")
84
+ ],
85
+ title="Submarine Load Forecast & Safety Margin Simulation",
86
+ description="Upload load and temperature CSV data, choose forecasting and regression models, set forecast horizon, and simulate temperature impact on load limit."
87
+ )
88
+
89
+ demo.launch(
90
+ server_name=os.getenv("SERVER_NAME", "0.0.0.0"),
91
+ server_port=int(os.getenv("PORT", 7860)),
92
+ )
dockerfile ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dockerfile
2
+ FROM python:3.10-slim
3
+
4
+ WORKDIR /app
5
+
6
+ # 1. Install system dependencies
7
+ RUN apt-get update && apt-get install -y \
8
+ git build-essential libglib2.0-0 libsm6 libxext6 libxrender-dev \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ # 2. Install Python dependencies
12
+ COPY requirements.txt .
13
+ RUN pip install --no-cache-dir -r requirements.txt
14
+
15
+ # 3. Copy all source code
16
+ COPY . .
17
+
18
+ # 4. Expose port
19
+ ENV PORT=7860
20
+ EXPOSE $PORT
21
+
22
+ # 5. Run app
23
+ CMD ["python", "app.py"]
models/forecast.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # models/forecasting.py
2
+
3
+ import pandas as pd
4
+ import numpy as np
5
+ from tsai.all import *
6
+ from typing import Literal, Tuple
7
+ from sklearn.preprocessing import MinMaxScaler
8
+
9
+ ForecastModelType = Literal['PatchTST', 'LSTM', 'BiLSTM', 'GRU', 'RNN', 'ARIMA', 'Prophet']
10
+
11
+ class TimeSeriesForecaster:
12
+ def __init__(self, model_type: ForecastModelType = 'PatchTST', horizon: int = 7):
13
+ self.model_type = model_type
14
+ self.horizon = horizon
15
+ self.scaler = MinMaxScaler()
16
+ self.model = None
17
+ self.dls = None
18
+ self.X_cols = None
19
+
20
+ def preprocess(self, df: pd.DataFrame, value_col: str) -> Tuple[np.ndarray, np.ndarray]:
21
+ """
22
+ รับ dataframe และคอลัมน์เป้าหมาย แล้วแปลงให้เป็น (X, y) สำหรับ tsai
23
+ """
24
+ df = df.copy()
25
+ df[value_col] = self.scaler.fit_transform(df[[value_col]])
26
+ ts = df[value_col].values.astype(np.float32)
27
+ X, y = SlidingWindow(window_len=self.horizon, horizon=self.horizon)(ts)
28
+ return X, y
29
+
30
+ def fit(self, df: pd.DataFrame, value_col: str):
31
+ """
32
+ เทรนโมเดลตาม model_type ที่เลือก
33
+ """
34
+ if self.model_type in ['PatchTST', 'LSTM', 'BiLSTM', 'GRU', 'RNN']:
35
+ X, y = self.preprocess(df, value_col)
36
+ self.dls = get_ts_dls(X, y, bs=32, splits=TSStandardSplit()(X))
37
+ if self.model_type == 'PatchTST':
38
+ self.model = PatchTST(self.dls.vars, self.dls.len, self.dls.c)
39
+ elif self.model_type == 'LSTM':
40
+ self.model = LSTM(self.dls.vars, self.dls.len, self.dls.c)
41
+ elif self.model_type == 'BiLSTM':
42
+ self.model = LSTM(self.dls.vars, self.dls.len, self.dls.c, bidirectional=True)
43
+ elif self.model_type == 'GRU':
44
+ self.model = GRU(self.dls.vars, self.dls.len, self.dls.c)
45
+ elif self.model_type == 'RNN':
46
+ self.model = RNN(self.dls.vars, self.dls.len, self.dls.c)
47
+ learn = Learner(self.dls, self.model, loss_func=MSELossFlat(), metrics=[mae, rmse])
48
+ learn.fit_one_cycle(20, 1e-3)
49
+ self.learn = learn
50
+ elif self.model_type == 'ARIMA':
51
+ from statsmodels.tsa.arima.model import ARIMA
52
+ self.model = ARIMA(df[value_col], order=(7, 1, 0)).fit()
53
+ elif self.model_type == 'Prophet':
54
+ from prophet import Prophet
55
+ prophet_df = df[['datetime', value_col]].rename(columns={'datetime': 'ds', value_col: 'y'})
56
+ self.model = Prophet()
57
+ self.model.fit(prophet_df)
58
+ else:
59
+ raise ValueError(f"Unsupported model type: {self.model_type}")
60
+
61
+ def predict(self, future_steps: int = None) -> np.ndarray:
62
+ """
63
+ พยากรณ์ค่าข้างหน้า future_steps (หรือ horizon)
64
+ """
65
+ if future_steps is None:
66
+ future_steps = self.horizon
67
+
68
+ if self.model_type in ['PatchTST', 'LSTM', 'BiLSTM', 'GRU', 'RNN']:
69
+ x_last = self.dls.items[-1][None, :]
70
+ pred = self.learn.model(x_last).detach().cpu().numpy().flatten()
71
+ pred = self.scaler.inverse_transform(pred.reshape(-1, 1)).flatten()
72
+ return pred[:future_steps]
73
+ elif self.model_type == 'ARIMA':
74
+ forecast = self.model.forecast(steps=future_steps)
75
+ return forecast.values if hasattr(forecast, 'values') else forecast
76
+ elif self.model_type == 'Prophet':
77
+ future = self.model.make_future_dataframe(periods=future_steps)
78
+ forecast = self.model.predict(future)
79
+ return forecast['yhat'][-future_steps:].values
80
+ else:
81
+ raise ValueError("Model not trained or unsupported type")
models/regression.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # models/regression.py
2
+
3
+ import pandas as pd
4
+ import numpy as np
5
+ from typing import Literal
6
+ from sklearn.linear_model import LinearRegression
7
+ from sklearn.ensemble import GradientBoostingRegressor
8
+ from sklearn.neural_network import MLPRegressor
9
+ from sklearn.metrics import mean_squared_error, r2_score
10
+ from sklearn.preprocessing import MinMaxScaler
11
+
12
+ RegressionModelType = Literal['linear', 'xgb', 'mlp']
13
+
14
+ class LoadLimitRegressor:
15
+ def __init__(self, model_type: RegressionModelType = 'xgb'):
16
+ self.model_type = model_type
17
+ self.scaler_X = MinMaxScaler()
18
+ self.scaler_y = MinMaxScaler()
19
+ self.model = None
20
+
21
+ def fit(self, df: pd.DataFrame, temp_col: str = 'MaxTemp', load_col: str = 'mw_max'):
22
+ """
23
+ เทรน regression model เพื่อหา relationship ระหว่างอุณหภูมิ ➜ โหลดสูงสุด
24
+ """
25
+ X = df[[temp_col]].values
26
+ y = df[[load_col]].values
27
+
28
+ X_scaled = self.scaler_X.fit_transform(X)
29
+ y_scaled = self.scaler_y.fit_transform(y)
30
+
31
+ if self.model_type == 'linear':
32
+ self.model = LinearRegression()
33
+ elif self.model_type == 'xgb':
34
+ self.model = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1, max_depth=3)
35
+ elif self.model_type == 'mlp':
36
+ self.model = MLPRegressor(hidden_layer_sizes=(64, 32), max_iter=1000)
37
+ else:
38
+ raise ValueError(f"Unsupported model type: {self.model_type}")
39
+
40
+ self.model.fit(X_scaled, y_scaled.ravel())
41
+
42
+ def predict(self, max_temp_values: np.ndarray) -> np.ndarray:
43
+ """
44
+ พยากรณ์โหลดสูงสุดที่เป็นไปได้ จากค่าความร้อน
45
+ """
46
+ X_scaled = self.scaler_X.transform(max_temp_values.reshape(-1, 1))
47
+ y_scaled = self.model.predict(X_scaled).reshape(-1, 1)
48
+ y = self.scaler_y.inverse_transform(y_scaled)
49
+ return y.flatten()
50
+
51
+ def evaluate(self, df: pd.DataFrame, temp_col: str = 'MaxTemp', load_col: str = 'mw_max') -> dict:
52
+ """
53
+ ประเมินผลโมเดล regression
54
+ """
55
+ y_true = df[load_col].values
56
+ y_pred = self.predict(df[temp_col].values)
57
+
58
+ return {
59
+ 'rmse': np.sqrt(mean_squared_error(y_true, y_pred)),
60
+ 'r2': r2_score(y_true, y_pred)
61
+ }
packages.txt ADDED
File without changes
requirements.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ gradio
3
+ uvicorn
4
+ pandas
5
+ scikit-learn
6
+ xgboost
7
+ numpy
8
+ python-dotenv
9
+ tsai==0.4.0
10
+ fastai==2.7.19
11
+ fastcore==1.7.29
12
+ ipykernel
utils/merge_data.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # utils/merge_data.py
2
+ import pandas as pd
3
+
4
+ def aggregate_daily_max(df_load_15min: pd.DataFrame) -> pd.DataFrame:
5
+ """
6
+ จากข้อมูลที่ resample มาแล้ว (ทุก 15 นาที) รวมให้เหลือแค่ daily max load
7
+ """
8
+ df = df_load_15min.copy()
9
+ df['date'] = df['datetime'].dt.date # เป็น datetime.date ไม่เอาเวลา
10
+ df_daily = df.groupby('date')['mw'].agg(['max', 'mean']).reset_index()
11
+ df_daily = df_daily.rename(columns={'max': 'mw_max', 'mean': 'mw_mean'})
12
+ return df_daily
13
+
14
+ def merge_load_temp(df_daily_load: pd.DataFrame, df_temp: pd.DataFrame) -> pd.DataFrame:
15
+ """
16
+ รวมข้อมูล Load (daily) กับ Temperature (daily) โดยใช้ date เป็น key
17
+ df_temp ต้องมีคอลัมน์ 'date' ที่เป็น datetime64[ns] หรือ datetime.date
18
+ """
19
+ df_temp = df_temp.copy()
20
+ # ถ้าไม่มี 'date' คอลัมน์ ให้สร้างจาก year,month,day
21
+ if 'date' not in df_temp.columns:
22
+ df_temp['date'] = pd.to_datetime(df_temp[['year', 'month', 'day']]).dt.date
23
+
24
+ # มึงอย่าไปแปลงเป็น datetime แล้วเอาเวลาออกอีกที ให้มันมี datatype เดียวกันระหว่าง 2 df นี่แหละดีที่สุด
25
+
26
+ df_merged = pd.merge(df_daily_load, df_temp[['date', 'MaxTemp']], on='date', how='inner')
27
+ return df_merged
28
+
29
+ # ตัวอย่างใช้
30
+ if __name__ == '__main__':
31
+ load_fp = 'load_15min.csv'
32
+ temp_fp = 'temp.csv'
33
+
34
+ df_load_15min = pd.read_csv(load_fp, parse_dates=['datetime'])
35
+ df_temp = pd.read_csv(temp_fp)
36
+
37
+ df_daily = aggregate_daily_max(df_load_15min)
38
+ df_merged = merge_load_temp(df_daily, df_temp)
39
+
40
+ print(df_merged.head())
utils/preprocessing.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # utils/preprocessing.py
2
+ import pandas as pd
3
+
4
+ def preprocess_load_data(df_load: pd.DataFrame) -> pd.DataFrame:
5
+ """
6
+ รับ DataFrame load ดิบ แปลง datetime, sort, resample 15 นาที เติม missing
7
+ """
8
+ df_load = df_load.copy()
9
+
10
+ # ลบ space รอบคอลัมน์เผื่อมีมั่ว
11
+ df_load.columns = df_load.columns.str.strip()
12
+
13
+ # รวม date + time เป็น datetime พร้อม dayfirst=True (วัน/เดือน/ปี)
14
+ df_load['datetime'] = pd.to_datetime(df_load['date'].astype(str) + ' ' + df_load['time'].astype(str), dayfirst=True)
15
+
16
+ # Drop คอลัมน์เดิม
17
+ df_load = df_load.drop(columns=['date', 'time'])
18
+
19
+ # Sort และตั้ง index เป็น datetime
20
+ df_load = df_load.sort_values('datetime').set_index('datetime')
21
+
22
+ # สร้าง full range datetime ทุก 15 นาที ตั้งแต่วันแรกถึงวันสุดท้าย
23
+ start = df_load.index.min().normalize()
24
+ end = df_load.index.max().normalize() + pd.Timedelta(days=1) - pd.Timedelta(minutes=15)
25
+ full_idx = pd.date_range(start=start, end=end, freq='15T')
26
+
27
+ # Reindex เติมช่วงเวลาทั้งหมด
28
+ df_load = df_load.reindex(full_idx)
29
+
30
+ # เติม missing ด้วย linear interpolation แล้ว forward fill / back fill อีกที
31
+ df_load['mw'] = df_load['mw'].interpolate(method='time').fillna(method='ffill').fillna(method='bfill')
32
+
33
+ # Reset index กลับเป็นคอลัมน์ datetime
34
+ df_load = df_load.reset_index().rename(columns={'index': 'datetime'})
35
+
36
+ return df_load
37
+
38
+
39
+ def preprocess_temperature_data(df_temp: pd.DataFrame) -> pd.DataFrame:
40
+ """
41
+ รับ DataFrame temp ดิบที่มี year, month, day, MaxTemp
42
+ รวม 3 คอลัมน์ปีเดือนวันเป็น datetime แล้ว sort
43
+ """
44
+ df_temp = df_temp.copy()
45
+
46
+ # ลบ space รอบคอลัมน์เผื่อมั่ว
47
+ df_temp.columns = df_temp.columns.str.strip()
48
+
49
+ # แปลงเป็น datetime
50
+ df_temp['date'] = pd.to_datetime(df_temp[['year', 'month', 'day']])
51
+
52
+ # Sort ตามวันที่
53
+ df_temp = df_temp.sort_values('date').reset_index(drop=True)
54
+
55
+ return df_temp
56
+
57
+
58
+ if __name__ == '__main__':
59
+ # ทดสอบง่าย ๆ
60
+ df_load = pd.DataFrame({
61
+ 'date': ['2024-03-01', '2024-03-01', '2024-03-01', '2024-03-02'],
62
+ 'time': ['00:00', '00:15', '01:00', '00:00'],
63
+ 'mw': [10, 12, 15, 11]
64
+ })
65
+ print(preprocess_load_data(df_load).head(10))
66
+
67
+ df_temp = pd.DataFrame({
68
+ 'year': [2024, 2024, 2024],
69
+ 'month': [3, 3, 3],
70
+ 'day': [1, 2, 3],
71
+ 'MaxTemp': [19.0, 18.5, 19.5]
72
+ })
73
+ print(preprocess_temperature_data(df_temp))
utils/simulate.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # utils/simulate.py
2
+
3
+ import pandas as pd
4
+ import numpy as np
5
+ from models.regression import LoadLimitRegressor
6
+
7
+ def simulate_temperature_change(df: pd.DataFrame, delta_temp: float) -> pd.DataFrame:
8
+ """
9
+ จำลองสถานการณ์เพิ่ม/ลด MaxTemp ทีละ delta_temp
10
+ แบบ simple linear approximation
11
+ """
12
+ df_sim = df.copy()
13
+ df_sim['MaxTemp_simulated'] = df_sim['MaxTemp'] + delta_temp
14
+
15
+ a, b = np.polyfit(df_sim['MaxTemp'], df_sim['mw_max'], 1)
16
+ df_sim['Load_simulated'] = a * df_sim['MaxTemp_simulated'] + b
17
+
18
+ return df_sim
19
+
20
+ def simulate_with_model(df: pd.DataFrame, delta_temp: float, model: LoadLimitRegressor) -> pd.DataFrame:
21
+ """
22
+ จำลองสถานการณ์ โดยใช้ regression model ที่ฝึกมาแล้ว
23
+ """
24
+ df_sim = df.copy()
25
+ df_sim['MaxTemp_simulated'] = df_sim['MaxTemp'] + delta_temp
26
+ df_sim['Load_simulated'] = model.predict(df_sim['MaxTemp_simulated'].values)
27
+
28
+ return df_sim