Skip to content

Core

plot_blank_calendar(day_labeler=DayLabeler(), time_labeler=TimeLabeler(), display_settings=None, ax=None, grid_lines=GridLines(), monday_start=True)

Create a blank calendar with no data

Parameters:

Name Type Description Default
day_labeler DayLabeler

instance in order to configure the day labels

DayLabeler()
time_labeler TimeLabeler

instance in order to configure the time labels

TimeLabeler()
display_settings DisplaySettings | None

override of the display settings in the calendar

None
ax Axes | None

Optional axes to plot on

None
grid_lines GridLines

GridLines instance

GridLines()
monday_start bool

whether to start the week on Monday or Sunday

True

Returns:

Type Description
Axes

Modified matplotlib axis

Source code in latent_calendar/plot/core/calendar.py
def plot_blank_calendar(
    day_labeler: DayLabeler = DayLabeler(),
    time_labeler: TimeLabeler = TimeLabeler(),
    display_settings: DisplaySettings | None = None,
    ax: plt.Axes | None = None,
    grid_lines: GridLines = GridLines(),
    monday_start: bool = True,
) -> plt.Axes:
    """Create a blank calendar with no data

    Args:
        day_labeler: instance in order to configure the day labels
        time_labeler: instance in order to configure the time labels
        display_settings: override of the display settings in the calendar
        ax: Optional axes to plot on
        grid_lines: GridLines instance
        monday_start: whether to start the week on Monday or Sunday

    Returns:
        Modified matplotlib axis

    """
    update_start(day_labeler=day_labeler, monday_start=monday_start)

    if display_settings is not None:
        update_display_settings(
            day_labeler=day_labeler,
            time_labeler=time_labeler,
            display_settings=display_settings,
        )

    ax = ax if ax is not None else plt.gca()

    configure_axis(ax=ax, day_labeler=day_labeler, time_labeler=time_labeler)
    grid_lines.configure_grid(ax=ax)

    return ax

plot_calendar(calendar_iter, *, day_labeler=DayLabeler(), time_labeler=TimeLabeler(), display_settings=None, cmap=None, alpha=None, ax=None, grid_lines=GridLines(), monday_start=True)

Plot a calendar from generator of values.

This can plot both numpy matrix and DataFrame values as long as the iterable fits CALENDAR_ITERATION definition

Parameters:

Name Type Description Default
calendar_iter CALENDAR_ITERATION

CALENDAR_ITERATION

required
day_labeler DayLabeler

instance in order to configure the day labels

DayLabeler()
time_labeler TimeLabeler

instance in order to configure the time labels

TimeLabeler()
display_settings DisplaySettings | None

override of the display settings in the calendar

None
cmap CMAP | None

function that maps floats to string colors

None
ax Axes | None

Optional axes to plot on

None
grid_lines GridLines

GridLines instance

GridLines()
monday_start bool

whether to start the week on Monday or Sunday

True

Returns:

Type Description
Axes

Modified matplotlib axis

Source code in latent_calendar/plot/core/calendar.py
def plot_calendar(
    calendar_iter: CALENDAR_ITERATION,
    *,
    day_labeler: DayLabeler = DayLabeler(),
    time_labeler: TimeLabeler = TimeLabeler(),
    display_settings: DisplaySettings | None = None,
    cmap: CMAP | None = None,
    alpha: float | None = None,
    ax: plt.Axes | None = None,
    grid_lines: GridLines = GridLines(),
    monday_start: bool = True,
) -> plt.Axes:
    """Plot a calendar from generator of values.

    This can plot both numpy matrix and DataFrame values as long as the iterable fits CALENDAR_ITERATION definition

    Args:
        calendar_iter: CALENDAR_ITERATION
        day_labeler: instance in order to configure the day labels
        time_labeler: instance in order to configure the time labels
        display_settings: override of the display settings in the calendar
        cmap: function that maps floats to string colors
        ax: Optional axes to plot on
        grid_lines: GridLines instance
        monday_start: whether to start the week on Monday or Sunday

    Returns:
        Modified matplotlib axis

    """
    ax = plot_blank_calendar(
        day_labeler=day_labeler,
        time_labeler=time_labeler,
        display_settings=display_settings,
        ax=ax,
        grid_lines=grid_lines,
        monday_start=monday_start,
    )

    if cmap is None:

        def cmap(x: float) -> str:
            return "lightblue"

    for calendar_data in calendar_iter:
        event = CalendarEvent.from_calendar_data(calendar_data=calendar_data)

        event.plot(
            ax=ax,
            facecolor=cmap(calendar_data.value),
            alpha=alpha,
            monday_start=monday_start,
        )

    return ax

plot_calendar_by_row(df, max_cols=3, title_func=None, day_labeler=None, time_labeler=None, cmaps=None, grid_lines=GridLines(), monday_start=True)

Iterate a DataFrame by row and plot calendar events.

Parameters:

Name Type Description Default
df DataFrame

wide DataFrame where each column is the vocabulary

required
max_cols int

max number of columns in the created grid.

3
title_func TITLE_FUNC | None

function to make the title from DataFrame index and DataFrame row, default like '2020-01-01 n_trip(s) = 10'

None
day_labeler DayLabeler | None

base day_labeler

None
time_labeler TimeLabeler | None

base day_labeler

None
cmaps CMAP | ColorMap | CMAP_GENERATOR | None

Colormapping function(s) to use for each row

None
grid_lines GridLines

GridLines instance

GridLines()
monday_start bool

whether to start the week on Monday or Sunday

True

Returns:

Type Description
None

None

Source code in latent_calendar/plot/core/calendar.py
def plot_calendar_by_row(
    df: pd.DataFrame,
    max_cols: int = 3,
    title_func: TITLE_FUNC | None = None,
    day_labeler: DayLabeler | None = None,
    time_labeler: TimeLabeler | None = None,
    cmaps: CMAP | ColorMap | CMAP_GENERATOR | None = None,
    grid_lines: GridLines = GridLines(),
    monday_start: bool = True,
) -> None:
    """Iterate a DataFrame by row and plot calendar events.

    Args:
        df: wide DataFrame where each column is the vocabulary
        max_cols: max number of columns in the created grid.
        title_func: function to make the title from DataFrame index and DataFrame row, default like '2020-01-01 n_trip(s) = 10'
        day_labeler: base day_labeler
        time_labeler: base day_labeler
        cmaps: Colormapping function(s) to use for each row
        grid_lines: GridLines instance
        monday_start: whether to start the week on Monday or Sunday

    Returns:
        None

    """
    n_cols = len(df.columns)
    if n_cols % 7 != 0:
        raise CalendarFormatError(
            f"Number of columns must be a multiple of 7, got {n_cols} columns. Make sure DataFrame is in wide calendar format."
        )

    title_func = title_func if title_func is not None else default_title_func

    if isinstance(cmaps, ColorMap):
        cmaps = repeat(cmaps)

    if cmaps is None:
        cmaps = repeat(create_default_cmap(value=df.to_numpy().max()))

    total = len(df)

    for (ax, plot_axes), (idx, row), cmap in zip(
        default_axes_and_grid_axes(
            total=total,
            max_cols=max_cols,
            day_labeler=day_labeler,
            time_labeler=time_labeler,
        ),
        df.iterrows(),
        cmaps,
    ):
        calendar_data = row.to_numpy()
        plot_calendar(
            iterate_long_array(calendar_data),
            day_labeler=day_labeler,
            time_labeler=time_labeler,
            grid_lines=grid_lines,
            ax=ax,
            cmap=cmap,
            monday_start=monday_start,
        )
        title = title_func(idx, row)
        ax.set_title(title)

plot_dataframe_as_calendar(df, config, *, day_labeler=DayLabeler(), time_labeler=TimeLabeler(), grid_lines=GridLines(), cmap=None, alpha=None, ax=None, monday_start=True)

Simple Wrapper about plot_calendar in order to plot DataFrame in various formats

Parameters:

Name Type Description Default
df DataFrame

DataFrame in format with columns in config instance

required
config DataFrameConfig

DataFrameConfig

required
day_labeler DayLabeler

instance in order to configure the day labels

DayLabeler()
time_labeler TimeLabeler

instance in order to configure the time labels

TimeLabeler()
grid_lines GridLines

GridLines instance

GridLines()
cmap CMAP | None

function that maps floats to string colors

None
alpha float | None

alpha level of each rectangle

None
ax Axes | None

optional axis to plot on

None
monday_start bool

whether to start the week on Monday or Sunday

True

Returns:

Type Description
Axes

new or modified axes

Source code in latent_calendar/plot/core/calendar.py
def plot_dataframe_as_calendar(
    df: pd.DataFrame,
    config: DataFrameConfig,
    *,
    day_labeler: DayLabeler = DayLabeler(),
    time_labeler: TimeLabeler = TimeLabeler(),
    grid_lines: GridLines = GridLines(),
    cmap: CMAP | None = None,
    alpha: float | None = None,
    ax: plt.Axes | None = None,
    monday_start: bool = True,
) -> plt.Axes:
    """Simple Wrapper about plot_calendar in order to plot DataFrame in various formats

    Args:
        df: DataFrame in format with columns in config instance
        config: DataFrameConfig
        day_labeler: instance in order to configure the day labels
        time_labeler: instance in order to configure the time labels
        grid_lines: GridLines instance
        cmap: function that maps floats to string colors
        alpha: alpha level of each rectangle
        ax: optional axis to plot on
        monday_start: whether to start the week on Monday or Sunday

    Returns:
        new or modified axes

    """
    return plot_calendar(
        iterate_dataframe(df, config),
        day_labeler=day_labeler,
        time_labeler=time_labeler,
        cmap=cmap,
        alpha=alpha,
        ax=ax,
        monday_start=monday_start,
        grid_lines=grid_lines,
    )

plot_dataframe_grid_across_column(df, grid_col, config=None, max_cols=3, *, alpha=None, monday_start=True, day_labeler=DayLabeler(), time_labeler=TimeLabeler(), grid_lines=GridLines())

Plot the long DataFrame in a grid by some different column.

Continuous version of the plot_calendar_by_row

Parameters:

Name Type Description Default
df DataFrame

DataFrame to plot. Requires all the columns in config

required
grid_col str

column name of DataFrame to plot across

required
config DataFrameConfig | None

DataFrameConfig instance of the column mapping. Default IterConfig

None
max_cols int

max number of columns in the grid

3
alpha float | None

alpha of each calendar event

None
monday_start bool

whether to start the week on Monday or Sunday

True
Source code in latent_calendar/plot/core/calendar.py
def plot_dataframe_grid_across_column(
    df: pd.DataFrame,
    grid_col: str,
    config: DataFrameConfig | None = None,
    max_cols: int = 3,
    *,
    alpha: float | None = None,
    monday_start: bool = True,
    day_labeler: DayLabeler = DayLabeler(),
    time_labeler: TimeLabeler = TimeLabeler(),
    grid_lines: GridLines = GridLines(),
) -> None:
    """Plot the long DataFrame in a grid by some different column.

    Continuous version of the plot_calendar_by_row

    Args:
        df: DataFrame to plot. Requires all the columns in config
        grid_col: column name of DataFrame to plot across
        config: DataFrameConfig instance of the column mapping. Default IterConfig
        max_cols: max number of columns in the grid
        alpha: alpha of each calendar event
        monday_start: whether to start the week on Monday or Sunday

    """
    if grid_col not in df.columns:
        msg = f"{grid_col} is not in the DataFrame."
        raise KeyError(msg)

    values = df.loc[:, grid_col].dropna().unique()
    values.sort()

    total = len(values)

    for (ax, plot_axes), value in zip(
        default_axes_and_grid_axes(
            total=total,
            max_cols=max_cols,
            day_labeler=day_labeler,
            time_labeler=time_labeler,
        ),
        values,
    ):
        idx = df[grid_col] == value
        df_tmp = df.loc[idx, :]

        day_labeler, time_labeler = plot_axes

        plot_dataframe_as_calendar(
            df=df_tmp,
            config=config,
            ax=ax,
            day_labeler=day_labeler,
            time_labeler=time_labeler,
            alpha=alpha,
            monday_start=monday_start,
        )
        ax.set_title(f"{value}")

plot_series_as_calendar(series, *, grid_lines=GridLines(), day_labeler=DayLabeler(), time_labeler=TimeLabeler(), cmap=None, alpha=None, ax=None, monday_start=True)

Simple Wrapper about plot_calendar in order to plot Series in various formats

Parameters:

Name Type Description Default
series Series

Series in format with index as datetime and values as float

required
grid_lines GridLines

GridLines instance

GridLines()
day_labeler DayLabeler

instance in order to configure the day labels

DayLabeler()
time_labeler TimeLabeler

instance in order to configure the time labels

TimeLabeler()
cmap CMAP | None

function that maps floats to string colors

None
alpha float | None

alpha level of each rectangle

None
ax Axes | None

optional axis to plot on

None
monday_start bool

whether to start the week on Monday or Sunday

True

Returns:

Type Description
Axes

new or modified axes

Source code in latent_calendar/plot/core/calendar.py
def plot_series_as_calendar(
    series: pd.Series,
    *,
    grid_lines: GridLines = GridLines(),
    day_labeler: DayLabeler = DayLabeler(),
    time_labeler: TimeLabeler = TimeLabeler(),
    cmap: CMAP | None = None,
    alpha: float | None = None,
    ax: plt.Axes | None = None,
    monday_start: bool = True,
) -> plt.Axes:
    """Simple Wrapper about plot_calendar in order to plot Series in various formats

    Args:
        series: Series in format with index as datetime and values as float
        grid_lines: GridLines instance
        day_labeler: instance in order to configure the day labels
        time_labeler: instance in order to configure the time labels
        cmap: function that maps floats to string colors
        alpha: alpha level of each rectangle
        ax: optional axis to plot on
        monday_start: whether to start the week on Monday or Sunday

    Returns:
        new or modified axes

    """
    if cmap is None:
        cmap = create_default_cmap(value=series.to_numpy().max())

    return plot_calendar(
        iterate_series(series),
        day_labeler=day_labeler,
        time_labeler=time_labeler,
        cmap=cmap,
        alpha=alpha,
        ax=ax,
        monday_start=monday_start,
        grid_lines=grid_lines,
    )

Plots including a model.

plot_component_distribution(X_latent, model, ax)

Third profile plot.

Source code in latent_calendar/plot/core/model.py
def plot_component_distribution(
    X_latent: np.ndarray, model: LatentCalendar, ax: plt.Axes
) -> plt.Axes:
    """Third profile plot."""
    x = range(len(X_latent))
    ax.bar(x, X_latent)
    step = 1 if model.n_components < 15 else 2
    ax.set_xticks(np.arange(model.n_components, step=step))
    ax.set_ylabel("P[L=l | Data]")
    ax.set_title("Latent Component Distribution")

    return ax

plot_distribution(X_probs, ax, display_y_axis=True, divergent=True, day_labeler=DayLabeler(), time_labeler=TimeLabeler())

Second plot of the profile calendar probability distribution.

Source code in latent_calendar/plot/core/model.py
def plot_distribution(
    X_probs: np.ndarray,
    ax: plt.Axes,
    display_y_axis: bool = True,
    divergent: bool = True,
    day_labeler: DayLabeler = DayLabeler(),
    time_labeler: TimeLabeler = TimeLabeler(),
) -> plt.Axes:
    """Second plot of the profile calendar probability distribution."""
    time_labeler.display = display_y_axis

    data, cmap = settle_data_and_cmap(data=X_probs, divergent=divergent)

    iter_data = iterate_long_array(data)

    subtext = "Comparison to random rate" if divergent else "Raw Probabilities"
    plot_calendar(
        iter_data,
        ax=ax,
        cmap=cmap,
        day_labeler=day_labeler,
        time_labeler=time_labeler,
    )
    title = f"Predicted Probability Distribution\n{subtext}"
    ax.set_title(title)

    return ax

plot_model_components(model, max_cols=5, divergent=True, components=None, day_labeler=DayLabeler(), time_labeler=TimeLabeler())

Helper function to create plot of all the components of the LatentCalendar instance.

Parameters:

Name Type Description Default
model LatentCalendar

LatentCalendar instance

required
max_cols int

maximum number of columns in the grid of calendar components.

5
divergent bool

what data to plot

True
components Iterable[int] | None

Specific subset of components to plot. Default is all

None
day_labeler DayLabeler

DayLabeler instance

DayLabeler()
time_labeler TimeLabeler

TimeLabeler instance

TimeLabeler()

Returns:

Type Description
None

None

Source code in latent_calendar/plot/core/model.py
def plot_model_components(
    model: LatentCalendar,
    max_cols: int = 5,
    divergent: bool = True,
    components: Iterable[int] | None = None,
    day_labeler: DayLabeler = DayLabeler(),
    time_labeler: TimeLabeler = TimeLabeler(),
) -> None:
    """Helper function to create plot of all the components of the LatentCalendar instance.

    Args:
        model: LatentCalendar instance
        max_cols: maximum number of columns in the grid of calendar components.
        divergent: what data to plot
        components: Specific subset of components to plot. Default is all
        day_labeler: DayLabeler instance
        time_labeler: TimeLabeler instance

    Returns:
        None

    """
    if components is None:
        components = list(range(model.n_components))

    if any([component > model.n_components - 1 for component in components]):
        msg = f"One of the listed components is greater than the total number {model.n_components}"
        raise ValueError(msg)

    total = len(components)
    normalized_components_to_plot = model.normalized_components_[components]

    def get_title(component_idx: int) -> str:
        return f"Component {component_idx}"

    # TOOD: refactor to just use the plot_calendar_by_row ?
    values = zip(
        components,
        default_axes_and_grid_axes(
            total=total,
            max_cols=max_cols,
            day_labeler=day_labeler,
            time_labeler=time_labeler,
        ),
        normalized_components_to_plot,
    )
    for component_idx, (ax, plot_axes), latent in values:
        data, cmap = settle_data_and_cmap(latent, divergent)

        day_labeler, time_labeler = plot_axes
        plot_calendar(
            iterate_long_array(data),
            cmap=cmap,
            ax=ax,
            day_labeler=day_labeler,
            time_labeler=time_labeler,
        )
        title = get_title(component_idx=component_idx)
        ax.set_title(title)

plot_model_predictions(X_to_predict, X_holdout, model, divergent=True, axes=None, day_labeler=DayLabeler(), time_labeler=TimeLabeler())

Plot the model predictions compared to the test data.

Parameters:

Name Type Description Default
X_to_predict ndarray

Data for the model

required
X_holdout ndarray

Holdout data for the model

required
model LatentCalendar

LatentCalendar model instance

required
divergent bool

Option to change the data displayed

True
axes Iterable[Axes]

list of 3 axes to plot this data

None

Returns:

Type Description
Iterable[Axes]

The axes used for plotting

Source code in latent_calendar/plot/core/model.py
def plot_model_predictions(
    X_to_predict: np.ndarray,
    X_holdout: np.ndarray,
    model: LatentCalendar,
    divergent: bool = True,
    axes: Iterable[plt.Axes] = None,
    day_labeler: DayLabeler = DayLabeler(),
    time_labeler: TimeLabeler = TimeLabeler(),
) -> Iterable[plt.Axes]:
    """Plot the model predictions compared to the test data.

    Args:
        X_to_predict: Data for the model
        X_holdout: Holdout data for the model
        model: LatentCalendar model instance
        divergent: Option to change the data displayed
        axes: list of 3 axes to plot this data

    Returns:
        The axes used for plotting

    """
    X_to_predict = X_to_predict[np.newaxis, :]
    X_holdout = X_holdout[np.newaxis, :]

    if axes is None:
        _, axes = plt.subplots(nrows=1, ncols=3)

    X_to_predict_probs = model.predict(X_to_predict)[0]

    ax = axes[0]
    plot_raw_data(
        array=X_to_predict, ax=ax, day_labeler=day_labeler, time_labeler=time_labeler
    )
    ax.set_title("Raw Data for Prediction")

    ax = axes[1]
    plot_distribution(
        X_probs=X_to_predict_probs,
        ax=ax,
        display_y_axis=False,
        divergent=divergent,
        day_labeler=day_labeler,
        time_labeler=time_labeler,
    )
    ax.set_title("Distribution from Prediction")

    ax = axes[2]
    plot_raw_data(
        array=X_holdout,
        ax=ax,
        display_y_axis=False,
        day_labeler=day_labeler,
        time_labeler=time_labeler,
    )
    ax.set_title("Raw Data in Future")

    return axes

plot_profile(array, model, divergent=True, axes=None, include_components=True, day_labeler=DayLabeler(), time_labeler=TimeLabeler())

Create a profile plot with 3 different plots.

Displays the raw data, predicted probability distribution, and latent breakdown.

Parameters:

Name Type Description Default
array ndarray

long array (n_timeslots, )

required
model LatentCalendar

LatentCalendar model instance

required
divergent bool

Option to change the data displayed

True
axes Iterable[Axes]

list of 3 axes to plot this data

None
include_components bool

If the last component plot should be included

True
day_labeler DayLabeler

DayLabeler instance

DayLabeler()
time_labeler TimeLabeler

TimeLabeler instance

TimeLabeler()

Returns:

Type Description
ndarray

None

Source code in latent_calendar/plot/core/model.py
def plot_profile(
    array: np.ndarray,
    model: LatentCalendar,
    divergent: bool = True,
    axes: Iterable[plt.Axes] = None,
    include_components: bool = True,
    day_labeler: DayLabeler = DayLabeler(),
    time_labeler: TimeLabeler = TimeLabeler(),
) -> np.ndarray:
    """Create a profile plot with 3 different plots.

    Displays the raw data, predicted probability distribution, and latent breakdown.

    Args:
        array: long array (n_timeslots, )
        model: LatentCalendar model instance
        divergent: Option to change the data displayed
        axes: list of 3 axes to plot this data
        include_components: If the last component plot should be included
        day_labeler: DayLabeler instance
        time_labeler: TimeLabeler instance

    Returns:
        None

    """
    ncols = 3 if include_components else 2
    if axes is None:
        _, axes = plt.subplots(nrows=1, ncols=ncols)

    if len(axes) != ncols:
        msg = "The axes do not equal the number of plots required."
        raise ValueError(msg)

    # Data under model
    X_new = array[np.newaxis, :]
    X_probs = model.predict(X_new)[0]

    # Raw Data
    ax = axes[0]
    plot_raw_data(
        array=array, ax=ax, day_labeler=day_labeler, time_labeler=time_labeler
    )

    # Under Model
    ax = axes[1]
    plot_distribution(
        X_probs=X_probs,
        ax=ax,
        display_y_axis=False,
        divergent=divergent,
        day_labeler=day_labeler,
        time_labeler=time_labeler,
    )

    # Component distribution
    if include_components:
        ax = axes[2]
        X_latent = model.transform(X_new)[0]
        plot_component_distribution(X_latent=X_latent, model=model, ax=ax)

    return axes

plot_raw_data(array, ax, display_y_axis=True, day_labeler=DayLabeler(), time_labeler=TimeLabeler())

First plot of raw data.

Source code in latent_calendar/plot/core/model.py
def plot_raw_data(
    array: np.ndarray,
    ax: plt.Axes,
    display_y_axis: bool = True,
    day_labeler: DayLabeler = DayLabeler(),
    time_labeler: TimeLabeler = TimeLabeler(),
) -> plt.Axes:
    """First plot of raw data."""
    try:
        max_value = np.quantile(array[array > 0], 0.95)
    except IndexError:
        max_value = 1

    time_labeler.display = display_y_axis

    cmap = create_default_cmap(value=max_value)

    plot_calendar(
        iterate_long_array(array),
        ax=ax,
        cmap=cmap,
        time_labeler=time_labeler,
        day_labeler=day_labeler,
    )
    ax.set_title("Raw Data")

    return ax

Comments