Spaces:
Sleeping
Sleeping
File size: 5,168 Bytes
e5ef030 | 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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | """
One-time data prep: AMPds2 -> compact hourly parquet for the Space.
Usage
-----
python prepare_data.py /path/to/dataverse_files.zip
python prepare_data.py /path/to/AMPds2_folder
python prepare_data.py # uses the default Colab/Drive path below
Produces data/ampds2_hourly.parquet (small enough to commit to the HF Space).
Resamples the minutely AMPds2 active-power readings to hourly means.
"""
import io
import os
import sys
import zipfile
import tempfile
import numpy as np
import pandas as pd
DEFAULT_INPUT = "/content/drive/MyDrive/AMPds2/dataverse_files.zip"
OUT = "data/ampds2_hourly.parquet"
METER_RE = __import__("re").compile(r"^[A-Z][A-Z0-9]{1,2}E$") # WHE, FGE, HPE, B1E, B2E, ...
def _read_table(buf_or_path, name):
sep = "\t" if name.lower().endswith((".tab", ".tsv")) else ","
return pd.read_csv(buf_or_path, sep=sep, low_memory=False)
def _score(df):
"""How likely this table is the wide active-power file (meter-code columns incl. WHE)."""
cols = [str(c).strip().upper() for c in df.columns]
meters = [c for c in cols if METER_RE.match(c)]
return (len(meters), "WHE" in cols)
def _iter_tables(path):
"""Yield (name, dataframe) for every csv/tab/tsv found in a zip (incl. nested) or folder."""
if zipfile.is_zipfile(path):
with zipfile.ZipFile(path) as z:
for m in z.namelist():
low = m.lower()
if low.endswith(".zip"):
with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tf:
tf.write(z.read(m)); nested = tf.name
try:
yield from _iter_tables(nested)
finally:
os.unlink(nested)
elif low.endswith((".csv", ".tab", ".tsv")):
try:
yield m, _read_table(io.BytesIO(z.read(m)), m)
except Exception as e:
print(" skip", m, "->", e)
elif os.path.isdir(path):
for root, _, files in os.walk(path):
for f in files:
if f.lower().endswith((".csv", ".tab", ".tsv")):
fp = os.path.join(root, f)
try:
yield f, _read_table(fp, f)
except Exception as e:
print(" skip", f, "->", e)
else: # single file
name = os.path.basename(path)
yield name, _read_table(path, name)
def find_power_table(path):
best, best_score, best_name = None, (-1, False), None
for name, df in _iter_tables(path):
# an active-power file is preferred (…_P… in AMPds2); compute a score regardless
s = _score(df)
bonus = (1, s[1]) if ("_p" in name.lower() or name.lower().startswith("electricity")) else (0, s[1])
score = (s[0] + bonus[0] * 100, s[1])
print(f" candidate {name:40s} meters={s[0]:2d} has_WHE={s[1]}")
if score > best_score:
best, best_score, best_name = df, score, name
if best is None or best_score[0] < 1:
raise SystemExit("No AMPds2 power table found (need a CSV with meter-code columns like WHE, FGE).")
print(" -> using:", best_name)
return best
def to_hourly(df):
df = df.copy()
df.columns = [str(c).strip() for c in df.columns]
upper = {c: c.upper() for c in df.columns}
# timestamp: prefer an explicit UNIX seconds column, else first datetime-like column
ts_col = next((c for c in df.columns if upper[c] in ("UNIX_TS", "TS", "TIMESTAMP", "TIME")), None)
if ts_col is not None and np.issubdtype(df[ts_col].dropna().dtype, np.number):
idx = pd.to_datetime(df[ts_col], unit="s")
elif ts_col is not None:
idx = pd.to_datetime(df[ts_col], errors="coerce")
else:
ts_col = df.columns[0]
idx = pd.to_datetime(df[ts_col], errors="coerce")
if idx.isna().mean() > 0.5: # maybe it's unix seconds in col 0
idx = pd.to_datetime(pd.to_numeric(df[ts_col], errors="coerce"), unit="s")
df.index = idx
meters = [c for c in df.columns if METER_RE.match(upper[c]) and c != ts_col]
out = df[meters].apply(pd.to_numeric, errors="coerce")
out.columns = [upper[c] for c in meters]
out = out[~out.index.isna()].sort_index()
hourly = out.resample("h").mean()
return hourly
def main():
inp = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_INPUT
if not os.path.exists(inp):
raise SystemExit(f"Input not found: {inp}\nPass the path to dataverse_files.zip or the AMPds2 folder.")
print("Reading AMPds2 from:", inp)
raw = find_power_table(inp)
print(f" raw shape: {raw.shape}")
hourly = to_hourly(raw)
os.makedirs(os.path.dirname(OUT), exist_ok=True)
hourly.index.name = "ts"
hourly.to_parquet(OUT)
mb = os.path.getsize(OUT) / 1e6
print(f"\nWrote {OUT} ({hourly.shape[0]} hours x {hourly.shape[1]} meters, {mb:.1f} MB)")
print("Columns:", list(hourly.columns))
print("Commit this parquet to your Space (or upload via the Files tab).")
if __name__ == "__main__":
main()
|