Skip to content

period_summary

period_summary ¤

Weekly/monthly summary module.

Roll up daily metrics to weekly/monthly summaries: - Weekly summary of KPIs - Monthly totals and averages - Period-over-period comparison

Two modes of operation: 1. From raw signals: weekly_summary(counter_uuid, ...) 2. From pre-computed daily DataFrames: from_daily_data(daily_df)

PeriodSummaryEvents ¤

PeriodSummaryEvents(
    dataframe: DataFrame, *, time_column: str = "systime"
)

Bases: Base

Aggregate daily metrics into weekly/monthly summaries.

Merge keys: [week_start, week_end] for weekly, [year, month] for monthly.

Two usage patterns:

Pattern A — from raw signals:

summary = PeriodSummaryEvents(df)
weekly = summary.weekly_summary(counter_uuid='prod')

Pattern B — pipeline (compose from upstream modules):

# Build a daily DataFrame from any combination of upstream modules
quality = QualityEvents(df).daily_quality_summary('ok', 'nok')
downtime = DowntimeEvents(df).availability_trend('state', window='1D')

# Merge on date
daily = quality.merge(downtime.rename(columns={'period': 'date'}), on='date')

# Roll up to weekly/monthly
weekly = PeriodSummaryEvents.from_daily_data(daily, window='W')
monthly = PeriodSummaryEvents.from_daily_data(daily, window='MS')
Example

A part counter that rises by 100 a day:

import ts_shape df = ts_shape.make_timeseries(["counter"], n_points=14, freq="1D", ... start="2025-01-06", baseline=0, noise=0, drift=1300, ... value_column="value_integer") weekly = ts_shape.PeriodSummaryEvents(df).weekly_summary("counter") weekly[["start", "end", "total_production", "daily_avg"]] start end total_production daily_avg 0 2025-01-06 2025-01-13 600 85.714286 1 2025-01-13 2025-01-20 700 100.000000

from_daily_data staticmethod ¤

from_daily_data(
    daily_df: DataFrame,
    *,
    window: str = "W",
    date_column: str = "date",
    aggregations: dict[str, str] | None = None
) -> pd.DataFrame

Roll up a pre-computed daily DataFrame to weekly or monthly.

This is the pipeline-friendly entry-point. It accepts any daily DataFrame (e.g. merged output of QualityEvents, DowntimeEvents, OEEEvents, ReliabilityEvents, the energy EnPI / SEC tables) and aggregates its numeric columns by a declared rule per column. A ratio KPI of a period is recomputed from the period's summed basis columns (ISO 22400-2), never summed and, when its basis is present, not averaged either:

======================== ========================================== column period value ======================== ========================================== counts, durations, sum (names containing parts, energy quantity, production, count, failures, resets, seconds, minutes, hours, kwh, energy, units, output, ...) quality_pct, 100 x sum(good) / sum(total), good = first_pass_yield_pct good_parts or ok_parts, total = total_parts or ok + nok / good + reject nok_rate_pct 100 x sum(nok) / sum(total) availability_pct 100 x sum(run) / sum(planned): run / planned = run_seconds / planned_seconds (OEE), uptime_* / (uptime + downtime) or total_seconds (downtime), operating_hours / (operating + repair) (reliability) performance_pct 100 x sum(actual_parts) / sum(ideal_parts) oee_pct availability x performance x quality of the period (each from its summed bases) mttr_hours sum(repair_hours) / sum(failures) mtbf_hours sum(operating_hours) / sum(failures) mtbf_iso22400_hours sum(operating) / (sum(failures) + 1) failure_rate_per_hour sum(failures) / sum(operating_hours) enpi sum(energy_kwh) / sum(units_produced) (ISO 50006) sec sum(total_energy) / sum(total_output) efficiency sum(units) / sum(energy) ======================== ==========================================

When a rule's basis columns are missing the ratio is the mean of the daily values weighted by the ratio's denominator if that column is present (total_parts for quality, planned_seconds / total_seconds for availability and OEE, ideal_parts / run_seconds for performance, failures for MTTR/MTBF, units_produced for EnPI, ...), which is exact. Availability, OEE and other *_pct columns fall back to the mean weighted by the daily rows' duration_seconds (time-weighted). With no weight either, the plain mean is used and a :class:~ts_shape.errors.DataQualityWarning names the columns whose basis is missing. Other intensive columns (avg, mean, rate, ratio, cycle times) are averaged the same way. Numeric columns that match no rule are dropped with a DataQualityWarning naming them, instead of being summed; pass aggregations to keep them.

The output always includes start, end, and days columns.

Parameters:

Name Type Description Default
daily_df DataFrame

DataFrame with a date column and numeric metric columns. Expected merge key: [date].

required
window str

Pandas frequency string. 'W' (pandas' weeks ending Sunday, same as 'W-SUN') gives Monday-to-Sunday weeks: start is the Monday, end the following Sunday (inclusive), even when the data covers only part of the week. An anchored 'W-<DAY>' gives weeks ending on <DAY> (e.g. 'W-SAT' = Sunday-to-Saturday). 'MS' = calendar months (end = last day of the month); any other frequency is passed to :class:pandas.Grouper and end is the last date present in the group.

'W'
date_column str

Name of the date column.

'date'
aggregations dict[str, str] | None

Optional per-column override, {column: how} with how one of "sum", "mean", "min", "max", "first", "last" or "drop". It wins over the built-in rules.

None

Returns:

Type Description
DataFrame

DataFrame with start, end, days, and aggregated

DataFrame

metric columns.

Example::

quality = QualityEvents(df).daily_quality_summary('ok', 'nok')
weekly = PeriodSummaryEvents.from_daily_data(quality, window='W')
# → [start, end, days, ok_parts, nok_parts,
#    total_parts, quality_pct, ...]

weekly_summary ¤

weekly_summary(
    counter_uuid: str,
    ok_counter_uuid: str | None = None,
    nok_counter_uuid: str | None = None,
    *,
    value_column: str = "value_integer",
    handle_resets: bool = True
) -> pd.DataFrame

Roll up daily production to weekly summaries from raw signals.

For pipeline usage with pre-computed DataFrames, use :meth:from_daily_data instead.

Parameters:

Name Type Description Default
counter_uuid str

UUID of production counter.

required
ok_counter_uuid str | None

UUID of good parts counter (optional).

None
nok_counter_uuid str | None

UUID of the NOK part counter (optional).

None
value_column str

Column containing counter values.

'value_integer'
handle_resets bool

When True (default), treat a decreasing counter as a reset and count the production after the reset instead of losing it (a daily boundary is a common reset point). Increments are booked to the day of the reading that reports them, so no production is lost between days. The result gains a resets column with the number of resets observed each week.

True

Returns:

Type Description
DataFrame

DataFrame with columns:

DataFrame
  • start, end, duration_seconds: the week window (Monday midnight to the following Monday midnight, tz-aware in the data's tz)
DataFrame
  • week_start, week_end, total_production, daily_avg, production_days, [ok_parts, nok_parts, quality_pct]
DataFrame
  • resets: Number of counter resets in the week (only when handle_resets=True)

monthly_summary ¤

monthly_summary(
    counter_uuid: str,
    ok_counter_uuid: str | None = None,
    nok_counter_uuid: str | None = None,
    *,
    value_column: str = "value_integer",
    handle_resets: bool = True
) -> pd.DataFrame

Roll up daily production to monthly summaries from raw signals.

For pipeline usage with pre-computed DataFrames, use :meth:from_daily_data instead.

Parameters:

Name Type Description Default
counter_uuid str

UUID of production counter.

required
ok_counter_uuid str | None

UUID of good parts counter (optional).

None
nok_counter_uuid str | None

UUID of the NOK part counter (optional).

None
value_column str

Column containing counter values.

'value_integer'
handle_resets bool

When True (default), treat a decreasing counter as a reset and count the production after the reset instead of losing it. Increments are booked to the day of the reading that reports them, so no production is lost between days. The result gains a resets column with the number of resets observed each month.

True

Returns:

Type Description
DataFrame

DataFrame with columns:

DataFrame
  • start, end, duration_seconds: the calendar-month window (first of the month midnight to first of the next month midnight, tz-aware in the data's tz)
DataFrame
  • year, month, total_production, daily_avg, production_days, [ok_parts, nok_parts, quality_pct]
DataFrame
  • resets: Number of counter resets in the month (only when handle_resets=True)

compare_periods ¤

compare_periods(
    counter_uuid: str,
    period_a: tuple[str, str],
    period_b: tuple[str, str],
    *,
    value_column: str = "value_integer",
    handle_resets: bool = True
) -> pd.DataFrame

Compare production between two time periods.

Parameters:

Name Type Description Default
counter_uuid str

UUID of production counter.

required
period_a tuple[str, str]

Tuple of (start_date, end_date) for first period.

required
period_b tuple[str, str]

Tuple of (start_date, end_date) for second period.

required
value_column str

Column containing counter values.

'value_integer'
handle_resets bool

When True (default), treat a decreasing counter as a reset and count the production after the reset instead of losing it, so the compared metrics are not understated for days containing a reset.

True

Returns:

Type Description
DataFrame

DataFrame with columns: metric, period_a_value, period_b_value,

DataFrame

change, change_pct. period1_value / period2_value are

DataFrame

deprecated twins of period_a_value / period_b_value (each

DataFrame

sits right before its replacement); they are removed in the next

DataFrame

major release.