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

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 package_kwargs(mapping: TransitionKwargs, **kwargs: Any) -> dict[str, typing.Any]:
194def package_kwargs(mapping: TransitionKwargs, **kwargs: Any) -> dict[str, Any]:
195    """
196    Package the keyword arguments for plotting functions.
197    Substitute defaults where arguments are not provided
198    (unless the default is None).
199
200    Args:
201    -   mapping: A mapping of original keys to  a tuple of (new-key, default value).
202    -   kwargs: The original keyword arguments.
203
204    Returns:
205    -   A dictionary with the packaged keyword arguments.
206    """
207    return {
208        v[0]: kwargs.get(k, v[1])
209        for k, v in mapping.items()
210        if k in kwargs or v[1] is not None
211    }

Package the keyword arguments for plotting functions. Substitute defaults where arguments are not provided (unless the default is None).

Args:

  • mapping: A mapping of original keys to a tuple of (new-key, default value).
  • kwargs: The original keyword arguments.

Returns:

  • A dictionary with the packaged keyword arguments.
def growth_plot(data: ~DataT, **kwargs) -> matplotlib.axes._axes.Axes:
214def growth_plot(
215    data: DataT,
216    **kwargs,
217) -> Axes:
218    """
219    Plot annual growth (as a line) and periodic growth (as bars)
220    on the same axes.
221
222    Args:
223    -   data: A pandas DataFrame with two columns:
224    -   kwargs:
225        -   line_width: The width of the line (default is 2).
226        -   line_color: The color of the line (default is "darkblue").
227        -   line_style: The style of the line (default is "-").
228        -   annotate_line: None | bool | int | str - fontsize to annotate
229            the line (default is "small", which means the line is annotated with
230            small text).
231        -   rounding: None | bool | int - the number of decimal places to round
232            the line (default is 0).
233        -   bar_width: The width of the bars (default is 0.8).
234        -   bar_color: The color of the bars (default is "indianred").
235        -   annotate_bar: None | int | str - fontsize to annotate the bars
236            (default is "small", which means the bars are annotated with
237            small text).
238        -   bar_rounding: The number of decimal places to round the
239            annotations to (default is 1).
240        -   plot_from: None | Period | int -- if:
241            -   None: the entire series is plotted
242            -   Period: the plot starts from this period
243            -   int: the plot starts from this +/- index position
244        -   max_ticks: The maximum number of ticks to show on the x-axis
245            (default is 10).
246
247    Returns:
248    -   axes: The matplotlib Axes object.
249
250    Raises:
251    -   TypeError if the annual and periodic arguments are not pandas Series.
252    -   TypeError if the annual index is not a PeriodIndex.
253    -   ValueError if the annual and periodic series do not have the same index.
254    """
255
256    # --- check the kwargs
257    me = "growth_plot"
258    report_kwargs(called_from=me, **kwargs)
259    kwargs = validate_kwargs(GROWTH_KW_TYPES, me, **kwargs)
260
261    # --- data checks
262    data = check_clean_timeseries(data, me)
263    if len(data.columns) != 2:
264        raise TypeError("The data argument must be a pandas DataFrame with two columns")
265    data, kwargs = constrain_data(data, **kwargs)
266
267    # --- get the series of interest ...
268    annual = data[data.columns[0]]
269    periodic = data[data.columns[1]]
270
271    # --- series names
272    annual.name = "Annual Growth"
273    periodic.name = {"M": "Monthly", "Q": "Quarterly", "D": "Daily"}[
274        PeriodIndex(periodic.index).freqstr[:1]
275    ] + " Growth"
276
277    # --- convert PeriodIndex periodic growth data to integer indexed data.
278    saved_pi = map_periodindex(periodic)
279    if saved_pi is not None:
280        periodic = saved_pi[0]  # extract the reindexed DataFrame
281
282    # --- simple bar chart for the periodic growth
283    if BAR_ANNO_COLOR not in kwargs or kwargs[BAR_ANNO_COLOR] is None:
284        kwargs[BAR_ANNO_COLOR] = "black" if kwargs.get(ABOVE, False) else "white"
285    selected = package_kwargs(to_bar_plot, **kwargs)
286    axes = bar_plot(periodic, **selected)
287
288    # --- and now the annual growth as a line
289    selected = package_kwargs(to_line_plot, **kwargs)
290    line_plot(annual, ax=axes, **selected)
291
292    # --- fix the x-axis labels
293    if saved_pi is not None:
294        set_labels(axes, saved_pi[1], kwargs.get("max_ticks", 10))
295
296    # --- and done ...
297    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:
    • line_width: The width of the line (default is 2).
    • line_color: The color of the line (default is "darkblue").
    • line_style: The style of the line (default is "-").
    • annotate_line: None | bool | int | str - fontsize to annotate the line (default is "small", which means the line is annotated with small text).
    • rounding: None | bool | int - the number of decimal places to round the line (default is 0).
    • bar_width: The width of the bars (default is 0.8).
    • bar_color: The color of the bars (default is "indianred").
    • annotate_bar: None | int | str - fontsize to annotate the bars (default is "small", which means the bars are annotated with small text).
    • bar_rounding: The number of decimal places to round the annotations to (default is 1).
    • plot_from: None | Period | int -- if:
      • None: the entire series is plotted
      • Period: the plot starts from this period
      • int: the plot starts from this +/- index position
    • max_ticks: The maximum number of ticks to show on the x-axis (default is 10).

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:
300def series_growth_plot(
301    data: DataT,
302    **kwargs,
303) -> Axes:
304    """
305    Plot annual and periodic growth in percentage terms from
306    a pandas Series, and finalise the plot.
307
308    Args:
309    -   data: A pandas Series with an appropriate PeriodIndex.
310    -   kwargs:
311        -   takes the same kwargs as for growth_plot()
312    """
313
314    # --- check the kwargs
315    me = "series_growth_plot"
316    report_kwargs(called_from=me, **kwargs)
317    kwargs = validate_kwargs(SERIES_GROWTH_KW_TYPES, me, **kwargs)
318
319    # --- sanity checks
320    if not isinstance(data, Series):
321        raise TypeError(
322            "The data argument to series_growth_plot() must be a pandas Series"
323        )
324
325    # --- calculate growth and plot - add ylabel
326    ylabel: str | None = kwargs.pop("ylabel", None)
327    if ylabel is not None:
328        print(f"Did you intend to specify a value for the 'ylabel' in {me}()?")
329    ylabel = "Growth (%)" if ylabel is None else ylabel
330    growth = calc_growth(data)
331    ax = growth_plot(growth, **kwargs)
332    ax.set_ylabel(ylabel)
333    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()