Minor improvements #33
@@ -5,7 +5,7 @@ type Celsius = float
|
|||||||
type Kelvin = float where _ >= 0
|
type Kelvin = float where _ >= 0
|
||||||
type Hectopascal = float
|
type Hectopascal = float
|
||||||
|
|
||||||
type Temperature = Celsius where in_range(-30.0, 100.0)
|
type Temperature = Celsius where in_range(-30.0, 100.0)(_)
|
||||||
type Pressure = Hectopascal where in_range(800.0, 1100.0)(_)
|
type Pressure = Hectopascal where in_range(800.0, 1100.0)(_)
|
||||||
type Humidity = float where is_percentage(_)
|
type Humidity = float where is_percentage(_)
|
||||||
type HeatIndex = float
|
type HeatIndex = float
|
||||||
@@ -38,7 +38,7 @@ alias RawData = Frame[
|
|||||||
|
|
||||||
alias Data = Frame[
|
alias Data = Frame[
|
||||||
station_id: StationID,
|
station_id: StationID,
|
||||||
timestamp: object,
|
timestamp: Any,
|
||||||
temperature: Temperature,
|
temperature: Temperature,
|
||||||
pressure: Pressure,
|
pressure: Pressure,
|
||||||
humidity: Humidity,
|
humidity: Humidity,
|
||||||
@@ -46,7 +46,7 @@ alias Data = Frame[
|
|||||||
|
|
||||||
alias DataWithHI = Frame[
|
alias DataWithHI = Frame[
|
||||||
station_id: StationID,
|
station_id: StationID,
|
||||||
timestamp: object,
|
timestamp: Any,
|
||||||
temperature: Temperature,
|
temperature: Temperature,
|
||||||
pressure: Pressure,
|
pressure: Pressure,
|
||||||
humidity: Humidity,
|
humidity: Humidity,
|
||||||
@@ -54,12 +54,12 @@ alias DataWithHI = Frame[
|
|||||||
]
|
]
|
||||||
|
|
||||||
alias DailyAverages = Frame[
|
alias DailyAverages = Frame[
|
||||||
timestamp: object,
|
timestamp: Any,
|
||||||
temperature: Mean[Temperature],
|
temperature: Mean[Temperature],
|
||||||
pressure: Mean[Pressure],
|
pressure: Mean[Pressure],
|
||||||
humidity: Mean[Humidity],
|
humidity: Mean[Humidity],
|
||||||
heat_index: Mean[HeatIndex],
|
heat_index: Mean[HeatIndex],
|
||||||
]
|
]
|
||||||
|
|
||||||
predicate limit_amplitude(max_amp: float)(ls: list[float]) = max(ls) - min(ls) <= max_amp
|
// 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)(_)
|
// type LowAmplitudeWave = list[float where _ >= 1] where limit_amplitude(10)(_)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ delta = end_ts - start_ts
|
|||||||
|
|
||||||
min_temp, max_temp = -30.0, 100.0
|
min_temp, max_temp = -30.0, 100.0
|
||||||
min_pres, max_pres = 800.0, 1100.0
|
min_pres, max_pres = 800.0, 1100.0
|
||||||
min_hum, max_hum = 0.0, 1.0
|
min_hum, max_hum = 0.0, 100.0
|
||||||
|
|
||||||
N = 3000
|
N = 3000
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ def daily_avg(df: DataWithHI):
|
|||||||
DailyAverages,
|
DailyAverages,
|
||||||
df.groupby(
|
df.groupby(
|
||||||
by=[
|
by=[
|
||||||
df["station_id"],
|
"station_id",
|
||||||
df["timestamp"].dt.day.rename("day"),
|
df["timestamp"].dt.day.rename("day"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ class FrameGroupByMethodRegistry(MethodRegistry[Call]):
|
|||||||
new_columns: list[DataFrameType.Column] = []
|
new_columns: list[DataFrameType.Column] = []
|
||||||
|
|
||||||
for column in call.groupby.frame.columns:
|
for column in call.groupby.frame.columns:
|
||||||
|
with self.reporter.with_context(f"in column '{column.name}'"):
|
||||||
column_groupby: ColumnGroupBy = ColumnGroupBy(column=column.type)
|
column_groupby: ColumnGroupBy = ColumnGroupBy(column=column.type)
|
||||||
result_type: Type = self.typer.call_method(
|
result_type: Type = self.typer.call_method(
|
||||||
location=call.location,
|
location=call.location,
|
||||||
|
|||||||
@@ -159,7 +159,10 @@ class FrameMethodRegistry(MethodRegistry[Call]):
|
|||||||
col_type2 = ColumnType(type=operand[1])
|
col_type2 = ColumnType(type=operand[1])
|
||||||
|
|
||||||
if col_type2 is not None:
|
if col_type2 is not None:
|
||||||
col_type = self._get_method_result(call, col_type1, col_type2, method)
|
with self.reporter.with_context(f"in column '{column.name}'"):
|
||||||
|
col_type = self._get_method_result(
|
||||||
|
call, col_type1, col_type2, method
|
||||||
|
)
|
||||||
|
|
||||||
new_column = DataFrameType.Column(
|
new_column = DataFrameType.Column(
|
||||||
index=column.index,
|
index=column.index,
|
||||||
@@ -595,8 +598,62 @@ class FrameMethodRegistry(MethodRegistry[Call]):
|
|||||||
)
|
)
|
||||||
return result.result
|
return result.result
|
||||||
|
|
||||||
|
def _filter_groupby_columns(
|
||||||
|
self, frame: DataFrameType, by: TypedExpr
|
||||||
|
) -> DataFrameType:
|
||||||
|
"""Remove columns passed as string literals in groupby's `by` argument
|
||||||
|
|
||||||
|
Args:
|
||||||
|
frame (DataFrameType): the original dataframe
|
||||||
|
by (TypedExpr): the by argument
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
DataFrameType: the filtered dataframe
|
||||||
|
"""
|
||||||
|
by_columns: list[str] = []
|
||||||
|
|
||||||
|
by_expr, _ = by
|
||||||
|
|
||||||
|
match by_expr:
|
||||||
|
case p.ListExpr(items=items):
|
||||||
|
for item in items:
|
||||||
|
match item:
|
||||||
|
case p.LiteralExpr(value=str() as name):
|
||||||
|
by_columns.append(name)
|
||||||
|
|
||||||
|
case p.LiteralExpr(value=str() as name):
|
||||||
|
by_columns.append(name)
|
||||||
|
|
||||||
|
if len(by_columns) == 0:
|
||||||
|
return frame
|
||||||
|
|
||||||
|
new_columns: list[DataFrameType.Column] = []
|
||||||
|
for column in frame.columns:
|
||||||
|
if column.name in by_columns:
|
||||||
|
continue
|
||||||
|
new_columns.append(
|
||||||
|
DataFrameType.Column(
|
||||||
|
index=len(new_columns),
|
||||||
|
name=column.name,
|
||||||
|
type=column.type,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return DataFrameType(columns=new_columns)
|
||||||
|
|
||||||
@method()
|
@method()
|
||||||
def groupby(self, call: Call) -> Type:
|
def groupby(self, call: Call) -> Type:
|
||||||
|
new_frame: DataFrameType = call.frame
|
||||||
|
|
||||||
|
by: Optional[TypedExpr] = None
|
||||||
|
if len(call.positional) != 0:
|
||||||
|
by = call.positional[0]
|
||||||
|
elif "by" in call.keywords:
|
||||||
|
by = call.keywords["by"]
|
||||||
|
|
||||||
|
if by is not None:
|
||||||
|
new_frame = self._filter_groupby_columns(call.frame, by)
|
||||||
|
|
||||||
bool_: Type = self.types.get_type("bool")
|
bool_: Type = self.types.get_type("bool")
|
||||||
function: Function = Function(
|
function: Function = Function(
|
||||||
params=ParamSpec(
|
params=ParamSpec(
|
||||||
@@ -626,7 +683,7 @@ class FrameMethodRegistry(MethodRegistry[Call]):
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
returns=FrameGroupBy(frame=call.frame),
|
returns=FrameGroupBy(frame=new_frame),
|
||||||
)
|
)
|
||||||
|
|
||||||
result: CallResult = self.dispatcher.get_result(
|
result: CallResult = self.dispatcher.get_result(
|
||||||
|
|||||||
@@ -407,8 +407,18 @@ class MidasTyper(m.Stmt.Visitor[None], m.Expr.Visitor[Type], m.Type.Visitor[Type
|
|||||||
return UnknownType()
|
return UnknownType()
|
||||||
|
|
||||||
def visit_constraint_type(self, type: m.ConstraintType) -> Type:
|
def visit_constraint_type(self, type: m.ConstraintType) -> Type:
|
||||||
|
base_type: Type = type.type.accept(self)
|
||||||
|
self._predicate_params["_"] = base_type
|
||||||
|
constraint_type: Type = self.type_of(type.constraint)
|
||||||
|
self._predicate_params = {}
|
||||||
|
if not self.types.is_subtype(constraint_type, self._bool):
|
||||||
|
self.reporter.error(
|
||||||
|
type.location,
|
||||||
|
f"Constraint must evaluate to a boolean, got {constraint_type}",
|
||||||
|
)
|
||||||
|
|
||||||
return ConstraintType(
|
return ConstraintType(
|
||||||
type=type.type.accept(self),
|
type=base_type,
|
||||||
constraint=type.constraint,
|
constraint=type.constraint,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -257,6 +257,9 @@ class PythonTyper(
|
|||||||
"""
|
"""
|
||||||
unfolded: Type = unfold_type(obj[1])
|
unfolded: Type = unfold_type(obj[1])
|
||||||
match unfolded:
|
match unfolded:
|
||||||
|
case TopType() | UnknownType():
|
||||||
|
return UnknownType()
|
||||||
|
|
||||||
case DataFrameType():
|
case DataFrameType():
|
||||||
return self.frame_mgr.call(
|
return self.frame_mgr.call(
|
||||||
method=method_name,
|
method=method_name,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from contextlib import contextmanager
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from midas.ast.location import Location
|
from midas.ast.location import Location
|
||||||
@@ -54,6 +55,7 @@ class FileReporter:
|
|||||||
def __init__(self, base_reporter: Reporter, path: Optional[str]) -> None:
|
def __init__(self, base_reporter: Reporter, path: Optional[str]) -> None:
|
||||||
self.base_reporter: Reporter = base_reporter
|
self.base_reporter: Reporter = base_reporter
|
||||||
self.path: Optional[str] = path
|
self.path: Optional[str] = path
|
||||||
|
self._context: list[str] = []
|
||||||
|
|
||||||
def for_file(self, path: Optional[str]) -> FileReporter:
|
def for_file(self, path: Optional[str]) -> FileReporter:
|
||||||
"""Create a new file reporter for the given path with the same base reporter
|
"""Create a new file reporter for the given path with the same base reporter
|
||||||
@@ -66,6 +68,14 @@ class FileReporter:
|
|||||||
"""
|
"""
|
||||||
return FileReporter(self.base_reporter, path)
|
return FileReporter(self.base_reporter, path)
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def with_context(self, ctx: str):
|
||||||
|
self._context.append(ctx)
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
self._context.pop()
|
||||||
|
|
||||||
def report(self, type: DiagnosticType, location: Location, message: str):
|
def report(self, type: DiagnosticType, location: Location, message: str):
|
||||||
"""Report a diagnostic to the base reporter
|
"""Report a diagnostic to the base reporter
|
||||||
|
|
||||||
@@ -74,6 +84,8 @@ class FileReporter:
|
|||||||
location (Location): the location of the diagnostic in the file
|
location (Location): the location of the diagnostic in the file
|
||||||
message (str): the diagnostic's message
|
message (str): the diagnostic's message
|
||||||
"""
|
"""
|
||||||
|
for ctx in self._context:
|
||||||
|
message = message + ", " + ctx
|
||||||
self.base_reporter.report(self.path, type, location, message)
|
self.base_reporter.report(self.path, type, location, message)
|
||||||
|
|
||||||
def error(self, location: Location, message: str):
|
def error(self, location: Location, message: str):
|
||||||
|
|||||||
Reference in New Issue
Block a user