File size: 3,301 Bytes
b11ef36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""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()