| |
| from __future__ import annotations |
| import pandas as pd |
| import numpy as np |
|
|
| def print_monthly_tables_split(df: pd.DataFrame, location: str, time_step_hours: float): |
| """ |
| Table 1: Monthly HVAC electricity + temps |
| Table 2: Monthly occupancy |
| """ |
| drop_cols = [c for c in df.columns if c.startswith("has_") or c == "feature_registry"] |
| df_clean = df.drop(columns=drop_cols, errors="ignore").copy() |
|
|
| if "month" not in df_clean.columns: |
| return |
|
|
| df_clean["month"] = df_clean["month"].round().astype(int) |
|
|
| |
| energy_cols = [] |
| peak_cols = [] |
| if "elec_power" in df_clean.columns: |
| if "elec_power_kw" not in df_clean.columns: |
| df_clean["elec_power_kw"] = df_clean["elec_power"] / 1000.0 |
| if "elec_energy_kwh" not in df_clean.columns: |
| df_clean["elec_energy_kwh"] = df_clean["elec_power_kw"] * time_step_hours |
| energy_cols.append("elec_energy_kwh") |
| peak_cols.append("elec_power_kw") |
|
|
| |
| temp_cols = [c for c in ["outdoor_temp", "core_temp", "perim1_temp", "perim2_temp", "perim3_temp", "perim4_temp"] |
| if c in df_clean.columns] |
| |
| agg1 = {c: "sum" for c in energy_cols} |
| agg1.update({c: "max" for c in peak_cols}) |
| agg1.update({c: "mean" for c in temp_cols}) |
| tbl1 = df_clean.groupby("month").agg(agg1).sort_index() |
|
|
| |
| occ_cols = [c for c in df_clean.columns if c.endswith("_occ_count")] |
| tbl2 = df_clean.groupby("month")[occ_cols].mean().sort_index() if occ_cols else pd.DataFrame() |
| if not tbl2.empty: |
| tbl2["occ_mean_total"] = tbl2.sum(axis=1) |
|
|
| print("\n" + "=" * 110) |
| print(f"MONTHLY ELECTRICITY + TEMPERATURE — {location}") |
| print("=" * 110) |
| print(tbl1.round(2).to_string()) |
| print("\n" + "=" * 110) |
| print(f"MONTHLY OCCUPANCY — {location}") |
| print("=" * 110) |
| print(tbl2.round(3).to_string()) |
| print("=" * 110 + "\n") |
|
|
|
|
| def print_monthly_tables_extra(df: pd.DataFrame, location: str) -> None: |
| |
| d = df.copy() |
| if "month" not in d.columns: |
| return |
| d["month"] = d["month"].round().astype(int) |
|
|
| violation_cols = [c for c in ["comfort_violation_degCh", "comfort_violation_fixed_degCh"] if c in d.columns] |
| tbl_sums = d.groupby("month")[violation_cols].sum() |
|
|
|
|
| occ_cols = [c for c in d.columns if c.endswith("_occ_count")] |
| total_occ = d[occ_cols].sum(axis=1) |
| is_occupied = total_occ > 1e-6 |
| d_occ = d[is_occupied].copy() |
|
|
|
|
| def person_weighted_ppd(group): |
| occ = group[occ_cols].sum(axis=1) |
| raw_ppd = group["ppd_weighted"] |
| return (raw_ppd * occ).sum() / occ.sum() if occ.sum() > 0 else np.nan |
|
|
| if not d_occ.empty and "ppd_weighted" in d_occ.columns: |
| ppd_monthly = d_occ.groupby("month", group_keys=False).apply(person_weighted_ppd) |
| ppd_monthly = ppd_monthly.clip(lower=5.0) |
| |
| pmv_monthly = d_occ.groupby("month")["pmv_weighted"].mean() |
| rh_monthly = d_occ.groupby("month")["rh_weighted"].mean() |
|
|
| tbl_means = pd.DataFrame({ |
| "ppd_weighted": ppd_monthly, |
| "pmv_weighted": pmv_monthly, |
| "rh_weighted_%": rh_monthly |
| }) |
| tbl3a = pd.concat([tbl_sums, tbl_means], axis=1).sort_index() |
| else: |
| tbl3a = tbl_sums |
|
|
|
|
| outdoor_vars = [c for c in ["outdoor_temp", "outdoor_dewpoint", "outdoor_wetbulb"] if c in d.columns] |
| tbl3b = d.groupby("month")[outdoor_vars].mean().sort_index() if outdoor_vars else None |
|
|
|
|
| print("\n" + "=" * 110) |
| print(f"MONTHLY COMFORT OUTCOMES (Occupancy Weighted) — {location}") |
| print("=" * 110) |
| print(tbl3a.round(3).to_string()) |
| print("=" * 110) |
|
|
|
|
| if tbl3b is not None: |
| print("\n" + "=" * 110) |
| print(f"MONTHLY OUTDOOR CONDITIONS — {location}") |
| print("=" * 110) |
| print(tbl3b.round(3).to_string()) |
| print("=" * 110) |
|
|