Skip to content

Commit

Permalink
Merge pull request #14 from EnergieID/develop
Browse files Browse the repository at this point in the history
Merge v0.1.18
  • Loading branch information
JrtPec authored Oct 2, 2024
2 parents 2b546ae + 45bac66 commit a4962b5
Show file tree
Hide file tree
Showing 11 changed files with 56,775 additions and 24,852 deletions.
80,076 changes: 55,725 additions & 24,351 deletions data/dyntar/sample_output.json

Large diffs are not rendered by default.

275 changes: 233 additions & 42 deletions demo_dyntar_analysis.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion openenergyid/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Open Energy ID Python SDK."""

__version__ = "0.1.17"
__version__ = "0.1.18"

from .enums import Granularity
from .models import TimeDataFrame, TimeSeries
Expand Down
4 changes: 4 additions & 0 deletions openenergyid/dyntar/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@
HEATMAP_DELIVERED = "heatmap_delivered"
HEATMAP_EXPORTED = "heatmap_exported"
HEATMAP_TOTAL = "heatmap_total"

HEATMAP_DELIVERED_DESCRIPTION = "heatmap_delivered_description"
HEATMAP_EXPORTED_DESCRIPTION = "heatmap_exported_description"
HEATMAP_TOTAL_DESCRIPTION = "heatmap_total_description"
117 changes: 115 additions & 2 deletions openenergyid/dyntar/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
HEATMAP_DELIVERED,
HEATMAP_EXPORTED,
HEATMAP_TOTAL,
HEATMAP_DELIVERED_DESCRIPTION,
HEATMAP_EXPORTED_DESCRIPTION,
HEATMAP_TOTAL_DESCRIPTION,
)


Expand Down Expand Up @@ -147,8 +150,7 @@ def extend_dataframe_with_heatmap(df: pd.DataFrame, inplace: bool = False) -> pd

# Invert scores so that positive values indicate a positive impact
heatmap_score_delivered = -heatmap_score_delivered
heatmap_score_exported = -heatmap_score_exported
heatmap_score_combined = heatmap_score_delivered - heatmap_score_exported
heatmap_score_combined = heatmap_score_delivered + heatmap_score_exported

df[HEATMAP_DELIVERED] = heatmap_score_delivered
df[HEATMAP_EXPORTED] = heatmap_score_exported
Expand All @@ -159,6 +161,116 @@ def extend_dataframe_with_heatmap(df: pd.DataFrame, inplace: bool = False) -> pd
return None


def extend_dataframe_with_heatmap_description(
df: pd.DataFrame, inplace: bool = False
) -> pd.DataFrame | None:
"""Extend a DataFrame with the heatmap description columns."""
if not inplace:
df = df.copy()

# Delivered

# Where Heatmap is 0, we put a desription of 0 (No impact)
df[HEATMAP_DELIVERED_DESCRIPTION] = df[HEATMAP_DELIVERED].apply(
lambda x: 0 if x == 0 else float("NaN")
)
# When the energy delta is positive, and the price delta is positive, we put a description of 1 (high consumption, high price)
df[HEATMAP_DELIVERED_DESCRIPTION] = df.apply(
lambda x: 1
if x[PRICE_ELECTRICITY_DELIVERED] > x[RLP_WEIGHTED_PRICE_DELIVERED]
and x[ELECTRICITY_DELIVERED_SMR3] > x[ELECTRICITY_DELIVERED_SMR2]
else x[HEATMAP_DELIVERED_DESCRIPTION],
axis=1,
)
# When the energy delta is negative, and the price delta is positive, we put a description of 2 (low consumption, high price)
df[HEATMAP_DELIVERED_DESCRIPTION] = df.apply(
lambda x: 2
if x[PRICE_ELECTRICITY_DELIVERED] > x[RLP_WEIGHTED_PRICE_DELIVERED]
and x[ELECTRICITY_DELIVERED_SMR3] < x[ELECTRICITY_DELIVERED_SMR2]
else x[HEATMAP_DELIVERED_DESCRIPTION],
axis=1,
)
# When the energy delta is positive, and the price delta is negative, we put a description of 3 (high consumption, low price)
df[HEATMAP_DELIVERED_DESCRIPTION] = df.apply(
lambda x: 3
if x[PRICE_ELECTRICITY_DELIVERED] < x[RLP_WEIGHTED_PRICE_DELIVERED]
and x[ELECTRICITY_DELIVERED_SMR3] > x[ELECTRICITY_DELIVERED_SMR2]
else x[HEATMAP_DELIVERED_DESCRIPTION],
axis=1,
)
# When the energy delta is negative, and the price delta is negative, we put a description of 4 (low consumption, low price)
df[HEATMAP_DELIVERED_DESCRIPTION] = df.apply(
lambda x: 4
if x[PRICE_ELECTRICITY_DELIVERED] < x[RLP_WEIGHTED_PRICE_DELIVERED]
and x[ELECTRICITY_DELIVERED_SMR3] < x[ELECTRICITY_DELIVERED_SMR2]
else x[HEATMAP_DELIVERED_DESCRIPTION],
axis=1,
)
# All other cases are put as 0
df[HEATMAP_DELIVERED_DESCRIPTION] = df[HEATMAP_DELIVERED_DESCRIPTION].replace(np.nan, 0)

# Exported

# Where Heatmap is 0, we put a desription of 0 (No impact)
df[HEATMAP_EXPORTED_DESCRIPTION] = df[HEATMAP_EXPORTED].apply(
lambda x: 0 if x == 0 else float("NaN")
)
# When the energy delta is positive, and the price delta is positive, we put a description of 5 (high injection, high price)
df[HEATMAP_EXPORTED_DESCRIPTION] = df.apply(
lambda x: 5
if x[PRICE_ELECTRICITY_EXPORTED] > x[SPP_WEIGHTED_PRICE_EXPORTED]
and x[ELECTRICITY_EXPORTED_SMR3] > x[ELECTRICITY_EXPORTED_SMR2]
else x[HEATMAP_EXPORTED_DESCRIPTION],
axis=1,
)
# When the energy delta is negative, and the price delta is positive, we put a description of 6 (low injection, high price)
df[HEATMAP_EXPORTED_DESCRIPTION] = df.apply(
lambda x: 6
if x[PRICE_ELECTRICITY_EXPORTED] > x[SPP_WEIGHTED_PRICE_EXPORTED]
and x[ELECTRICITY_EXPORTED_SMR3] < x[ELECTRICITY_EXPORTED_SMR2]
else x[HEATMAP_EXPORTED_DESCRIPTION],
axis=1,
)
# When the energy delta is positive, and the price delta is negative, we put a description of 7 (high injection, low price)
df[HEATMAP_EXPORTED_DESCRIPTION] = df.apply(
lambda x: 7
if x[PRICE_ELECTRICITY_EXPORTED] < x[SPP_WEIGHTED_PRICE_EXPORTED]
and x[ELECTRICITY_EXPORTED_SMR3] > x[ELECTRICITY_EXPORTED_SMR2]
else x[HEATMAP_EXPORTED_DESCRIPTION],
axis=1,
)
# When the energy delta is negative, and the price delta is negative, we put a description of 8 (low injection, low price)
df[HEATMAP_EXPORTED_DESCRIPTION] = df.apply(
lambda x: 8
if x[PRICE_ELECTRICITY_EXPORTED] < x[SPP_WEIGHTED_PRICE_EXPORTED]
and x[ELECTRICITY_EXPORTED_SMR3] < x[ELECTRICITY_EXPORTED_SMR2]
else x[HEATMAP_EXPORTED_DESCRIPTION],
axis=1,
)
# All other cases are put as 0
df[HEATMAP_EXPORTED_DESCRIPTION] = df[HEATMAP_EXPORTED_DESCRIPTION].replace(np.nan, 0)

# Total

# We see which of the individual heatmaps has the highest absolute value
# We put the description of the highest absolute value
df[HEATMAP_TOTAL_DESCRIPTION] = df.apply(
lambda x: x[HEATMAP_DELIVERED_DESCRIPTION]
if abs(x[HEATMAP_DELIVERED]) > abs(x[HEATMAP_EXPORTED])
else x[HEATMAP_EXPORTED_DESCRIPTION],
axis=1,
)
# Where Heatmap is 0, we put a desription of 0 (No impact)
df[HEATMAP_TOTAL_DESCRIPTION] = df.apply(
lambda x: 0 if x[HEATMAP_TOTAL] == 0 else x[HEATMAP_TOTAL_DESCRIPTION], axis=1
)
# All other cases are put as 0
df[HEATMAP_TOTAL_DESCRIPTION] = df[HEATMAP_TOTAL_DESCRIPTION].replace(np.nan, 0)

if not inplace:
return df


def calculate_dyntar_columns(df: pd.DataFrame, inplace: bool = False) -> pd.DataFrame | None:
"""Calculate all columns required for the dynamic tariff analysis."""
if not inplace:
Expand All @@ -168,6 +280,7 @@ def calculate_dyntar_columns(df: pd.DataFrame, inplace: bool = False) -> pd.Data
extend_dataframe_with_costs(df, inplace=True)
extend_dataframe_with_weighted_prices(df, inplace=True)
extend_dataframe_with_heatmap(df, inplace=True)
extend_dataframe_with_heatmap_description(df, inplace=True)

if not inplace:
return df
Expand Down
17 changes: 12 additions & 5 deletions openenergyid/dyntar/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Models for dynamic tariff analysis."""

from typing import Literal
from pydantic import Field, conlist
from pydantic import Field, conlist, confloat

from openenergyid.models import TimeDataFrame

Expand Down Expand Up @@ -33,6 +33,9 @@
"heatmap_delivered",
"heatmap_exported",
"heatmap_total",
"heatmap_delivered_description",
"heatmap_exported_description",
"heatmap_total_description",
]


Expand All @@ -46,11 +49,11 @@ class DynamicTariffAnalysisInput(TimeDataFrame):
)
data: list[
conlist(
item_type=float,
item_type=confloat(allow_inf_nan=True),
min_length=len(RequiredColumns.__args__),
max_length=len(RequiredColumns.__args__),
) # type: ignore
] = Field(examples=[[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]])
] = Field(examples=[[0.0] * len(RequiredColumns.__args__)])


class DynamicTariffAnalysisOutput(TimeDataFrame):
Expand All @@ -62,5 +65,9 @@ class DynamicTariffAnalysisOutput(TimeDataFrame):
examples=[OutputColumns.__args__],
)
data: list[
conlist(item_type=float, min_length=1, max_length=len(OutputColumns.__args__)) # type: ignore
] = Field(examples=[[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]])
conlist(
item_type=confloat(allow_inf_nan=True),
min_length=1,
max_length=len(OutputColumns.__args__),
) # type: ignore
] = Field(examples=[[0.0] * len(OutputColumns.__args__)])
18 changes: 3 additions & 15 deletions openenergyid/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import Self

import pandas as pd
from pydantic import BaseModel, field_validator
from pydantic import BaseModel


class TimeSeriesBase(BaseModel):
Expand Down Expand Up @@ -78,13 +78,7 @@ class TimeSeries(TimeSeriesBase):
"""

name: str | None = None
data: list[float | None]

@field_validator("data")
@classmethod
def replace_nan_with_none(cls, data: list[float]) -> list[float | None]:
"""Replace NaN values with None."""
return [None if pd.isna(value) else value for value in data]
data: list[float]

@classmethod
def from_pandas(cls, data: pd.Series) -> Self:
Expand All @@ -102,13 +96,7 @@ class TimeDataFrame(TimeSeriesBase):
"""Time series data with multiple columns."""

columns: list[str]
data: list[list[float | None]]

@field_validator("data")
@classmethod
def replace_nan_with_none(cls, data: list[list[float]]) -> list[list[float | None]]:
"""Replace NaN values with None."""
return [[None if pd.isna(value) else value for value in row] for row in data]
data: list[list[float]]

@classmethod
def from_pandas(cls, data: pd.DataFrame) -> Self:
Expand Down
Loading

0 comments on commit a4962b5

Please sign in to comment.