maxb4567 commited on
Commit
6fcc9f3
·
verified ·
1 Parent(s): 0bf6d60

Upload submission.py

Browse files
Files changed (1) hide show
  1. submission.py +97 -0
submission.py ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import numpy as np
3
+ import pandas as pd
4
+ from sklearn.ensemble import GradientBoostingRegressor
5
+ import emflow as ef
6
+
7
+
8
+ class QuantileRegressionPredictor(ef.Predictor):
9
+ def __init__(self, name="quantile-regression-ar"):
10
+ self.name = name
11
+ self.lags = [1, 24]
12
+ self.quantile = 0.5
13
+ self.models = {}
14
+
15
+ def _prepare_features(self, df: pd.DataFrame, col: str):
16
+ series = df[col]
17
+ # Use timezone-normalized timestamps to derive time features
18
+ # Note: input_df index is expected to be a DatetimeIndex
19
+ hour = df.index.hour
20
+ day_of_week = df.index.dayofweek
21
+ month = df.index.month
22
+
23
+ X = pd.DataFrame(
24
+ {
25
+ "hour": hour,
26
+ "day_of_week": day_of_week,
27
+ "month": month,
28
+ "lag_1h": series.shift(1),
29
+ "lag_24h": series.shift(24),
30
+ },
31
+ index=df.index,
32
+ )
33
+ return X
34
+
35
+ def train(self, train_df: pd.DataFrame):
36
+ if isinstance(train_df, pd.Series):
37
+ train_df = train_df.to_frame()
38
+
39
+ # Filter for Stockholm-Observatoriekullen A as per user's specific interest
40
+ # though the contract implies handling all columns passed.
41
+ # Given the "swedish-temperatures:ar" context, we handle all.
42
+ for col in train_df.columns:
43
+ X = self._prepare_features(train_df, col)
44
+ y = train_df[col]
45
+
46
+ # Combine and drop NaNs
47
+ data = pd.concat([X, y], axis=1).dropna()
48
+ if len(data) < 100: # Heuristic for sufficient data
49
+ self.models[col] = None
50
+ continue
51
+
52
+ X_train = data.drop(columns=[col])
53
+ y_train = data[col]
54
+
55
+ model = GradientBoostingRegressor(
56
+ loss="quantile",
57
+ alpha=self.quantile,
58
+ n_estimators=100,
59
+ max_depth=5,
60
+ random_state=42,
61
+ )
62
+ model.fit(X_train, y_train)
63
+ self.models[col] = model
64
+ return self
65
+
66
+ def predict(self, input_df: pd.DataFrame):
67
+ if isinstance(input_df, pd.Series):
68
+ input_df = input_df.to_frame()
69
+
70
+ preds = {}
71
+ for col in input_df.columns:
72
+ model = self.models.get(col)
73
+ if model is None:
74
+ preds[col] = np.full(len(input_df), np.nan)
75
+ continue
76
+
77
+ X = self._prepare_features(input_df, col)
78
+ # We can't dropna here because we need a value for the last row
79
+ # even if it has NaNs in current values (but lags should be there)
80
+ # The contract says last timestamp is the target to forecast.
81
+ # Only the current value at last timestamp is NaN. Lags should be fine.
82
+
83
+ # Fill NaNs in features with a neutral value or previous if necessary
84
+ # but usually for the last row, shift(1) of NaN is the value at T-1.
85
+ out = np.full(len(input_df), np.nan)
86
+
87
+ # Identify rows where we have all features
88
+ valid_mask = X.notna().all(axis=1)
89
+ if valid_mask.any():
90
+ out[valid_mask] = model.predict(X[valid_mask])
91
+
92
+ preds[col] = out
93
+
94
+ return pd.DataFrame(preds, index=input_df.index, columns=input_df.columns)
95
+
96
+
97
+ model = QuantileRegressionPredictor()