MeteoNorm-RF / scripts /fake_data.py
zhangrenchao's picture
Publish MeteoNorm-RF reproduction
b11ef36 verified
Raw
History Blame Contribute Delete
3.3 kB
"""Generate compact hourly data retaining all 12 stations and six pollutants."""
import sys
from pathlib import Path
import numpy as np
import yaml
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from model.meteonorm_rf import BASE_FEATURES, FORMAT_VERSION, POLLUTANTS, STATIONS
def main():
config = yaml.safe_load((ROOT / "conf/config.yaml").read_text())
rng = np.random.default_rng(int(config["seed"]))
days = int(config["data"]["synthetic_days"])
hours = np.tile(np.arange(days * 24), len(STATIONS))
station = np.repeat(np.arange(len(STATIONS)), days * 24)
day = hours // 24
hour = hours % 24
doy = day + int(config["data"]["start_day_of_year"])
weekend = ((day + 1) % 7 >= 5).astype(np.float32)
seasonal = np.sin(2 * np.pi * (doy - 172) / 365.25)
diurnal = np.sin(2 * np.pi * (hour - 14) / 24)
synoptic = np.sin(2 * np.pi * day / 6.5 + station * 0.17)
wind_speed = np.maximum(0.2, 2.7 + 1.1 * synoptic + rng.normal(0, .55, len(hours)))
wind_direction = np.mod(185 + 75 * synoptic + station * 9 + rng.normal(0, 28, len(hours)), 360)
temperature = 13 + 12 * seasonal + 5.5 * diurnal + rng.normal(0, 1.4, len(hours))
rh = np.clip(52 - 18 * seasonal - 10 * diurnal - 3 * wind_speed + rng.normal(0, 7, len(hours)), 15, 98)
pressure = 1013 - .32 * temperature + 4 * np.cos(2 * np.pi * day / 8) + rng.normal(0, 1.8, len(hours))
ttrend = 2013.0 + hours / (24 * 365.25)
emission = 1.0 - .22 * day / max(days - 1, 1)
stagnation = np.exp(-wind_speed / 3.2) * (0.65 + rh / 150)
rush = ((hour >= 7) & (hour <= 10)) | ((hour >= 17) & (hour <= 21))
station_factor = 0.82 + station * .025
pm25 = (22 + 78 * stagnation + 16 * rush) * emission * station_factor
pm10 = 28 + 1.22 * pm25 + 7 * wind_speed
no2 = (15 + 37 * stagnation + 19 * rush) * emission * station_factor
so2 = (4 + 25 * stagnation) * emission ** 1.8 * (1.1 - .15 * seasonal)
o3 = np.maximum(3, 30 + 29 * np.maximum(diurnal, -.4) + 1.2 * temperature - .28 * no2)
co = (0.25 + .012 * pm25 + .008 * no2) * emission
pollution = np.column_stack([pm25, pm10, no2, so2, o3, co])
pollution *= rng.lognormal(0, .09, pollution.shape)
output = ROOT / config["data"]["path"]
output.parent.mkdir(parents=True, exist_ok=True)
np.savez_compressed(output, format_version=np.array(FORMAT_VERSION),
station_names=np.asarray(STATIONS), pollutant_names=np.asarray(POLLUTANTS),
station_id=station.astype(np.int16), timestamp_hour=hours.astype(np.int32),
ttrend=ttrend.astype(np.float32), day_of_year=doy.astype(np.float32),
weekend=weekend, hour=hour.astype(np.float32),
wind_speed=wind_speed.astype(np.float32), wind_direction=wind_direction.astype(np.float32),
pressure=pressure.astype(np.float32), temperature=temperature.astype(np.float32),
relative_humidity=rh.astype(np.float32), pollution=pollution.astype(np.float32),
feature_names=np.asarray(BASE_FEATURES))
print(f"data={output.relative_to(ROOT)} rows={len(hours)} stations=12 pollutants=6 hourly_days={days}")
if __name__ == "__main__":
main()