Skip to content

Line Plot

Line plots of pre-aggregated data, with one line per value column or group value.

For x-axes that are relative or sequential values ("days since an event", "months since a competitor opened"), use line.plot. For an actual datetime x-axis use plots.time, which resamples and aggregates; this module does not aggregate, so pass one row per x value per line.

plot(df, value_col=None, x_label=None, y_label=None, title=None, eyebrow=None, subtitle=None, x_col=None, group_col=None, ax=None, source_text=None, legend_title=None, move_legend_outside=False, legend_style=None, fill_na_value=None, highlight=None, **kwargs)

Plot value_col over x_col or the index, with a separate line per unique group_col value.

When df is a Series, its values are plotted against its index and value_col, x_col, and group_col must all be None. For a DataFrame, value_col is required, and a list of columns cannot be combined with group_col.

Parameters:

Name Type Description Default
df DataFrame | Series

The dataframe or series to plot.

required
value_col str | list[str]

The column(s) to plot; a list renders one line per column. Required when df is a DataFrame.

None
x_label str

X-axis label; omitted means no label.

None
y_label str

Y-axis label; omitted means no label.

None
title str

The title of the plot.

None
eyebrow str

Small uppercase label rendered above the title.

None
subtitle str

Supporting copy rendered below the title.

None
x_col str

The column to use as the x-axis. If None, the index is used.

None
group_col str

The column used to define different lines.

None
ax Axes

Matplotlib axes object to plot on.

None
source_text str

The source text to add to the plot.

None
legend_title str

The title of the legend.

None
move_legend_outside bool

Move the legend outside the plot.

False
legend_style Literal['box', 'end_of_line']

How series are labelled. "box" (default when None) renders the standard matplotlib legend. "end_of_line" suppresses the legend and places a colored series label at the right end of each line; it is a no-op on single-line plots. On multi-line plots with "end_of_line", move_legend_outside and legend_title are ignored and a UserWarning is emitted if either is supplied.

None
fill_na_value float

Value for missing points, including x values a group has no row for. Applied only when group_col is set; silently ignored otherwise.

None
highlight str | list[str]

Line(s) to emphasize; only for multi-line plots. With group_col these are group values; with a list of value_col these are column names. Highlighted lines use the linewidth kwarg (default 3) and the color kwarg (default palette colors); non-highlighted context lines are rendered behind them in the plot.color.context color (default #d1d5db, option-configurable) with a 1.0 linewidth.

None
**kwargs Any

Additional keyword arguments for pandas' plot function. linewidth, color, and alpha apply to highlighted lines only, not context lines.

{}

Returns:

Name Type Description
SubplotBase SubplotBase

The matplotlib axes object.

Raises:

Type Description
ValueError

If value_col is a list and group_col is provided.

ValueError

If df is a Series and value_col is not None.

ValueError

If df is a Series and x_col is specified (the Series index is the x-axis).

ValueError

If df is a Series and group_col is specified (cannot group a single series).

ValueError

If df is a DataFrame and value_col is None.

ValueError

If highlight is provided for a single-line plot.

ValueError

If highlight is an empty list.

ValueError

If highlight values do not match available groups/columns.

ValueError

If legend_style is not one of None, "box", or "end_of_line".

Source code in openretailscience/plots/line.py
def plot(  # noqa: PLR0913, PLR0917
    df: pd.DataFrame | pd.Series,
    value_col: str | list[str] | None = None,
    x_label: str | None = None,
    y_label: str | None = None,
    title: str | None = None,
    eyebrow: str | None = None,
    subtitle: str | None = None,
    x_col: str | None = None,
    group_col: str | None = None,
    ax: Axes | None = None,
    source_text: str | None = None,
    legend_title: str | None = None,
    move_legend_outside: bool = False,
    legend_style: Literal["box", "end_of_line"] | None = None,
    fill_na_value: float | None = None,
    highlight: str | list[str] | None = None,
    **kwargs: Any,  # noqa: ANN401
) -> SubplotBase:
    """Plot ``value_col`` over ``x_col`` or the index, with a separate line per unique ``group_col`` value.

    When ``df`` is a Series, its values are plotted against its index and ``value_col``,
    ``x_col``, and ``group_col`` must all be None. For a DataFrame, ``value_col`` is required,
    and a list of columns cannot be combined with ``group_col``.

    Args:
        df (pd.DataFrame | pd.Series): The dataframe or series to plot.
        value_col (str | list[str], optional): The column(s) to plot; a list renders one line per
            column. Required when ``df`` is a DataFrame.
        x_label (str, optional): X-axis label; omitted means no label.
        y_label (str, optional): Y-axis label; omitted means no label.
        title (str, optional): The title of the plot.
        eyebrow (str, optional): Small uppercase label rendered above the title.
        subtitle (str, optional): Supporting copy rendered below the title.
        x_col (str, optional): The column to use as the x-axis. If None, the index is used.
        group_col (str, optional): The column used to define different lines.
        ax (Axes, optional): Matplotlib axes object to plot on.
        source_text (str, optional): The source text to add to the plot.
        legend_title (str, optional): The title of the legend.
        move_legend_outside (bool, optional): Move the legend outside the plot.
        legend_style (Literal["box", "end_of_line"], optional): How series are labelled. ``"box"``
            (default when None) renders the standard matplotlib legend. ``"end_of_line"`` suppresses
            the legend and places a colored series label at the right end of each line; it is a no-op
            on single-line plots. On multi-line plots with ``"end_of_line"``, ``move_legend_outside``
            and ``legend_title`` are ignored and a UserWarning is emitted if either is supplied.
        fill_na_value (float, optional): Value for missing points, including x values a group has no
            row for. Applied only when ``group_col`` is set; silently ignored otherwise.
        highlight (str | list[str], optional): Line(s) to emphasize; only for multi-line plots. With
            ``group_col`` these are group values; with a list of ``value_col`` these are column
            names. Highlighted lines use the ``linewidth`` kwarg (default 3) and the ``color`` kwarg
            (default palette colors); non-highlighted context lines are rendered behind them in the
            ``plot.color.context`` color (default #d1d5db, option-configurable) with a 1.0 linewidth.
        **kwargs: Additional keyword arguments for pandas' ``plot`` function. ``linewidth``,
            ``color``, and ``alpha`` apply to highlighted lines only, not context lines.

    Returns:
        SubplotBase: The matplotlib axes object.

    Raises:
        ValueError: If value_col is a list and group_col is provided.
        ValueError: If df is a Series and value_col is not None.
        ValueError: If df is a Series and x_col is specified (the Series index is the x-axis).
        ValueError: If df is a Series and group_col is specified (cannot group a single series).
        ValueError: If df is a DataFrame and value_col is None.
        ValueError: If highlight is provided for a single-line plot.
        ValueError: If highlight is an empty list.
        ValueError: If highlight values do not match available groups/columns.
        ValueError: If legend_style is not one of ``None``, ``"box"``, or ``"end_of_line"``.
    """
    if legend_style not in (None, "box", "end_of_line"):
        msg = f"legend_style must be one of (None, 'box', 'end_of_line'); got {legend_style!r}"
        raise ValueError(msg)

    df, value_col = _validate_and_prepare_input(df, value_col, x_col, group_col)
    pivot_df = _create_pivot_dataframe(df, value_col, x_col, group_col, fill_na_value)
    highlight = _validate_highlight_parameter(highlight, value_col, group_col, pivot_df)

    highlighted_cols, context_cols = _categorize_columns(pivot_df, highlight)
    highlighted_colors = _generate_colors(highlighted_cols)

    is_multi_line = (group_col is not None) or (isinstance(value_col, list) and len(value_col) > 1)

    ax = _render_plot(pivot_df, highlighted_cols, context_cols, highlighted_colors, is_multi_line, ax, **kwargs)

    return standard_graph_styles(
        ax=ax,
        title=title,
        eyebrow=eyebrow,
        subtitle=subtitle,
        x_label=x_label,
        y_label=y_label,
        legend_title=legend_title,
        move_legend_outside=move_legend_outside,
        show_legend=is_multi_line,
        legend_style=legend_style,
        source_text=source_text,
        grid_axis="y",
        x_margin=0,
    )