Spaces:
Sleeping
Sleeping
| from pathlib import Path | |
| import numpy as np | |
| import pandas as pd | |
| import torch | |
| from torch.utils.data import Dataset | |
| # --------------------------------------------------------------------- | |
| # 1. Sparse Dataset μ μ (panel + static β ST-ResNet μ λ ₯ μμ±) | |
| # --------------------------------------------------------------------- | |
| class GridSTSparseDataset(Dataset): | |
| """ | |
| ST-ResNet κ³μ΄ λͺ¨λΈμ© μκ³΅κ° Grid λ°μ΄ν°μ . | |
| λμ (dynamic) νΌμ² (F_dyn = 3): | |
| 1) cnt(g,t) : μ¬κ³ 건μ | |
| 2) dist(g,t) : νκ· νμ₯거리 (mean_dist, meter) | |
| 3) traffic(g,t): GRID_SPEED (νκ· κ΅ν΅λ/μλ) | |
| β ν μμ λμ ν μ shape: (H, W, 3) | |
| μ) closeness μ λ ₯: (L_c, H, W, 3) | |
| μ μ (static) νΌμ² (F_stat = 3): | |
| 1) road_len_in_grid(g) | |
| 2) n_links(g) | |
| 3) n_segments(g) | |
| β μ μ ν μ shape: (H, W, 3) | |
| λ°ν dict: | |
| { | |
| "X_closeness": (L_c, H, W, 3), | |
| "X_period": (L_p, H, W, 3) or None, | |
| "X_trend": (L_t, H, W, 3) or None, | |
| "X_static": (H, W, 3), | |
| "y": (H, W), # νκ· μμμκ° | |
| "mask": (H, W), # κ΄μΈ‘ λ§μ€ν¬ | |
| "t": int, # t_idx | |
| "ext": (3,) or None, # [hour/23, dow/6, is_weekend] | |
| } | |
| """ | |
| def __init__( | |
| self, | |
| panel: pd.DataFrame, | |
| static: np.ndarray, | |
| T: int, | |
| R: int, | |
| C: int, | |
| seq_len: int = 6, | |
| period: int = 24, | |
| trend: int = 24 * 7, | |
| num_periods: int = 3, | |
| num_trends: int = 2, | |
| hour_arr: np.ndarray | None = None, | |
| dow_arr: np.ndarray | None = None, | |
| is_weekend_arr: np.ndarray | None = None, | |
| ): | |
| """ | |
| panel μ»¬λΌ μꡬμ¬ν: | |
| - "t_idx" | |
| - "r_idx", "c_idx" | |
| - "cnt" : μ¬κ³ 건μ | |
| - "mean_time" : νκ· μμμκ° (μ΄) | |
| - "mean_dist" : νκ· νμ₯거리 | |
| - "traffic" : GRID_SPEED (νκ· μλ/κ΅ν΅λ) | |
| """ | |
| super().__init__() | |
| self.panel = panel | |
| self.static = torch.from_numpy(static).float() # (R, C, F_stat=3) | |
| self.T = T | |
| self.R = R | |
| self.C = C | |
| self.seq_len = seq_len | |
| self.period = period | |
| self.trend = trend | |
| self.num_periods = num_periods | |
| self.num_trends = num_trends | |
| self.hour_arr = hour_arr | |
| self.dow_arr = dow_arr | |
| self.is_weekend_arr = is_weekend_arr | |
| # (t_idx β sparse grid μ 보) μΊμ | |
| # time_dict[t] = (r_idx, c_idx, cnt, mean_time, mean_dist, traffic) | |
| self._build_time_index() | |
| # temporal dependency λ§μ‘±νλ tλ§ μ ν¨ | |
| min_t = seq_len | |
| if period > 0 and num_periods > 0: | |
| min_t = max(min_t, num_periods * period) | |
| if trend > 0 and num_trends > 0: | |
| min_t = max(min_t, num_trends * trend) | |
| self.min_t = min_t | |
| self.valid_t = list(range(self.min_t, self.T)) | |
| print( | |
| f"[GridSTSparseDataset] T={self.T}, R={self.R}, C={self.C}, " | |
| f"seq_len={self.seq_len}, period={self.period}, trend={self.trend}, " | |
| f"|valid_t|={len(self.valid_t)}" | |
| ) | |
| def _build_time_index(self): | |
| """ | |
| panelλ₯Ό t_idxλ³λ‘ κ·Έλ£Ήνν΄μ sparse grid μ μ₯. | |
| time_dict[t] = ( | |
| r_idx: int[], | |
| c_idx: int[], | |
| cnt: float32[], | |
| mean_time: float32[], | |
| mean_dist: float32[], | |
| traffic: float32[], | |
| ) | |
| """ | |
| self.time_dict = {} | |
| g = self.panel.groupby("t_idx") | |
| for t, df_t in g: | |
| r = df_t["r_idx"].to_numpy(dtype=int) | |
| c = df_t["c_idx"].to_numpy(dtype=int) | |
| cnt = df_t["cnt"].to_numpy(dtype=np.float32) | |
| mt = df_t["mean_time"].to_numpy(dtype=np.float32) | |
| dist = df_t["mean_dist"].to_numpy(dtype=np.float32) | |
| traf = df_t["traffic"].to_numpy(dtype=np.float32) | |
| self.time_dict[int(t)] = (r, c, cnt, mt, dist, traf) | |
| def __len__(self): | |
| return len(self.valid_t) | |
| def _build_map_at_t(self, t: int): | |
| """ | |
| μμ tμ λν΄ full grid λ§΅ μμ±. | |
| Returns | |
| ------- | |
| cnt_map : (R, C), μ¬κ³ 건μ | |
| dist_map : (R, C), νκ· νμ₯거리 | |
| traf_map : (R, C), νκ· κ΅ν΅λ/μλ | |
| y_map : (R, C), νκ· μμμκ° (μ΄) | |
| mask_map : (R, C), κ΄μΈ‘ λ§μ€ν¬ (0/1) | |
| """ | |
| R, C = self.R, self.C | |
| cnt_map = np.zeros((R, C), dtype=np.float32) | |
| dist_map = np.zeros((R, C), dtype=np.float32) | |
| traf_map = np.zeros((R, C), dtype=np.float32) | |
| y_map = np.zeros((R, C), dtype=np.float32) | |
| if t in self.time_dict: | |
| r_idx, c_idx, cnt, mt, dist, traf = self.time_dict[t] | |
| cnt_map[r_idx, c_idx] = cnt | |
| dist_map[r_idx, c_idx] = dist | |
| traf_map[r_idx, c_idx] = traf | |
| y_map[r_idx, c_idx] = mt | |
| # 0 < y < 3600 μΈ μ§μ λ§ μ ν¨ κ΄μΈ‘ | |
| mask_map = ((y_map > 0) & (y_map < 3600)).astype(np.float32) | |
| return cnt_map, dist_map, traf_map, y_map, mask_map | |
| def __getitem__(self, idx): | |
| t = self.valid_t[idx] | |
| # ---------- closeness (L_c μμ , F_dyn=3) ---------- | |
| Xc_cnt = [] | |
| Xc_dist = [] | |
| Xc_traf = [] | |
| for k in range(self.seq_len, 0, -1): | |
| t_k = t - k | |
| cnt_map, dist_map, traf_map, _, _ = self._build_map_at_t(t_k) | |
| Xc_cnt.append(cnt_map) | |
| Xc_dist.append(dist_map) | |
| Xc_traf.append(traf_map) | |
| # (L_c, R, C, 3) = [cnt, dist, traffic] | |
| X_c = np.stack( | |
| [ | |
| np.stack(Xc_cnt, axis=0), | |
| np.stack(Xc_dist, axis=0), | |
| np.stack(Xc_traf, axis=0), | |
| ], | |
| axis=-1, | |
| ) | |
| # ---------- period ---------- | |
| if self.period > 0 and self.num_periods > 0: | |
| Xp_cnt = [] | |
| Xp_dist = [] | |
| Xp_traf = [] | |
| for k in range(1, self.num_periods + 1): | |
| t_k = t - k * self.period | |
| cnt_map, dist_map, traf_map, _, _ = self._build_map_at_t(t_k) | |
| Xp_cnt.append(cnt_map) | |
| Xp_dist.append(dist_map) | |
| Xp_traf.append(traf_map) | |
| # (L_p, R, C, 3) | |
| X_p = np.stack( | |
| [ | |
| np.stack(Xp_cnt, axis=0), | |
| np.stack(Xp_dist, axis=0), | |
| np.stack(Xp_traf, axis=0), | |
| ], | |
| axis=-1, | |
| ) | |
| else: | |
| X_p = None | |
| # ---------- trend ---------- | |
| if self.trend > 0 and self.num_trends > 0: | |
| Xt_cnt = [] | |
| Xt_dist = [] | |
| Xt_traf = [] | |
| for k in range(1, self.num_trends + 1): | |
| t_k = t - k * self.trend | |
| cnt_map, dist_map, traf_map, _, _ = self._build_map_at_t(t_k) | |
| Xt_cnt.append(cnt_map) | |
| Xt_dist.append(dist_map) | |
| Xt_traf.append(traf_map) | |
| # (L_t, R, C, 3) | |
| X_t = np.stack( | |
| [ | |
| np.stack(Xt_cnt, axis=0), | |
| np.stack(Xt_dist, axis=0), | |
| np.stack(Xt_traf, axis=0), | |
| ], | |
| axis=-1, | |
| ) | |
| else: | |
| X_t = None | |
| # ---------- target & mask ---------- | |
| _, _, _, y_map, mask_map = self._build_map_at_t(t) | |
| # ---------- static ---------- | |
| X_s = self.static # (R, C, 3): [road_len_in_grid, n_links, n_segments] | |
| # ---------- μΈμ λ³μ ---------- | |
| if ( | |
| self.hour_arr is not None | |
| and self.dow_arr is not None | |
| and self.is_weekend_arr is not None | |
| ): | |
| h = float(self.hour_arr[t]) / 23.0 | |
| d = float(self.dow_arr[t]) / 6.0 | |
| w = float(self.is_weekend_arr[t]) | |
| ext = torch.tensor([h, d, w], dtype=torch.float32) | |
| else: | |
| ext = None | |
| # Torch λ³ν | |
| X_c = torch.from_numpy(X_c).float() # (L_c, R, C, 3) | |
| y_t = torch.from_numpy(y_map).float() # (R, C) | |
| m_t = torch.from_numpy(mask_map).float() # (R, C) | |
| X_s = X_s.float() # (R, C, 3) | |
| if X_p is not None: | |
| X_p = torch.from_numpy(X_p).float() # (L_p, R, C, 3) | |
| if X_t is not None: | |
| X_t = torch.from_numpy(X_t).float() # (L_t, R, C, 3) | |
| return { | |
| "X_closeness": X_c, | |
| "X_period": X_p, | |
| "X_trend": X_t, | |
| "X_static": X_s, | |
| "y": y_t, | |
| "mask": m_t, | |
| "t": t, | |
| "ext": ext, | |
| } | |
| # --------------------------------------------------------------------- | |
| # 2. ν΄μλλ³ parquet β panel / static / μκ°λ©ν μμ± | |
| # --------------------------------------------------------------------- | |
| def build_sparse_panel_and_static(GRID_RES_M: int): | |
| """ | |
| μ€λΉλ parquetμμ λͺ¨λΈ μ λ ₯μ© panel / static / μκ° λ©νλ°μ΄ν° μμ±. | |
| λμ (panel) (t_idx, r_idx, c_idx): | |
| - cnt : cnt_events (μ¬κ³ 건μ) | |
| - mean_time : μμμκ° νκ· | |
| - mean_dist : νμ₯거리 νκ· | |
| - traffic : GRID_SPEED (νκ· κ΅ν΅λ/μλ) | |
| μ μ (static) (R, C, 3): | |
| - road_len_in_grid | |
| - n_links | |
| - n_segments | |
| """ | |
| BASE_DIR = Path(__file__).resolve().parent | |
| DB_DIR = BASE_DIR / "DB" | |
| PREP_DIR = DB_DIR / "prepared" | |
| PATH_FACT_ACC_GRID = PREP_DIR / f"fact_accident_dispatch_grid_{GRID_RES_M}m.parquet" | |
| PATH_DYN_FEAT = PREP_DIR / f"dyn_features_grid_{GRID_RES_M}m.parquet" | |
| PATH_GRID_STATIC = PREP_DIR / f"grid_static_road_{GRID_RES_M}m.parquet" | |
| PATH_TIME_BIAS = PREP_DIR / "time_bias_features.parquet" | |
| print(f"[{GRID_RES_M}m] Loading prepared parquet files...") | |
| fact = pd.read_parquet(PATH_FACT_ACC_GRID) | |
| dyn = pd.read_parquet(PATH_DYN_FEAT) | |
| grid_static = pd.read_parquet(PATH_GRID_STATIC) | |
| time_bias = pd.read_parquet(PATH_TIME_BIAS) | |
| print(f"[{GRID_RES_M}m] fact shape:", fact.shape) | |
| print(f"[{GRID_RES_M}m] dyn shape:", dyn.shape) | |
| print(f"[{GRID_RES_M}m] grid_static shape:", grid_static.shape) | |
| print(f"[{GRID_RES_M}m] time_bias shape:", time_bias.shape) | |
| # r_idx / c_idx μ€λΉ | |
| if "r_idx" in fact.columns and "c_idx" in fact.columns: | |
| print(f"[{GRID_RES_M}m] Using existing r_idx / c_idx from fact table.") | |
| else: | |
| print(f"[{GRID_RES_M}m] r_idx / c_idx μμ. grid_row / grid_col κΈ°μ€μΌλ‘ μμ±.") | |
| row_vals = np.sort(fact["grid_row"].unique()) | |
| col_vals = np.sort(fact["grid_col"].unique()) | |
| row2idx = {r: i for i, r in enumerate(row_vals)} | |
| col2idx = {c: i for i, c in enumerate(col_vals)} | |
| fact["r_idx"] = fact["grid_row"].map(row2idx) | |
| fact["c_idx"] = fact["grid_col"].map(col2idx) | |
| T = int(fact["t_idx"].max()) + 1 | |
| R = int(fact["r_idx"].max()) + 1 | |
| C = int(fact["c_idx"].max()) + 1 | |
| print(f"[{GRID_RES_M}m] T (hours) = {T}, R = {R}, C = {C}") | |
| # ---------------- μκ° λ©νλ°μ΄ν° (time_bias) ---------------- | |
| time_bias = time_bias.sort_values("t_idx").drop_duplicates(subset=["t_idx"]) | |
| if time_bias["t_idx"].max() + 1 < T: | |
| T = int(time_bias["t_idx"].max()) + 1 | |
| print(f"[{GRID_RES_M}m] Adjusted T to {T} based on time_bias.") | |
| hour_arr = np.zeros(T, dtype=np.int16) | |
| dow_arr = np.zeros(T, dtype=np.int16) | |
| is_weekend_arr = np.zeros(T, dtype=np.int8) | |
| t_idx_meta = time_bias["t_idx"].to_numpy(dtype=int) | |
| hours = time_bias["hour"].to_numpy(dtype=int) | |
| dows = time_bias["weekday"].to_numpy(dtype=int) | |
| weekends = time_bias["is_weekend"].to_numpy(dtype=np.int8) | |
| hour_arr[t_idx_meta] = hours | |
| dow_arr[t_idx_meta] = dows | |
| is_weekend_arr[t_idx_meta] = weekends | |
| # ---------------- panel μμ± ---------------- | |
| print(f"[{GRID_RES_M}m] Aggregating mean response time per (t_idx, r_idx, c_idx)...") | |
| if "μμμκ°" not in fact.columns: | |
| raise ValueError(f"[{GRID_RES_M}m] fact ν μ΄λΈμ 'μμμκ°' 컬λΌμ΄ μμ΅λλ€.") | |
| # (t_idx, r_idx, c_idx)λ³ νκ· μμμκ° | |
| agg_y = ( | |
| fact | |
| .groupby(["t_idx", "r_idx", "c_idx"]) | |
| .agg( | |
| mean_time=("μμμκ°", "mean"), | |
| ) | |
| .reset_index() | |
| ) | |
| # dyn: (grid_id, λ¨μ_m, t_idx)λ³ cnt_events, mean_dist, GRID_SPEED, link_cnt | |
| grid_map = ( | |
| fact[["grid_id", "λ¨μ_m", "r_idx", "c_idx"]] | |
| .drop_duplicates(subset=["grid_id", "λ¨μ_m"]) | |
| ) | |
| dyn = dyn.merge( | |
| grid_map, | |
| on=["grid_id", "λ¨μ_m"], | |
| how="left", | |
| validate="many_to_one", | |
| ) | |
| dyn = dyn.dropna(subset=["r_idx", "c_idx"]).copy() | |
| dyn["r_idx"] = dyn["r_idx"].astype(int) | |
| dyn["c_idx"] = dyn["c_idx"].astype(int) | |
| # dyn + mean_time merge | |
| panel = dyn.merge( | |
| agg_y, | |
| on=["t_idx", "r_idx", "c_idx"], | |
| how="left", | |
| validate="one_to_one", | |
| ) | |
| # μ΄λ¦ μ 리: λͺ¨λΈμμ μ¬μ©ν μ΄λ¦ | |
| panel = panel.rename( | |
| columns={ | |
| "cnt_events": "cnt", | |
| "GRID_SPEED": "traffic", # λμ traffic νΌμ² | |
| # link_cntλ μ§κΈμ μ¬μ© μ ν¨ (μνλ©΄ staticμΌλ‘ λ°λ‘ μΈ μ μμ) | |
| } | |
| ) | |
| for col in ["cnt", "mean_dist", "traffic", "mean_time"]: | |
| if col not in panel.columns: | |
| raise ValueError(f"[{GRID_RES_M}m] panelμ '{col}' 컬λΌμ΄ μμ΅λλ€.") | |
| panel[col] = panel[col].fillna(0.0).astype(np.float32) | |
| panel = panel[["t_idx", "r_idx", "c_idx", | |
| "cnt", "mean_time", "mean_dist", "traffic"]].copy() | |
| print(f"[{GRID_RES_M}m] panel rows:", len(panel)) | |
| # ---------------- static: (R, C, 3) ---------------- | |
| print(f"[{GRID_RES_M}m] Building static grid features...") | |
| row_map = fact[["grid_row", "r_idx"]].drop_duplicates() | |
| col_map = fact[["grid_col", "c_idx"]].drop_duplicates() | |
| row2idx = dict(zip(row_map["grid_row"], row_map["r_idx"])) | |
| col2idx = dict(zip(col_map["grid_col"], col_map["c_idx"])) | |
| grid_static["r_idx"] = grid_static["grid_row"].map(row2idx) | |
| grid_static["c_idx"] = grid_static["grid_col"].map(col2idx) | |
| grid_static = grid_static.dropna(subset=["r_idx", "c_idx"]).copy() | |
| grid_static["r_idx"] = grid_static["r_idx"].astype(int) | |
| grid_static["c_idx"] = grid_static["c_idx"].astype(int) | |
| for col in ["road_len_in_grid", "n_links", "n_segments"]: | |
| if col not in grid_static.columns: | |
| grid_static[col] = 0.0 | |
| else: | |
| grid_static[col] = grid_static[col].fillna(0.0) | |
| features = ["road_len_in_grid", "n_links", "n_segments"] | |
| F_stat = len(features) | |
| static = np.zeros((R, C, F_stat), dtype=np.float32) | |
| r_idx_arr = grid_static["r_idx"].to_numpy(dtype=int) | |
| c_idx_arr = grid_static["c_idx"].to_numpy(dtype=int) | |
| for i, feat in enumerate(features): | |
| vals = grid_static[feat].to_numpy(dtype=np.float32) | |
| static[r_idx_arr, c_idx_arr, i] = vals | |
| print(f"[{GRID_RES_M}m] static shape:", static.shape) | |
| return panel, static, T, R, C, hour_arr, dow_arr, is_weekend_arr | |