Degradation kinetics (first-order decay)

Statistics
Curve Fitting
Scientific Computing
Fit single first-order (SFO) degradation kinetics with replicates, half-life DT50, DT90, and log-linear cross-check.

Overview

Degradation kinetics (first-order decay) fits a single first-order (SFO) kinetic model to chemical residue or potency measurements over time. This approach is widely used in environmental chemistry, pesticide dissipation studies in soil and water, and pharmaceutical stability testing to determine the degradation rate constant \(k\), the half-life \(\text{DT}_{50}\) (time required for 50% degradation), and \(\text{DT}_{90}\) (time required for 90% degradation).

The template accepts time-course sampling data with up to three replicate measurements per sampling time, allowing missing replicate entries. Parameter estimation is performed via nonlinear least squares (NLS) with analytical gradients, yielding Wald confidence intervals for \(C_0\) and \(k\) from the parameter covariance matrix, and delta-method confidence intervals for \(\text{DT}_{50}\) and \(\text{DT}_{90}\). An ordinary least squares (OLS) log-linear cross-check evaluates the difference between logarithmic transformation and direct nonlinear fitting.

The workbook ships pre-populated with illustrative pesticide dissipation data in soil spanning 0 to 60 days, configurable defaults, automated acceptance checks, an in-workbook modification guide, and a validation suite of reference checks (scipy re-fits, closed-form calculations, and synthetic benchmarks).

Before you start

Requirements. Excel with Python for Excel enabled. The template operates entirely within the workbook sheets without requiring external add-ins, instrument software, or command-line scripts.

Core degradation kinetics concepts. - Initial concentration (\(C_0\)): The estimated analyte concentration or residue at time zero (\(t = 0\)). - Degradation rate constant (\(k\)): The fractional decline in residue per unit time, expressed in units of \(1/\text{time}\) (such as \(\text{day}^{-1}\)). - Half-life (\(\text{DT}_{50}\)): The time required for the initial residue concentration to decline by 50%, calculated as \(\ln(2) / k\). - \(\text{DT}_{90}\): The time required for the initial residue concentration to decline by 90%, calculated as \(\ln(10) / k\). - Single first-order (SFO) model: Assumes the degradation rate is directly proportional to current concentration: \[\frac{dC}{dt} = -k C \implies C(t) = C_0 e^{-k t}\] - Replicate handling: Measurements at each sampling interval are treated as individual \((t, y)\) pairs rather than averaged prior to fitting, preserving residual degrees of freedom and within-time experimental variability.

Physical and statistical assumptions. 1. First-order mechanism: Degradation follows simple exponential decline without lag phase, microbial adaptation periods, or biphasic kinetics. 2. Additive measurement error: Measurement noise is assumed additive and normally distributed with constant variance across time points. When errors are additive, nonlinear regression on raw concentrations is more appropriate than linear regression on log-transformed data. 3. Independent observations: Sampling replicates and time points are independently prepared and measured.

Workbook tour

Sheet and range What it holds Kind
Time course, A4:D30 Time-course table: Time (days) and replicate residue measurements (Rep 1, Rep 2, Rep 3 in mg/kg). Editable input
Defaults, A4:B24 Configurable analysis settings: Setting in column A, Value in column B (columns C:D provide allowed values and guidance). Editable input
Results, A4:E8 Degradation fit parameters (C0, k) and derived half-lives (DT50, DT90) with standard error and confidence bounds. Output
Results, A11:B14 Automated acceptance checks: R² floor, minimum distinct sampling times, and DT50 study duration bounds. Output
Results, A17:B28 Fit statistics: n, sampling times, degrees of freedom, R², residual SD (s_y.x), Residual SD % of mean, AICc, outlier check (“No points flagged” or flagged count), and diagnostic flags. Output
Results, G5:H11 Log-linear cross-check: OLS regression of ln(residue) on time, comparing k and DT50 against nonlinear results. Output
Results, A30 Native scatter chart plotting observed replicate residues, fitted degradation curve, and confidence envelope. Output
Curve, A4:D54 Grid of 50 time points with fitted curve and lower/upper confidence bounds feeding the Results chart. Output
Validation, A4:G14 Reference checks: scipy re-fits of the shipped data, closed-form calculations on the scipy re-fit, and recovery of known generating parameters from synthetic noise-free data. Output
Guide, A4:B9 Step-by-step instructions for adapting the workbook to laboratory SOPs and custom time intervals. Authored reference

Using the template

1. Enter time-course observations

  1. Navigate to the Time course sheet.
  2. Enter sampling intervals in column A (Time (days)) starting in row 5.
  3. Enter measured residues for up to three replicates in columns B, C, and D (Rep 1, Rep 2, Rep 3).
  4. If a replicate injection was lost or unavailable, leave the cell blank. The template automatically skips blank cells without requiring row deletions.

2. Configure analysis defaults

  1. Open the Defaults sheet.
  2. Adjust cell B5 (Confidence level) to set the Student \(t\) confidence interval width (default 0.95 for 95% confidence).
  3. Adjust cell B6 (R-squared floor) to specify the goodness-of-fit acceptance threshold (default 0.90).
  4. Verify or change cell B7 (Time unit) and cell B8 (Concentration unit). These labels update the notebook-computed text on the Results sheet, the notebook-written Curve grid header, and task-pane messages. The static Curve sheet header, Time course column headers and chart axis titles are set when the workbook is built and do not follow these cells.
  5. Review cell B9 (Outlier SD threshold) to set the residual multiplier for identifying outlying measurements (default 3.0 standard deviations).

3. Review results and half-lives

  1. Navigate to the Results sheet.
  2. Inspect the parameter estimates table: initial concentration \(C_0\), degradation rate constant \(k\), half-life \(\text{DT}_{50}\), and \(\text{DT}_{90}\) with standard errors and confidence intervals.
  3. Check the Acceptance checks table to verify that \(R^2\) meets the threshold, at least 3 distinct time points are present, and the calculated \(\text{DT}_{50}\) does not exceed the maximum empirical study duration.
  4. Review the Log-linear cross-check table in columns G:H to examine the relative difference between nonlinear least squares and linear regression on log-transformed residues.
  5. Examine the native scatter chart displaying replicate data points alongside the fitted exponential decay curve and confidence envelope.

Inputs and settings

Time course (A5:D30): - Time (days): Elapsed incubation or sampling time (non-negative number). Rows with non-numeric or negative times are excluded from fitting, and the count of dropped rows is reported in a task-pane warning callout. - Rep 1 (mg/kg), Rep 2 (mg/kg), Rep 3 (mg/kg): Measured residue concentration or potency for replicate samples. Blanks and non-numeric entries are safely skipped.

Defaults (A4:B24): - Confidence level (B5): Two-sided Student \(t\) confidence level. Allowed: 0.50 to 0.99. Default 0.95. - R-squared floor (B6): Minimum coefficient of determination required for fit acceptance. Allowed: 0.50 to 0.99. Default 0.90. - Time unit (B7): Free text label for sampling intervals (for example days, hours, weeks). Not validated against a list. Updates the parameter row labels on Results, the notebook-written Curve grid header, and pane messages; the static Curve sheet header, Time course headers and chart axes remain fixed. - Concentration unit (B8): Free text label for residue measurements (for example mg/kg, ug/g, ppm, %). Not validated against a list. Updates the initial concentration (\(C_0\)) row label, the residual SD unit and log-linear cross-check labels on Results, and pane messages; Time course headers and chart axes remain fixed. - Outlier SD threshold (B9): Residual standard deviation multiplier for flagging outliers (\(\lvert e_i \rvert > c \cdot s_{y.x}\)). Allowed: 1.0 to 10.0. Default 3.0.

How results are calculated

  1. Data ingestion and coordinate stacking: The notebook reads the time-course table from the Time course sheet. Non-empty replicate values are extracted along with their corresponding sampling times, yielding 1D observation vectors \((t_i, y_i)\). Blank cells and non-numeric entries are omitted from fitting.

  2. Nonlinear parameter estimation (NLS): The single first-order model \(y(t) = C_0 e^{-kt}\) is fitted by bounded nonlinear least squares using an analytical Jacobian: \[J(t) = \begin{bmatrix} e^{-kt} & -C_0 t e^{-kt} \end{bmatrix}\] Parameters are constrained to non-negative bounds (\(C_0 \ge 0\), \(k \ge 0\)). An initial estimate for \(k\) and \(C_0\) is generated via a log-linear fit through positive residue points.

  3. Covariance and standard errors: The parameter covariance matrix \(V\) is computed from the Jacobian at convergence: \[V = s_{y.x}^2 \left( J^T J \right)^{-1}, \quad s_{y.x} = \sqrt{\frac{\sum (y_i - \hat{y}_i)^2}{n - 2}}\] Standard errors for \(C_0\) and \(k\) are the square roots of the diagonal elements of \(V\). Two-sided Wald confidence intervals for \(C_0\) and \(k\) are formed directly from the parameter covariance matrix using the Student \(t\) critical value with \(n - 2\) degrees of freedom.

  4. Derived half-lives via the delta method: Half-life \(\text{DT}_{50} = \ln(2) / k\) and \(\text{DT}_{90} = \ln(10) / k\) have gradients \(\nabla \text{DT}_{50} = [0, -\ln(2) / k^2]\) and \(\nabla \text{DT}_{90} = [0, -\ln(10) / k^2]\). Their standard errors are calculated using the delta method: \[\mathrm{SE}(\text{DT}_{50}) = \frac{\ln(2)}{k^2} \mathrm{SE}(k), \quad \mathrm{SE}(\text{DT}_{90}) = \frac{\ln(10)}{k^2} \mathrm{SE}(k)\] Two-sided confidence intervals for \(\text{DT}_{50}\) and \(\text{DT}_{90}\) are formed as \(\widehat{\text{DT}} \pm t_{\alpha/2, n-2} \cdot \mathrm{SE}(\text{DT})\) at the user-specified confidence level using the Student \(t\) distribution with \(n - 2\) degrees of freedom.

  5. Log-linear cross-check: For positive observations (\(y_i > 0\)), ordinary least squares is performed on \(\ln(y_i) = \ln(C_0) - k t\). Estimated \(k_{\text{loglin}}\) and \(\text{DT}_{50, \text{loglin}}\) are compared against nonlinear estimates to assess sensitivity to the error structure.

Reading the results

Parameter table (Results!A4:E8): - Initial concentration C0: Fitted residue concentration at time zero (\(C_0\)) with standard error and confidence bounds. - Rate constant k: Fitted first-order degradation rate constant (\(k\)) in units of \(1/\text{time}\). - Half-life DT50: Estimated time to 50% degradation with delta-method confidence interval. - DT90: Estimated time to 90% degradation with delta-method confidence interval.

Acceptance checks (Results!A11:B14): - Fit R-squared at least 0.90: Verifies that the first-order kinetic model explains at least the R-squared floor (default 0.90) of empirical residue variation. - At least 3 distinct sampling times: Verifies that sufficient distinct time points are present to fit 2 parameters with positive degrees of freedom. - DT50 within study duration: Verifies that the calculated half-life does not extrapolate beyond the maximum empirical sampling time (\(t_{\max}\)).

Fit statistics (Results!A17:B28): - Observations used (n): Total non-empty replicate residue measurements included in the regression. - Residual SD, % of mean: Residual standard deviation expressed as a percentage of the mean observed residue (\(100 \times s_{y.x} / \bar{y}\)). - Outlier check: Displays “No points flagged” when all residuals fall within the threshold (\(c \cdot s_{y.x}\)), or reports the count and percentage of flagged observations.

Log-linear cross-check (Results!G5:H11): - k (log-linear) & DT50 (log-linear): Estimates obtained from OLS regression of \(\ln(\text{residue})\) on time. - Difference vs NLS (%): Relative percent discrepancy between log-linear regression and nonlinear least squares.

Saving, sharing and App mode

  • Saving: Save the workbook normally as an .xlsx file. Time-course entries, configurable settings, and published calculation outputs persist within the workbook.
  • Sharing: Share the workbook with collaborators who have Python for Excel enabled. Opening the file re-evaluates the notebook and updates all outputs automatically.
  • App mode: In App mode, non-technical users interact exclusively with the Excel sheets (Time course, Defaults, Results, and Guide) while analytical logic executes seamlessly in the background.

Errors, empty states and limits

  • Empty state: The template ships populated with illustrative dissipation data. If the Time course sheet is completely cleared, the notebook displays a data guard notice and publishes placeholder output rows.
  • Minimum observations: Fitting requires at least 5 usable data points across at least 3 distinct sampling times. If fewer points or sampling times are available, the pane shows a data guard callout and the Results statistics read “Not enough data for a fit”. If the optimizer fails to converge (for example, on values near 1e200), the pane shows a convergence failure callout instead and the statistics read “Fit did not converge”. In both cases the R-squared and DT50 acceptance checks show “Not evaluated” rather than a pass or fail result, and the Curve grid spans 0 to the latest usable sampling time (times with only ND or blank entries do not extend it), with blank fitted and bound columns.
  • Non-numeric and blank cells: Missing replicates and non-numeric text (such as “ND” or “n/a”) are automatically ignored, allowing uneven sampling ladders across time points.
  • Extrapolation guard: When the calculated \(\text{DT}_{50}\) exceeds the longest sampled time (study duration), the acceptance check flags FAIL and a warning callout highlights that the half-life represents an extrapolation beyond the empirical observation window.
  • Row limit: The data sheet ingests up to 26 time-course rows (rows 5 to 30). An overflow cell at Time course!A31 alerts the user if additional rows are pasted beyond row 30.

Customizing as an author

To adjust analytical rules, acceptance floors, or plotting parameters: 1. Open the Boardflare task pane and switch to Edit mode. 2. Locate the marked rules section: python # --- Edit these rules --- DEFAULT_CONFIDENCE = 0.95 DEFAULT_R_SQUARED_MIN = 0.90 # Minimum R^2 floor for first-order kinetic fit acceptance DEFAULT_TIME_UNIT = "days" # Default time unit for rate constant and half-life DEFAULT_CONC_UNIT = "mg/kg" # Default concentration / residue unit OUTLIER_SD = 3.0 # Residual multiplier for flagging outlier observations MIN_TIMES = 3 # Minimum distinct sampling times required MIN_OBSERVATIONS = 5 # Minimum usable observations required GRID_POINTS = 50 # Number of grid points for the fitted curve and confidence band # --- End of edit rules --- 3. Edit thresholds directly, then re-run the cell. Changes immediately take effect in the workbook.

Limitations and when to use this

  • Single first-order only: This workbook models single first-order exponential decay. Biphasic kinetics (such as double first-order in parallel / DFOP, first-order multi-compartment / FOMC, or Hockey-Stick / HS models) and multi-temperature Arrhenius stability modeling are out of scope.
  • Additive noise assumption: The primary nonlinear least-squares fit assumes independent additive normal measurement noise. When noise is purely multiplicative across multiple orders of magnitude, log-linear regression may be preferred; the log-linear cross-check block allows direct numerical comparison.
  • Illustrative sample data: Shipped example values represent synthetic pesticide soil dissipation data with deliberate gaps, illustrating realistic multi-replicate experimental behavior.

Free alternatives

Free alternative: Excel exponential trendlines, or a linear fit of ln(concentration) against time in Excel or Real Statistics. This workbook adds rate constant, half-life and shelf-life estimates with confidence intervals and model diagnostic flags.

Developer reference

Notebook implementation

The notebook imports boardflare, marimo, numpy, pandas, scipy.optimize, and scipy.stats. Input ranges are read via bf.inputs(...):

inputs = bf.inputs(
    time_course=bf.ref("Time course!A4:D30", headers=True),
    defaults=bf.ref("Defaults!A4:B24", headers=True),
    overflow="Time course!A31",
)

Inputs are accessed using dictionary subscript notation (inputs["time_course"]). The notebook extracts non-empty replicate entries into 1D coordinate arrays \((x, y)\), guards against degenerate data, executes bounded nonlinear least squares via the embedded curve-fitting engine model FIRST_ORDER_DECAY, evaluates derived quantities via the delta method, runs the log-linear cross-check, and publishes output tables:

publication = bf.publish(
    outputs={
        "results": results_table,
        "checks": checks_table,
        "statistics": statistics_table,
        "loglinear": loglinear_table,
        "curve": curve_table,
        "validation": validation_table,
    }
)

How the calculation/model works

The single first-order degradation model is parameterized as: \[y(t) = C_0 e^{-k t}\] with bounds \(C_0 \ge 0\) and \(k \ge 0\).

The analytical Jacobian with respect to \([C_0, k]\) is: \[J(t) = \begin{bmatrix} e^{-kt} & -C_0 t e^{-kt} \end{bmatrix}\]

Parameter covariance is estimated as: \[V = s_{y.x}^2 \left( J^T J \right)^{-1}, \quad s_{y.x} = \sqrt{\frac{\sum (y_i - \hat{y}_i)^2}{n - 2}}\]

Derived quantities \(\text{DT}_{50} = \ln(2) / k\) and \(\text{DT}_{90} = \ln(10) / k\) have analytical gradients \(\nabla \text{DT}_{50} = [0, -\ln(2) / k^2]\) and \(\nabla \text{DT}_{90} = [0, -\ln(10) / k^2]\). Their standard errors are calculated via the delta method: \[\mathrm{SE}(\text{DT}_{50}) = \sqrt{\nabla \text{DT}_{50} \cdot V \cdot \nabla \text{DT}_{50}^T} = \frac{\ln(2)}{k^2} \mathrm{SE}(k)\] Two-sided confidence intervals are formed as \(\text{estimate} \pm t_{\alpha/2, n-2} \cdot \mathrm{SE}\).

The error-percent statistic is defined as: \[\text{Residual SD, \% of mean} = 100 \times \frac{s_{y.x}}{\bar{y}}\]

Validation / expected results

The baseline workbook evaluation with sample data produces: - Initial concentration \(C_0\): \(0.9841\ \text{mg/kg}\) (SE: \(0.0141\), 95% CI: \(0.9550\) to \(1.0132\)) - Degradation rate constant \(k\): \(0.0452\ \text{day}^{-1}\) (SE: \(0.0016\), 95% CI: \(0.0419\) to \(0.0485\)) - Half-life \(\text{DT}_{50}\): \(15.33\ \text{days}\) (SE: \(0.54\), 95% CI: \(14.21\) to \(16.45\)) - \(\text{DT}_{90}\): \(50.92\ \text{days}\) (SE: \(1.80\), 95% CI: \(47.19\) to \(54.65\)) - Goodness of fit: \(R^2 = 0.9890\), \(s_{y.x} = 0.0354\ \text{mg/kg}\) (6.79% of mean residue) - Log-linear cross-check: \(k = 0.0459\ \text{day}^{-1}\) (+1.61% difference vs NLS), \(\text{DT}_{50} = 15.09\ \text{days}\) (-1.58% difference vs NLS) - Validation sheet: All ten reference checks report PASS in the baseline evaluation. Six use the shipped data: three compare \(C_0\), \(k\), and \(\mathrm{SE}(k)\) with a scipy re-fit of that data, and three compare \(\text{DT}_{50}\), \(\text{DT}_{90}\), and \(R^2\) with closed-form calculations on the scipy re-fit. Four use a synthetic noise-free dataset: three recover \(C_0\), \(k\), and \(\text{DT}_{50} = \ln(2)/k\) from the known generating values, and one checks the log-linear OLS \(k\) recovery from the template’s log-linear function. The shipped-data checks compare against the scipy re-fit, not against an external reference value.

Changelog

2026-10-26 (Issue #236)

  • Created hand-crafted field-authentic workbook for single first-order degradation kinetics (tier: field-workbook).
  • Built Time course sheet supporting up to 3 replicates with missing value tolerance.
  • Added Defaults sheet for configurable confidence levels, R-squared floor, unit labels, and outlier threshold.
  • Added Results sheet displaying \(C_0\), \(k\), delta-method \(\text{DT}_{50}\) and \(\text{DT}_{90}\) intervals, acceptance checks, fit statistics, and log-linear cross-check.
  • Added native Excel scatter chart with fitted degradation curve and confidence envelope.
  • Implemented Validation sheet with ten reference checks (scipy re-fits, closed-form calculations, and synthetic benchmarks) and in-workbook Modification Guide.
  • Revision: removed the sheet-match row from the Validation sheet, because it failed whenever the shipped time course was edited, so the PASS summary covers only the ten reference checks; labelled Time unit and Concentration unit as free text; reworded the R-squared floor guide as “meets or exceeds”; reworded the Defaults unit notes to match where each label is set; showed Confidence level as a percentage in both fit and no-fit states; set no-fit R-squared and DT50 checks to “Not evaluated”.
  • Revision: the no-fit pane callout and statistics now name the reason (not enough data, or a convergence failure) instead of always showing the data guard; the no-fit Curve grid spans 0 to the latest usable sampling time instead of a fixed 60 time units.