mgplot.growth_plot

growth_plot.py: plot period and annual/through-the-year growth rates on the same axes.

  • calc_growth()
  • growth_plot()
  • series_growth_plot()
  1"""
  2growth_plot.py:
  3plot period and annual/through-the-year growth rates on the same axes.
  4- calc_growth()
  5- growth_plot()
  6- series_growth_plot()
  7"""
  8
  9# --- imports
 10from typing import Final
 11
 12from pandas import Series, DataFrame, Period, PeriodIndex, period_range
 13from numpy import nan
 14from matplotlib.pyplot import Axes
 15from tabulate import tabulate
 16
 17from mgplot.bar_plot import bar_plot, BAR_KW_TYPES
 18from mgplot.line_plot import line_plot, LINE_KW_TYPES
 19from mgplot.axis_utils import map_periodindex
 20from mgplot.test import prepare_for_test
 21from mgplot.settings import DataT
 22from mgplot.axis_utils import set_labels
 23from mgplot.utilities import check_clean_timeseries, constrain_data
 24from mgplot.kw_type_checking import (
 25    validate_kwargs,
 26    report_kwargs,
 27    validate_expected,
 28    ExpectedTypeDict,
 29    TransitionKwargs,
 30    trans_check,
 31    package_kwargs,
 32)
 33from mgplot.keyword_names import (
 34    # - common
 35    AX,
 36    REPORT_KWARGS,
 37    LABEL_SERIES,
 38    MAX_TICKS,
 39    WIDTH,
 40    COLOR,
 41    STYLE,
 42    ANNOTATE,
 43    ANNOTATE_COLOR,
 44    ROUNDING,
 45    FONTSIZE,
 46    FONTNAME,
 47    ROTATION,
 48    # - line related
 49    LINE_WIDTH,
 50    LINE_COLOR,
 51    LINE_STYLE,
 52    ANNOTATE_LINE,
 53    LINE_ROUNDING,
 54    LINE_FONTSIZE,
 55    LINE_FONTNAME,
 56    LINE_ANNO_COLOR,
 57    # - bar related
 58    ANNOTATE_BARS,
 59    BAR_ROUNDING,
 60    BAR_ROTATION,
 61    BAR_WIDTH,
 62    BAR_COLOR,
 63    BAR_ANNO_COLOR,
 64    BAR_FONTSIZE,
 65    BAR_FONTNAME,
 66    PLOT_FROM,
 67    ABOVE,
 68)
 69
 70# === constants
 71
 72# - overarching constants
 73
 74ANNUAL = "annual"
 75PERIODIC = "periodic"
 76
 77GROWTH_KW_TYPES: Final[ExpectedTypeDict] = {
 78    # --- common options
 79    AX: (Axes, type(None)),
 80    PLOT_FROM: (int, Period, type(None)),
 81    LABEL_SERIES: (bool),
 82    MAX_TICKS: int,
 83    # --- options passed to the line plot
 84    LINE_WIDTH: (float, int),
 85    LINE_COLOR: str,
 86    LINE_STYLE: str,
 87    ANNOTATE_LINE: bool,  # None, True
 88    LINE_ROUNDING: (bool, int),  # None, True or rounding
 89    LINE_FONTSIZE: (str, int, float),  # fontsize for the line annotations
 90    LINE_FONTNAME: str,  # font name for the line annotations
 91    LINE_ANNO_COLOR: str,  # color for the line annotations
 92    # --- options passed to the bar plot
 93    ANNOTATE_BARS: (type(None), bool),
 94    BAR_FONTSIZE: (str, int, float),
 95    BAR_FONTNAME: str,
 96    BAR_ROUNDING: int,
 97    BAR_WIDTH: float,
 98    BAR_COLOR: str,
 99    BAR_ANNO_COLOR: (str, type(None)),
100    BAR_ROTATION: (int, float),
101    ABOVE: bool,
102}
103validate_expected(GROWTH_KW_TYPES, "growth_plot")
104
105SERIES_GROWTH_KW_TYPES: Final[ExpectedTypeDict] = {
106    "ylabel": (str, type(None)),
107} | GROWTH_KW_TYPES
108validate_expected(SERIES_GROWTH_KW_TYPES, "growth_plot")
109
110
111# - transition of kwargs from growth_plot to line_plot
112common_transitions: TransitionKwargs = {
113    # arg-to-growth_plot : (arg-to-line_plot, default_value)
114    LABEL_SERIES: (LABEL_SERIES, True),
115    AX: (AX, None),
116    # MAX_TICKS: (MAX_TICKS, None),
117    PLOT_FROM: (PLOT_FROM, None),
118    REPORT_KWARGS: (REPORT_KWARGS, None),
119}
120trans_check(common_transitions, GROWTH_KW_TYPES, LINE_KW_TYPES, __name__)
121trans_check(common_transitions, GROWTH_KW_TYPES, BAR_KW_TYPES, __name__)
122
123to_line_plot: TransitionKwargs = common_transitions | {
124    # arg-to-growth_plot : (arg-to-line_plot, default_value)
125    LINE_WIDTH: (WIDTH, None),
126    LINE_COLOR: (COLOR, "darkblue"),
127    LINE_STYLE: (STYLE, None),
128    ANNOTATE_LINE: (ANNOTATE, True),
129    LINE_ROUNDING: (ROUNDING, None),
130    LINE_FONTSIZE: (FONTSIZE, None),
131    LINE_FONTNAME: (FONTNAME, None),
132    LINE_ANNO_COLOR: (ANNOTATE_COLOR, None),
133}
134trans_check(to_line_plot, GROWTH_KW_TYPES, LINE_KW_TYPES, __name__)
135
136# - constants for the bar plot
137to_bar_plot: TransitionKwargs = common_transitions | {
138    # arg-to-growth_plot : (arg-to-bar_plot, default_value)
139    BAR_WIDTH: (WIDTH, 0.8),
140    BAR_COLOR: (COLOR, "#dd0000"),
141    ANNOTATE_BARS: (ANNOTATE, True),
142    BAR_ROUNDING: (ROUNDING, None),
143    ABOVE: (ABOVE, False),
144    BAR_ROTATION: (ROTATION, None),
145    BAR_FONTSIZE: (FONTSIZE, None),
146    BAR_FONTNAME: (FONTNAME, None),
147    BAR_ANNO_COLOR: (ANNOTATE_COLOR, None),
148}
149trans_check(to_bar_plot, GROWTH_KW_TYPES, BAR_KW_TYPES, __name__)
150
151
152# === functions
153# --- public functions
154def calc_growth(series: Series) -> DataFrame:
155    """
156    Calculate annual and periodic growth for a pandas Series,
157    where the index is a PeriodIndex.
158
159    Args:
160    -   series: A pandas Series with an appropriate PeriodIndex.
161
162    Returns a two column DataFrame:
163
164    Raises
165    -   TypeError if the series is not a pandas Series.
166    -   TypeError if the series index is not a PeriodIndex.
167    -   ValueError if the series is empty.
168    -   ValueError if the series index does not have a frequency of Q, M, or D.
169    -   ValueError if the series index has duplicates.
170    """
171
172    # --- sanity checks
173    if not isinstance(series, Series):
174        raise TypeError("The series argument must be a pandas Series")
175    if not isinstance(series.index, PeriodIndex):
176        raise TypeError("The series index must be a pandas PeriodIndex")
177    if series.empty:
178        raise ValueError("The series argument must not be empty")
179    if series.index.freqstr[0] not in ("Q", "M", "D"):
180        raise ValueError("The series index must have a frequency of Q, M, or D")
181    if series.index.has_duplicates:
182        raise ValueError("The series index must not have duplicate values")
183
184    # --- ensure the index is complete and the date is sorted
185    complete = period_range(start=series.index.min(), end=series.index.max())
186    series = series.reindex(complete, fill_value=nan)
187    series = series.sort_index(ascending=True)
188
189    # --- calculate annual and periodic growth
190    ppy = {"Q": 4, "M": 12, "D": 365}[PeriodIndex(series.index).freqstr[:1]]
191    annual = series.pct_change(periods=ppy) * 100
192    periodic = series.pct_change(periods=1) * 100
193    periodic_name = {4: "Quarterly", 12: "Monthly", 365: "Daily"}[ppy] + " Growth"
194    return DataFrame(
195        {
196            "Annual Growth": annual,
197            periodic_name: periodic,
198        }
199    )
200
201
202def growth_plot(
203    data: DataT,
204    **kwargs,
205) -> Axes:
206    """
207        Plot annual growth (as a line) and periodic growth (as bars)
208        on the same axes.
209
210        Args:
211        -   data: A pandas DataFrame with two columns:
212        -   kwargs:
213            # --- common options
214            ax: Axes | None -- the matplotlib Axes to plot on, or None to create a new one.
215            plot_from: Period | int | None -- the period to start plotting from
216            label_series: bool -- whether to label the series in the legend.
217            max_ticks: int -- maximum number of ticks on the x-axis
218            # --- options passed to the line plot
219            line_width: float | int  -- the width of the line
220            line_color: str  -- the color of the line
221            line_style: str  -- the style of the line
222            annotate_line: None | bool -- whether to annotate the end of the line
223            line_rounding: bool | int  -- rounding for line annotation
224            line_fontsize: str | int | float  -- fontsize for the line annotation
225            line_fontname: str  -- font name for the line annotation
226            line_anno_color: str | bool | None  -- color for the line annotation
227            # --- options passed to the bar plot
228            bar_width: float,
229            bar_color: str,
230            annotate_bars: None | bool -- whether to annotate the bars
231            above: bool -- whether to place the bar annotations above the bars
232            bar_fontsize: str | int | float -- fontsize for the bar annotations
233            bar_fontname: str -- font name for the bar annotations
234            bar_rounding: bool | int -- rounding for bar annotation
235            bar_anno_color: str | None -- color for the bar annotation
236            bar_rotation: int | float -- rotation for the bar annotation
237    }
238        Returns:
239        -   axes: The matplotlib Axes object.
240
241        Raises:
242        -   TypeError if the annual and periodic arguments are not pandas Series.
243        -   TypeError if the annual index is not a PeriodIndex.
244        -   ValueError if the annual and periodic series do not have the same index.
245    """
246
247    # --- check the kwargs
248    me = "growth_plot"
249    report_kwargs(called_from=me, **kwargs)
250    kwargs = validate_kwargs(GROWTH_KW_TYPES, me, **kwargs)
251
252    # --- data checks
253    data = check_clean_timeseries(data, me)
254    if len(data.columns) != 2:
255        raise TypeError("The data argument must be a pandas DataFrame with two columns")
256    data, kwargs = constrain_data(data, **kwargs)
257
258    # --- get the series of interest ...
259    annual = data[data.columns[0]]
260    periodic = data[data.columns[1]]
261
262    # --- series names
263    annual.name = "Annual Growth"
264    periodic.name = {"M": "Monthly", "Q": "Quarterly", "D": "Daily"}[
265        PeriodIndex(periodic.index).freqstr[:1]
266    ] + " Growth"
267
268    # --- convert PeriodIndex periodic growth data to integer indexed data.
269    saved_pi = map_periodindex(periodic)
270    if saved_pi is not None:
271        periodic = saved_pi[0]  # extract the reindexed DataFrame
272
273    # --- simple bar chart for the periodic growth
274    if BAR_ANNO_COLOR not in kwargs or kwargs[BAR_ANNO_COLOR] is None:
275        kwargs[BAR_ANNO_COLOR] = "black" if kwargs.get(ABOVE, False) else "white"
276    selected = package_kwargs(to_bar_plot, **kwargs)
277    axes = bar_plot(periodic, **selected)
278
279    # --- and now the annual growth as a line
280    selected = package_kwargs(to_line_plot, **kwargs)
281    line_plot(annual, ax=axes, **selected)
282
283    # --- fix the x-axis labels
284    if saved_pi is not None:
285        set_labels(axes, saved_pi[1], kwargs.get("max_ticks", 10))
286
287    # --- and done ...
288    return axes
289
290
291def series_growth_plot(
292    data: DataT,
293    **kwargs,
294) -> Axes:
295    """
296    Plot annual and periodic growth in percentage terms from
297    a pandas Series, and finalise the plot.
298
299    Args:
300    -   data: A pandas Series with an appropriate PeriodIndex.
301    -   kwargs:
302        -   takes the same kwargs as for growth_plot()
303    """
304
305    # --- check the kwargs
306    me = "series_growth_plot"
307    report_kwargs(called_from=me, **kwargs)
308    kwargs = validate_kwargs(SERIES_GROWTH_KW_TYPES, me, **kwargs)
309
310    # --- sanity checks
311    if not isinstance(data, Series):
312        raise TypeError(
313            "The data argument to series_growth_plot() must be a pandas Series"
314        )
315
316    # --- calculate growth and plot - add ylabel
317    ylabel: str | None = kwargs.pop("ylabel", None)
318    if ylabel is not None:
319        print(f"Did you intend to specify a value for the 'ylabel' in {me}()?")
320    ylabel = "Growth (%)" if ylabel is None else ylabel
321    growth = calc_growth(data)
322    ax = growth_plot(growth, **kwargs)
323    ax.set_ylabel(ylabel)
324    return ax
325
326
327# --- test code
328if __name__ == "__main__":
329    print("Testing")
330    prepare_for_test("growth_plot")
331    series_ = Series([1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0])
332    series_.index = period_range("2020Q1", periods=len(series_), freq="Q")
333    growth_ = calc_growth(series_)
334    text_ = tabulate(growth_, headers="keys", tablefmt="pipe")  # type: ignore[arg-type]
335    print(text_)
ANNUAL = 'annual'
PERIODIC = 'periodic'
GROWTH_KW_TYPES: Final[ExpectedTypeDict] = {'ax': (<class 'matplotlib.axes._axes.Axes'>, <class 'NoneType'>), 'plot_from': (<class 'int'>, <class 'pandas._libs.tslibs.period.Period'>, <class 'NoneType'>), 'label_series': <class 'bool'>, 'max_ticks': <class 'int'>, 'line_width': (<class 'float'>, <class 'int'>), 'line_color': <class 'str'>, 'line_style': <class 'str'>, 'annotate_line': <class 'bool'>, 'line_rounding': (<class 'bool'>, <class 'int'>), 'line_fontsize': (<class 'str'>, <class 'int'>, <class 'float'>), 'line_fontname': <class 'str'>, 'line_annotate_color': <class 'str'>, 'annotate_bars': (<class 'NoneType'>, <class 'bool'>), 'bar_fontsize': (<class 'str'>, <class 'int'>, <class 'float'>), 'bar_fontname': <class 'str'>, 'bar_rounding': <class 'int'>, 'bar_width': <class 'float'>, 'bar_color': <class 'str'>, 'bar_annotate_color': (<class 'str'>, <class 'NoneType'>), 'bar_rotation': (<class 'int'>, <class 'float'>), 'above': <class 'bool'>}
SERIES_GROWTH_KW_TYPES: Final[ExpectedTypeDict] = {'ylabel': (<class 'str'>, <class 'NoneType'>), 'ax': (<class 'matplotlib.axes._axes.Axes'>, <class 'NoneType'>), 'plot_from': (<class 'int'>, <class 'pandas._libs.tslibs.period.Period'>, <class 'NoneType'>), 'label_series': <class 'bool'>, 'max_ticks': <class 'int'>, 'line_width': (<class 'float'>, <class 'int'>), 'line_color': <class 'str'>, 'line_style': <class 'str'>, 'annotate_line': <class 'bool'>, 'line_rounding': (<class 'bool'>, <class 'int'>), 'line_fontsize': (<class 'str'>, <class 'int'>, <class 'float'>), 'line_fontname': <class 'str'>, 'line_annotate_color': <class 'str'>, 'annotate_bars': (<class 'NoneType'>, <class 'bool'>), 'bar_fontsize': (<class 'str'>, <class 'int'>, <class 'float'>), 'bar_fontname': <class 'str'>, 'bar_rounding': <class 'int'>, 'bar_width': <class 'float'>, 'bar_color': <class 'str'>, 'bar_annotate_color': (<class 'str'>, <class 'NoneType'>), 'bar_rotation': (<class 'int'>, <class 'float'>), 'above': <class 'bool'>}
common_transitions: mgplot.kw_type_checking.TransitionKwargs = {'label_series': ('label_series', True), 'ax': ('ax', None), 'plot_from': ('plot_from', None), 'report_kwargs': ('report_kwargs', None)}
to_line_plot: mgplot.kw_type_checking.TransitionKwargs = {'label_series': ('label_series', True), 'ax': ('ax', None), 'plot_from': ('plot_from', None), 'report_kwargs': ('report_kwargs', None), 'line_width': ('width', None), 'line_color': ('color', 'darkblue'), 'line_style': ('style', None), 'annotate_line': ('annotate', True), 'line_rounding': ('rounding', None), 'line_fontsize': ('fontsize', None), 'line_fontname': ('fontname', None), 'line_annotate_color': ('annotate_color', None)}
to_bar_plot: mgplot.kw_type_checking.TransitionKwargs = {'label_series': ('label_series', True), 'ax': ('ax', None), 'plot_from': ('plot_from', None), 'report_kwargs': ('report_kwargs', None), 'bar_width': ('width', 0.8), 'bar_color': ('color', '#dd0000'), 'annotate_bars': ('annotate', True), 'bar_rounding': ('rounding', None), 'above': ('above', False), 'bar_rotation': ('rotation', None), 'bar_fontsize': ('fontsize', None), 'bar_fontname': ('fontname', None), 'bar_annotate_color': ('annotate_color', None)}
def calc_growth(series: pandas.core.series.Series) -> pandas.core.frame.DataFrame:
155def calc_growth(series: Series) -> DataFrame:
156    """
157    Calculate annual and periodic growth for a pandas Series,
158    where the index is a PeriodIndex.
159
160    Args:
161    -   series: A pandas Series with an appropriate PeriodIndex.
162
163    Returns a two column DataFrame:
164
165    Raises
166    -   TypeError if the series is not a pandas Series.
167    -   TypeError if the series index is not a PeriodIndex.
168    -   ValueError if the series is empty.
169    -   ValueError if the series index does not have a frequency of Q, M, or D.
170    -   ValueError if the series index has duplicates.
171    """
172
173    # --- sanity checks
174    if not isinstance(series, Series):
175        raise TypeError("The series argument must be a pandas Series")
176    if not isinstance(series.index, PeriodIndex):
177        raise TypeError("The series index must be a pandas PeriodIndex")
178    if series.empty:
179        raise ValueError("The series argument must not be empty")
180    if series.index.freqstr[0] not in ("Q", "M", "D"):
181        raise ValueError("The series index must have a frequency of Q, M, or D")
182    if series.index.has_duplicates:
183        raise ValueError("The series index must not have duplicate values")
184
185    # --- ensure the index is complete and the date is sorted
186    complete = period_range(start=series.index.min(), end=series.index.max())
187    series = series.reindex(complete, fill_value=nan)
188    series = series.sort_index(ascending=True)
189
190    # --- calculate annual and periodic growth
191    ppy = {"Q": 4, "M": 12, "D": 365}[PeriodIndex(series.index).freqstr[:1]]
192    annual = series.pct_change(periods=ppy) * 100
193    periodic = series.pct_change(periods=1) * 100
194    periodic_name = {4: "Quarterly", 12: "Monthly", 365: "Daily"}[ppy] + " Growth"
195    return DataFrame(
196        {
197            "Annual Growth": annual,
198            periodic_name: periodic,
199        }
200    )

Calculate annual and periodic growth for a pandas Series, where the index is a PeriodIndex.

Args:

  • series: A pandas Series with an appropriate PeriodIndex.

Returns a two column DataFrame:

Raises

  • TypeError if the series is not a pandas Series.
  • TypeError if the series index is not a PeriodIndex.
  • ValueError if the series is empty.
  • ValueError if the series index does not have a frequency of Q, M, or D.
  • ValueError if the series index has duplicates.
def growth_plot(data: ~DataT, **kwargs) -> matplotlib.axes._axes.Axes:
203def growth_plot(
204    data: DataT,
205    **kwargs,
206) -> Axes:
207    """
208        Plot annual growth (as a line) and periodic growth (as bars)
209        on the same axes.
210
211        Args:
212        -   data: A pandas DataFrame with two columns:
213        -   kwargs:
214            # --- common options
215            ax: Axes | None -- the matplotlib Axes to plot on, or None to create a new one.
216            plot_from: Period | int | None -- the period to start plotting from
217            label_series: bool -- whether to label the series in the legend.
218            max_ticks: int -- maximum number of ticks on the x-axis
219            # --- options passed to the line plot
220            line_width: float | int  -- the width of the line
221            line_color: str  -- the color of the line
222            line_style: str  -- the style of the line
223            annotate_line: None | bool -- whether to annotate the end of the line
224            line_rounding: bool | int  -- rounding for line annotation
225            line_fontsize: str | int | float  -- fontsize for the line annotation
226            line_fontname: str  -- font name for the line annotation
227            line_anno_color: str | bool | None  -- color for the line annotation
228            # --- options passed to the bar plot
229            bar_width: float,
230            bar_color: str,
231            annotate_bars: None | bool -- whether to annotate the bars
232            above: bool -- whether to place the bar annotations above the bars
233            bar_fontsize: str | int | float -- fontsize for the bar annotations
234            bar_fontname: str -- font name for the bar annotations
235            bar_rounding: bool | int -- rounding for bar annotation
236            bar_anno_color: str | None -- color for the bar annotation
237            bar_rotation: int | float -- rotation for the bar annotation
238    }
239        Returns:
240        -   axes: The matplotlib Axes object.
241
242        Raises:
243        -   TypeError if the annual and periodic arguments are not pandas Series.
244        -   TypeError if the annual index is not a PeriodIndex.
245        -   ValueError if the annual and periodic series do not have the same index.
246    """
247
248    # --- check the kwargs
249    me = "growth_plot"
250    report_kwargs(called_from=me, **kwargs)
251    kwargs = validate_kwargs(GROWTH_KW_TYPES, me, **kwargs)
252
253    # --- data checks
254    data = check_clean_timeseries(data, me)
255    if len(data.columns) != 2:
256        raise TypeError("The data argument must be a pandas DataFrame with two columns")
257    data, kwargs = constrain_data(data, **kwargs)
258
259    # --- get the series of interest ...
260    annual = data[data.columns[0]]
261    periodic = data[data.columns[1]]
262
263    # --- series names
264    annual.name = "Annual Growth"
265    periodic.name = {"M": "Monthly", "Q": "Quarterly", "D": "Daily"}[
266        PeriodIndex(periodic.index).freqstr[:1]
267    ] + " Growth"
268
269    # --- convert PeriodIndex periodic growth data to integer indexed data.
270    saved_pi = map_periodindex(periodic)
271    if saved_pi is not None:
272        periodic = saved_pi[0]  # extract the reindexed DataFrame
273
274    # --- simple bar chart for the periodic growth
275    if BAR_ANNO_COLOR not in kwargs or kwargs[BAR_ANNO_COLOR] is None:
276        kwargs[BAR_ANNO_COLOR] = "black" if kwargs.get(ABOVE, False) else "white"
277    selected = package_kwargs(to_bar_plot, **kwargs)
278    axes = bar_plot(periodic, **selected)
279
280    # --- and now the annual growth as a line
281    selected = package_kwargs(to_line_plot, **kwargs)
282    line_plot(annual, ax=axes, **selected)
283
284    # --- fix the x-axis labels
285    if saved_pi is not None:
286        set_labels(axes, saved_pi[1], kwargs.get("max_ticks", 10))
287
288    # --- and done ...
289    return axes

Plot annual growth (as a line) and periodic growth (as bars) on the same axes.

Args:
-   data: A pandas DataFrame with two columns:
-   kwargs:
    # --- common options
    ax: Axes | None -- the matplotlib Axes to plot on, or None to create a new one.
    plot_from: Period | int | None -- the period to start plotting from
    label_series: bool -- whether to label the series in the legend.
    max_ticks: int -- maximum number of ticks on the x-axis
    # --- options passed to the line plot
    line_width: float | int  -- the width of the line
    line_color: str  -- the color of the line
    line_style: str  -- the style of the line
    annotate_line: None | bool -- whether to annotate the end of the line
    line_rounding: bool | int  -- rounding for line annotation
    line_fontsize: str | int | float  -- fontsize for the line annotation
    line_fontname: str  -- font name for the line annotation
    line_anno_color: str | bool | None  -- color for the line annotation
    # --- options passed to the bar plot
    bar_width: float,
    bar_color: str,
    annotate_bars: None | bool -- whether to annotate the bars
    above: bool -- whether to place the bar annotations above the bars
    bar_fontsize: str | int | float -- fontsize for the bar annotations
    bar_fontname: str -- font name for the bar annotations
    bar_rounding: bool | int -- rounding for bar annotation
    bar_anno_color: str | None -- color for the bar annotation
    bar_rotation: int | float -- rotation for the bar annotation

} Returns: - axes: The matplotlib Axes object.

Raises:
-   TypeError if the annual and periodic arguments are not pandas Series.
-   TypeError if the annual index is not a PeriodIndex.
-   ValueError if the annual and periodic series do not have the same index.
def series_growth_plot(data: ~DataT, **kwargs) -> matplotlib.axes._axes.Axes:
292def series_growth_plot(
293    data: DataT,
294    **kwargs,
295) -> Axes:
296    """
297    Plot annual and periodic growth in percentage terms from
298    a pandas Series, and finalise the plot.
299
300    Args:
301    -   data: A pandas Series with an appropriate PeriodIndex.
302    -   kwargs:
303        -   takes the same kwargs as for growth_plot()
304    """
305
306    # --- check the kwargs
307    me = "series_growth_plot"
308    report_kwargs(called_from=me, **kwargs)
309    kwargs = validate_kwargs(SERIES_GROWTH_KW_TYPES, me, **kwargs)
310
311    # --- sanity checks
312    if not isinstance(data, Series):
313        raise TypeError(
314            "The data argument to series_growth_plot() must be a pandas Series"
315        )
316
317    # --- calculate growth and plot - add ylabel
318    ylabel: str | None = kwargs.pop("ylabel", None)
319    if ylabel is not None:
320        print(f"Did you intend to specify a value for the 'ylabel' in {me}()?")
321    ylabel = "Growth (%)" if ylabel is None else ylabel
322    growth = calc_growth(data)
323    ax = growth_plot(growth, **kwargs)
324    ax.set_ylabel(ylabel)
325    return ax

Plot annual and periodic growth in percentage terms from a pandas Series, and finalise the plot.

Args:

  • data: A pandas Series with an appropriate PeriodIndex.
  • kwargs:
    • takes the same kwargs as for growth_plot()