Add confidence intervals to Forecast model

- add `.low80`, `.high80`, `.low95`, and `.high95` columns
- add check contraints for the confidence intervals
- rename the `.method` column into `.model` for consistency
This commit is contained in:
Alexander Hess 2021-01-20 16:57:39 +01:00
commit f37d8adb9d
Signed by: alexander
GPG key ID: 344EA5AB10D868E0
7 changed files with 461 additions and 25 deletions

View file

@ -21,7 +21,11 @@ log_config.fileConfig(context.config.config_file_name)
def include_object(obj, _name, type_, _reflected, _compare_to):
"""Only include the clean schema into --autogenerate migrations."""
if type_ in {'table', 'column'} and obj.schema != umd_config.CLEAN_SCHEMA:
if ( # noqa:WPS337
type_ in {'table', 'column'}
and hasattr(obj, 'schema') # noqa:WPS421 => fix for rare edge case
and obj.schema != umd_config.CLEAN_SCHEMA
):
return False
return True

View file

@ -0,0 +1,124 @@
"""Add confidence intervals to forecasts.
Revision: #26711cd3f9b9 at 2021-01-20 16:08:21
Revises: #e40623e10405
"""
import os
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql
from urban_meal_delivery import configuration
revision = '26711cd3f9b9'
down_revision = 'e40623e10405'
branch_labels = None
depends_on = None
config = configuration.make_config('testing' if os.getenv('TESTING') else 'production')
def upgrade():
"""Upgrade to revision 26711cd3f9b9."""
op.alter_column(
'forecasts', 'method', new_column_name='model', schema=config.CLEAN_SCHEMA,
)
op.add_column(
'forecasts',
sa.Column('low80', postgresql.DOUBLE_PRECISION(), nullable=True),
schema=config.CLEAN_SCHEMA,
)
op.add_column(
'forecasts',
sa.Column('high80', postgresql.DOUBLE_PRECISION(), nullable=True),
schema=config.CLEAN_SCHEMA,
)
op.add_column(
'forecasts',
sa.Column('low95', postgresql.DOUBLE_PRECISION(), nullable=True),
schema=config.CLEAN_SCHEMA,
)
op.add_column(
'forecasts',
sa.Column('high95', postgresql.DOUBLE_PRECISION(), nullable=True),
schema=config.CLEAN_SCHEMA,
)
op.create_check_constraint(
op.f('ck_forecasts_on_ci_upper_and_lower_bounds'),
'forecasts',
"""
NOT (
low80 IS NULL AND high80 IS NOT NULL
OR
low80 IS NOT NULL AND high80 IS NULL
OR
low95 IS NULL AND high95 IS NOT NULL
OR
low95 IS NOT NULL AND high95 IS NULL
)
""",
schema=config.CLEAN_SCHEMA,
)
op.create_check_constraint(
op.f('prediction_must_be_within_ci'),
'forecasts',
"""
NOT (
prediction < low80
OR
prediction < low95
OR
prediction > high80
OR
prediction > high95
)
""",
schema=config.CLEAN_SCHEMA,
)
op.create_check_constraint(
op.f('ci_upper_bound_greater_than_lower_bound'),
'forecasts',
"""
NOT (
low80 > high80
OR
low95 > high95
)
""",
schema=config.CLEAN_SCHEMA,
)
op.create_check_constraint(
op.f('ci95_must_be_wider_than_ci80'),
'forecasts',
"""
NOT (
low80 < low95
OR
high80 > high95
)
""",
schema=config.CLEAN_SCHEMA,
)
def downgrade():
"""Downgrade to revision e40623e10405."""
op.alter_column(
'forecasts', 'model', new_column_name='method', schema=config.CLEAN_SCHEMA,
)
op.drop_column(
'forecasts', 'low80', schema=config.CLEAN_SCHEMA,
)
op.drop_column(
'forecasts', 'high80', schema=config.CLEAN_SCHEMA,
)
op.drop_column(
'forecasts', 'low95', schema=config.CLEAN_SCHEMA,
)
op.drop_column(
'forecasts', 'high95', schema=config.CLEAN_SCHEMA,
)