File size: 3,319 Bytes
47d42b1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | """Feature engineering for train_xgboost."""
from __future__ import annotations
import logging
import numpy as np
import pandas as pd
LOGGER = logging.getLogger("static_xgboost")
def build_feature_frame(df: pd.DataFrame) -> pd.DataFrame:
"""Build a model-ready frame with engineered features and both targets."""
work = df.copy()
if "timestamp" not in work.columns:
raise KeyError("Expected canonical 'timestamp' column after loading data.")
work["timestamp"] = pd.to_datetime(work["timestamp"], errors="coerce", utc=True)
work = work.dropna(subset=["timestamp"]).sort_values("timestamp")
numeric_cols = [
"temperature",
"humidity",
"wind_direction",
"wind_speed",
"wind_gust",
"pressure",
"ghi",
"dni",
"dhi",
]
for col in numeric_cols:
if col in work.columns:
work[col] = pd.to_numeric(work[col], errors="coerce")
present_numeric = [col for col in numeric_cols if col in work.columns]
work = work.dropna(subset=["ghi"])
work[present_numeric] = work[present_numeric].interpolate(limit_direction="both")
work[present_numeric] = work[present_numeric].ffill().bfill()
# Consolidate duplicate timestamps by averaging numeric weather columns.
grouped = work.groupby("timestamp", as_index=False)[present_numeric].mean()
grouped = grouped.sort_values("timestamp").reset_index(drop=True)
ts = grouped["timestamp"]
grouped["hour"] = ts.dt.hour
grouped["day_of_year"] = ts.dt.dayofyear
grouped["month"] = ts.dt.month
grouped["hour_sin"] = np.sin(2 * np.pi * grouped["hour"] / 24)
grouped["hour_cos"] = np.cos(2 * np.pi * grouped["hour"] / 24)
grouped["doy_sin"] = np.sin(2 * np.pi * grouped["day_of_year"] / 365.25)
grouped["doy_cos"] = np.cos(2 * np.pi * grouped["day_of_year"] / 365.25)
for lag in (1, 2, 3, 24):
grouped[f"ghi_lag_{lag}"] = grouped["ghi"].shift(lag)
if "temperature" in grouped.columns:
grouped["temp_lag_1"] = grouped["temperature"].shift(1)
grouped["ghi_rolling_mean_3h"] = grouped["ghi"].shift(1).rolling(3, min_periods=1).mean()
grouped["ghi_rolling_std_3h"] = grouped["ghi"].shift(1).rolling(3, min_periods=2).std()
grouped["target_ghi_next_1h"] = grouped["ghi"].shift(-1)
grouped["target_temperature_next_1h"] = grouped["temperature"].shift(-1)
grouped = grouped.dropna(subset=["target_ghi_next_1h", "target_temperature_next_1h"]).copy()
return grouped.fillna(0.0)
def prepare_features(df: pd.DataFrame, target_mode: str) -> tuple[pd.DataFrame, pd.Series, pd.Series, str]:
"""Build feature matrix and selected target vector."""
grouped = build_feature_frame(df)
target_column = {
"ghi": "target_ghi_next_1h",
"temperature": "target_temperature_next_1h",
}[target_mode]
feature_cols = [
col
for col in grouped.columns
if col
not in {
"timestamp",
"target_ghi_next_1h",
"target_temperature_next_1h",
}
]
x = grouped[feature_cols]
y = grouped[target_column]
ts = grouped["timestamp"]
LOGGER.info("Prepared features for %s: rows=%d cols=%d", target_mode, x.shape[0], x.shape[1])
return x, y, ts, target_column
|