Skip to content

timeseries.py

ESM time series scatter plots.

ESMTimeSeriesChart

A class that produces one time series chart.

Parameters:

Name Type Description Default
df pandas.DataFrame

Metric data frame complying with the evaluation data model.

required
cfg types.SimpleNamespace

Plotly configuration object with styling and export settings.

required
Source code in evals/plots/timeseries.py
 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
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
class ESMTimeSeriesChart:
    """
    A class that produces one time series chart.

    Parameters
    ----------
    df
        Metric data frame complying with the evaluation data model.
    cfg
        Plotly configuration object with styling and export settings.
    """

    def __init__(self, df: pd.DataFrame, cfg: SimpleNamespace) -> None:
        self._df = df
        self.cfg = cfg
        self.fig = go.Figure()
        self.unit = self.cfg.unit or df.attrs["unit"]
        self.metric_name = df.attrs["name"]
        self.year = self._df.index.unique("year")[0]
        self.location = self._df.index.unique(DataModel.LOCATION)[0]
        self.col_values = ""

        self.cfg.yaxes_showgrid = self.cfg.yaxes_visible = True

    @cached_property
    def df(self) -> pd.DataFrame:
        """
        Plot data formatted for time series charts.

        Returns
        -------
        :
            The formatted data for creating time series charts.
        """
        df = apply_cutoff(self._df, limit=self.cfg.cutoff, drop=self.cfg.cutoff_drop)
        df = custom_sort(df, by=self.cfg.plot_category, values=self.cfg.category_orders)
        df = self.fix_snapshots(df, int(self.year))
        df = df.droplevel([DataModel.YEAR, DataModel.LOCATION])
        return df.T

    def plot(self) -> None:
        """
        Plot the data to the chart.

        Iterates over data series, adds scatter traces, applies styling,
        and appends footnotes.
        """
        title = self.cfg.title.format(
            location=self.location, year=self.year, unit=self.unit
        )
        if empty_input(self._df):
            self.fig = empty_figure(title)
            return

        stackgroup = None
        for i, (name, series) in enumerate(self.df.items()):
            if self.cfg.stacked:
                stackgroup = "supply" if series.sum() >= 0 else "withdrawal"
            legendrank = 1000 + i if stackgroup == "supply" else 1000 - i
            self.fig.add_trace(
                go.Scatter(
                    x=series.index,
                    y=series.values,
                    hovertemplate="%{y:.2f} " + self.unit,
                    name=name,
                    fill=self.cfg.fill.get(name, "tonexty"),
                    fillpattern_shape=self.cfg.pattern.get(name),
                    line_dash=self.cfg.line_dash.get(name, "solid"),
                    line_width=self.cfg.line_width.get(name, 1),
                    line_color=self.cfg.colors.get(name),
                    line_shape=self.cfg.line_shape,
                    fillcolor=self.cfg.colors.get(name),
                    stackgroup=stackgroup,
                    legendrank=legendrank,
                )
            )

        ts_styler = TimeSeriesStyler(self.cfg)
        layout_styler = LayoutStyler(self.cfg)

        ts_styler.style_inflexible_demand(self.fig)
        layout_styler.set_base_layout(self.fig)
        layout_styler.style_title_and_legend_and_xaxis_label(self.fig)
        ts_styler.style_axes_and_layout(self.fig, title, self.unit)
        layout_styler.append_footnotes(self.fig)

    @staticmethod
    def fix_snapshots(df: pd.DataFrame, year: int) -> pd.DataFrame:
        """
        Correct the year in snapshot timestamp column labels.

        Parameters
        ----------
        df
            The DataFrame with timestamps to be adjusted.
        year
            The correct year to use in the data frame columns.

        Returns
        -------
        :
            The DataFrame with corrected timestamps.
        """
        if isinstance(df.columns, pd.DatetimeIndex):
            df.columns = [s.replace(year=year) for s in df.columns]
        return df

df cached property

Plot data formatted for time series charts.

Returns:

Type Description
pandas.DataFrame

The formatted data for creating time series charts.

fix_snapshots(df, year) staticmethod

Correct the year in snapshot timestamp column labels.

Parameters:

Name Type Description Default
df pandas.DataFrame

The DataFrame with timestamps to be adjusted.

required
year int

The correct year to use in the data frame columns.

required

Returns:

Type Description
pandas.DataFrame

The DataFrame with corrected timestamps.

Source code in evals/plots/timeseries.py
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
@staticmethod
def fix_snapshots(df: pd.DataFrame, year: int) -> pd.DataFrame:
    """
    Correct the year in snapshot timestamp column labels.

    Parameters
    ----------
    df
        The DataFrame with timestamps to be adjusted.
    year
        The correct year to use in the data frame columns.

    Returns
    -------
    :
        The DataFrame with corrected timestamps.
    """
    if isinstance(df.columns, pd.DatetimeIndex):
        df.columns = [s.replace(year=year) for s in df.columns]
    return df

plot()

Plot the data to the chart.

Iterates over data series, adds scatter traces, applies styling, and appends footnotes.

Source code in evals/plots/timeseries.py
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
def plot(self) -> None:
    """
    Plot the data to the chart.

    Iterates over data series, adds scatter traces, applies styling,
    and appends footnotes.
    """
    title = self.cfg.title.format(
        location=self.location, year=self.year, unit=self.unit
    )
    if empty_input(self._df):
        self.fig = empty_figure(title)
        return

    stackgroup = None
    for i, (name, series) in enumerate(self.df.items()):
        if self.cfg.stacked:
            stackgroup = "supply" if series.sum() >= 0 else "withdrawal"
        legendrank = 1000 + i if stackgroup == "supply" else 1000 - i
        self.fig.add_trace(
            go.Scatter(
                x=series.index,
                y=series.values,
                hovertemplate="%{y:.2f} " + self.unit,
                name=name,
                fill=self.cfg.fill.get(name, "tonexty"),
                fillpattern_shape=self.cfg.pattern.get(name),
                line_dash=self.cfg.line_dash.get(name, "solid"),
                line_width=self.cfg.line_width.get(name, 1),
                line_color=self.cfg.colors.get(name),
                line_shape=self.cfg.line_shape,
                fillcolor=self.cfg.colors.get(name),
                stackgroup=stackgroup,
                legendrank=legendrank,
            )
        )

    ts_styler = TimeSeriesStyler(self.cfg)
    layout_styler = LayoutStyler(self.cfg)

    ts_styler.style_inflexible_demand(self.fig)
    layout_styler.set_base_layout(self.fig)
    layout_styler.style_title_and_legend_and_xaxis_label(self.fig)
    ts_styler.style_axes_and_layout(self.fig, title, self.unit)
    layout_styler.append_footnotes(self.fig)