diff --git a/examples/02_demonstration/weather/custom_types.midas b/examples/02_demonstration/weather/custom_types.midas new file mode 100644 index 0000000..289b358 --- /dev/null +++ b/examples/02_demonstration/weather/custom_types.midas @@ -0,0 +1,65 @@ +predicate in_range(min: float, max: float)(v: float) = min <= v & v <= max +predicate is_percentage = in_range(0.0, 100.0) + +type Celsius = float +type Kelvin = float where _ >= 0 +type Hectopascal = float + +type Temperature = Celsius where in_range(-30.0, 100.0) +type Pressure = Hectopascal where in_range(800.0, 1100.0)(_) +type Humidity = float where is_percentage(_) +type HeatIndex = float + +type StationID = str where len(_) == 3 & _.isupper() + +type Mean[T <: float] = float + +extend Celsius { + def __add__: fn(Celsius, /) -> Celsius + def __sub__: fn(Celsius, /) -> Celsius +} + +extend Kelvin { + def __add__: fn(Kelvin, /) -> Kelvin +} + +extend Hectopascal { + def __add__: fn(Hectopascal, /) -> Hectopascal + def __sub__: fn(Hectopascal, /) -> Hectopascal +} + +alias RawData = Frame[ + station_id: str, + timestamp: str, + temperature: float, + pressure: float, + humidity: float, +] + +alias Data = Frame[ + station_id: StationID, + timestamp: object, + temperature: Temperature, + pressure: Pressure, + humidity: Humidity, +] + +alias DataWithHI = Frame[ + station_id: StationID, + timestamp: object, + temperature: Temperature, + pressure: Pressure, + humidity: Humidity, + heat_index: HeatIndex, +] + +alias DailyAverages = Frame[ + timestamp: object, + temperature: Mean[Temperature], + pressure: Mean[Pressure], + humidity: Mean[Humidity], + heat_index: Mean[HeatIndex], +] + +predicate limit_amplitude(max_amp: float)(ls: list[float]) = max(ls) - min(ls) <= max_amp +type LowAmplitudeWave = list[float where _ >= 1] where limit_amplitude(10)(_) diff --git a/examples/02_demonstration/weather/gen_data.py b/examples/02_demonstration/weather/gen_data.py new file mode 100644 index 0000000..bdd3b5f --- /dev/null +++ b/examples/02_demonstration/weather/gen_data.py @@ -0,0 +1,43 @@ +import datetime +import random + +import pandas as pd + +stations = ["SIO", "AIG", "ZER"] + +start_ts = datetime.datetime(2026, 1, 1) +end_ts = datetime.datetime(2027, 1, 1) +delta = end_ts - start_ts + +min_temp, max_temp = -30.0, 100.0 +min_pres, max_pres = 800.0, 1100.0 +min_hum, max_hum = 0.0, 1.0 + +N = 3000 + +rows: list[tuple[str, datetime.datetime, float, float, float]] = [] + +for _ in range(N): + ts = random.random() * delta + start_ts + rows.append( + ( + random.choice(stations), + ts, + random.random() * (max_temp - min_temp) + min_temp, + random.random() * (max_pres - min_pres) + min_pres, + random.random() * (max_hum - min_hum) + min_hum, + ) + ) + +df = pd.DataFrame( + rows, + columns=[ + "station_id", + "timestamp", + "temperature", + "pressure", + "humidity", + ], +) +df = df.sort_values(by=["timestamp", "station_id"]) +df.to_csv("data.csv", index=False) diff --git a/examples/02_demonstration/weather/pipeline.py b/examples/02_demonstration/weather/pipeline.py new file mode 100644 index 0000000..04aa88c --- /dev/null +++ b/examples/02_demonstration/weather/pipeline.py @@ -0,0 +1,69 @@ +from pathlib import Path + +import matplotlib.pyplot as plt +import pandas as pd +from custom_types import DailyAverages, Data, DataWithHI, HeatIndex, RawData + +from midas.typing import Column, cast, unsafe_cast + + +def load_data(path: Path) -> RawData: + return cast(RawData, pd.read_csv(path)) + + +def convert_data(raw_df: RawData) -> Data: + new_df = raw_df.copy() + new_df["timestamp"] = cast( + Column[object], + pd.to_datetime(new_df["timestamp"]), + ) + return cast(Data, new_df) + + +def compute_heat_index(df: Data): + df["heat_index"] = cast( + Column[HeatIndex], + ( + df["temperature"] * 2.0 + + df["humidity"] * 10.0 + - df["temperature"] * df["humidity"] * 0.2 + ), + ) + return df + + +def daily_avg(df: DataWithHI): + return cast( + DailyAverages, + df.groupby( + by=[ + df["station_id"], + df["timestamp"].dt.day.rename("day"), + ], + ) + .mean() + .sort_values(by="timestamp"), + ) + + +def plot(df: DailyAverages): + stations = unsafe_cast(list[str], list(df.index.get_level_values(0).unique())) + for station in stations: + sub_df = unsafe_cast(DailyAverages, df.loc[station]) + # plt.plot(sub_df["timestamp"], sub_df["temperature"]) + plt.plot(sub_df["timestamp"], sub_df["heat_index"]) + plt.show() + + +def main(): + raw_df: RawData = load_data(Path("data.csv")) + df: Data = convert_data(raw_df) + + with_hi = compute_heat_index(df) + dailies = daily_avg(with_hi) + print(dailies) + plot(dailies) + + +if __name__ == "__main__": + main()