Skill Tracer
Skills › Scientific Agent Skills › relsa-severity-assessment

relsa-severity-assessment

skill · 364 lines · MIT · skills/relsa-severity-assessment/SKILL.md

The facts

Its description

Supports multivariate severity assessment and exploratory endpoint-time score forecasting for laboratory animal studies using the RELSA (RELative Severity Assessment) score and ARIMA-based foRcast forecasting. Use when combining welfare readouts — body weight or weight loss, body temperature, clinical or nesting scores, biomarkers, activity, heart rate, burrowing, wheel running — into one severity score per animal per day, when asking which animals are at risk of reaching a humane endpoint at a specified future observation time, when defining attention/danger zones or thresholds on a severity scale by kernel density estimation, or when reporting severity for a 3Rs, refinement, animal-welfare, or EU Directive 2010/63/EU severity-assessment context. Covers directionality ("turned" variables), baseline normalization, reference sets, RELSA weights, ARIMA prediction intervals, and RMSE/PICP/MPIW evaluation.

The description the agent reads to decide when to use it.

From its settingsline 3

Tools it may use, or may not

allowed-tools: Read Write Edit Bash

Tools it may use while it runs without asking you first.

From its settingsline 5

Runs scripts

bin/python, bin/activate, python scripts/relsa_score.py, python scripts/forecast_relsa.py (first 5 of 10 lines)

Scripts the text names. Found by matching words, so treat them as pointers.

Found by matching wordsline 53 line 54 line 126 line 191 line 206

Files it mentions

references/thresholds-and-zones.md, scripts/relsa_score.py, scripts/forecast_relsa.py, scripts/kde_thresholds.py, scripts/_common.py (first 5 of 8 lines)

Files the text names, such as ones to read or write. Found by matching words, so treat them as pointers.

Found by matching wordsline 242 line 306 line 309 line 312 line 314

Licence

MIT, from the skill file

The terms its author shares it under, which let it be shown here.

From its settingsline 4 licence text

Other settings

compatibility: Requires Python >=3.12 with numpy, pandas, scipy, statsmodels and matplotlib. Local analysis needs no network or credentials; installation and upstream review need network access. metadata: version: "1.3" last-reviewed: "2026-10-01" skill-author: K-Dense Inc.

More settings at the top of the file, shown as written.

From its settingsline 6 line 7
Other files in this skill (7) Close

The text

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1---
2name: relsa-severity-assessment
3description: Supports multivariate severity assessment and exploratory endpoint-time score forecasting for laboratory animal studies using the RELSA (RELative Severity Assessment) score and ARIMA-based foRcast forecasting. Use when combining welfare readouts — body weight or weight loss, body temperature, clinical or nesting scores, biomarkers, activity, heart rate, burrowing, wheel running — into one severity score per animal per day, when asking which animals are at risk of reaching a humane endpoint at a specified future observation time, when defining attention/danger zones or thresholds on a severity scale by kernel density estimation, or when reporting severity for a 3Rs, refinement, animal-welfare, or EU Directive 2010/63/EU severity-assessment context. Covers directionality ("turned" variables), baseline normalization, reference sets, RELSA weights, ARIMA prediction intervals, and RMSE/PICP/MPIW evaluation.
4license: MIT
5allowed-tools: Read Write Edit Bash
6compatibility: Requires Python >=3.12 with numpy, pandas, scipy, statsmodels and matplotlib. Local analysis needs no network or credentials; installation and upstream review need network access.
7metadata:
8 version: "1.3"
9 last-reviewed: "2026-10-01"
10 skill-author: K-Dense Inc.
11---
13# RELSA severity assessment and exploratory score forecasting

RELSA severity assessment and exploratory score forecasting

Raw lines 1 to 14
15## Overview
17This skill computes reference-relative multivariate scores and exploratory ARIMA forecasts.
18It targets the equations and printed example of [RELSA 0.0.1.9000](https://github.com/mytalbot/RELSA)
19(commit `e68e8451e8719ccc3600179f55900cd7254ede9d`, current upstream at review), plus the
20published [foRcast methodology](https://doi.org/10.3389/fphys.2026.1869563).
21The Python forecast helper is an independent nonseasonal approximation, not an exact R port.
23RELSA is the RMS of directional deviations divided by each variable's reference maximum
24**deviation**. Zero means no measured worsening in those directions; it does not establish
25normal welfare. One is a reference scale unit, not a universal endpoint or an upper bound.
26Different variables may attain their reference extrema in different animals or at different
27times, so the reference cohort need not contain a score of exactly one.
29Forecasts estimate a **score at a specified time**, not time-to-endpoint or probability of death.
30Keep the approved study's observation schedule and humane endpoint criteria separate from
31these exploratory outputs. See [Boundaries](#boundaries-state-these-when-you-report).

Overview

This skill computes reference-relative multivariate scores and exploratory ARIMA forecasts. It targets the equations and printed example of RELSA 0.0.1.9000 (commit e68e8451e8719ccc3600179f55900cd7254ede9d, current upstream at review), plus the published foRcast methodology. The Python forecast helper is an independent nonseasonal approximation, not an exact R port.

RELSA is the RMS of directional deviations divided by each variable's reference maximum deviation. Zero means no measured worsening in those directions; it does not establish normal welfare. One is a reference scale unit, not a universal endpoint or an upper bound. Different variables may attain their reference extrema in different animals or at different times, so the reference cohort need not contain a score of exactly one.

Forecasts estimate a score at a specified time, not time-to-endpoint or probability of death. Keep the approved study's observation schedule and humane endpoint criteria separate from these exploratory outputs. See Boundaries.

Raw lines 15 to 32
33## When to use this skill
35- Combining weight loss, temperature, clinical scoring, biomarkers, or telemetry into a single
36 per-animal severity score
37- Asking which animals in a cohort are at risk of reaching a humane endpoint, or predicting
38 the severity score at a coming time point
39- Comparing severity between treatment groups, interventions, or animal models on a common
40 relative scale
41- Defining thresholds or zones on a severity scale from the data
42- Writing the severity-assessment section of an animal welfare report, a 3Rs/refinement
43 analysis, or an application under EU Directive 2010/63/EU
45For general forecasting of a time series that is not a severity score, use
46**timesfm-forecasting** or **statsmodels**. For study design and sample size, use
47**experimental-design** and **statistical-power**.

When to use this skill

  • Combining weight loss, temperature, clinical scoring, biomarkers, or telemetry into a single per-animal severity score
  • Asking which animals in a cohort are at risk of reaching a humane endpoint, or predicting the severity score at a coming time point
  • Comparing severity between treatment groups, interventions, or animal models on a common relative scale
  • Defining thresholds or zones on a severity scale from the data
  • Writing the severity-assessment section of an animal welfare report, a 3Rs/refinement analysis, or an application under EU Directive 2010/63/EU

For general forecasting of a time series that is not a severity score, use timesfm-forecasting or statsmodels. For study design and sample size, use experimental-design and statistical-power.

Raw lines 33 to 48
49## Installation
51```bash
52uv venv --python 3.13 .venv-relsa
53uv pip install --python .venv-relsa/bin/python numpy==2.5.3 pandas==3.0.6 scipy==1.18.1 statsmodels==0.15.0 matplotlib==3.11.2
54source .venv-relsa/bin/activate
55```
57Run the examples from this skill directory. `relsa_score.py` needs numpy/pandas;
58`kde_thresholds.py` additionally needs scipy; statsmodels is required
59for forecasting and matplotlib only for figures.

Installation

uv venv --python 3.13 .venv-relsa
uv pip install --python .venv-relsa/bin/python numpy==2.5.3 pandas==3.0.6 scipy==1.18.1 statsmodels==0.15.0 matplotlib==3.11.2
source .venv-relsa/bin/activate

Run the examples from this skill directory. relsa_score.py needs numpy/pandas; kde_thresholds.py additionally needs scipy; statsmodels is required for forecasting and matplotlib only for figures.

Raw lines 49 to 60
Runs code bin/python :53
Runs code bin/activate :54
61## Data format
63One row per animal per time point, in a CSV:
65| id | treatment | condition | day | temp | weight | score | il6 |
66| --- | --- | --- | --- | --- | --- | --- | --- |
67| M01 | treated | endpoint | -1 | 37.15 | 25.17 | 0 | 35.1 |
68| M01 | treated | endpoint | 0 | 37.26 | 25.25 | 0 | 39.5 |
69| M01 | treated | endpoint | 1 | 35.83 | 23.12 | 4 | 162.0 |
71- `id` and a time column (`day`, `time`, `hour`, …) are required; `treatment` and `condition`
72 are optional labels used for grouping and for selecting the reference set.
73- Time must be finite, with one consistent unit and origin across cohorts. Forecast training and
74 target times must align with the chosen regular grid; disable interpolation only on regular observations. The RELSA
75 convention codes the baseline time point as `-1`.
76- **One row per animal per time point.** Average hourly telemetry to one value per interval
77 first (the published models average heart rate, HRV, and temperature, and sum activity).
78- Preserve lexical animal IDs (including leading zeros). Duplicate IDs/times, nonnumeric
79 measurements and infinities are rejected. Leave missing measurements empty. They are dropped from the score, never imputed — a
80 missing value treated as "no deviation" biases severity downward.
82`assets/example_cohort.csv` is a small synthetic cohort (6 mice, 9 days, temperature, body
83weight, an 0–8 clinical score, and an IL-6-like biomarker) used by every command below, so
84each one is runnable as written.

Data format

One row per animal per time point, in a CSV:

idtreatmentconditiondaytempweightscoreil6
M01treatedendpoint-137.1525.17035.1
M01treatedendpoint037.2625.25039.5
M01treatedendpoint135.8323.124162.0
  • id and a time column (day, time, hour, …) are required; treatment and condition are optional labels used for grouping and for selecting the reference set.
  • Time must be finite, with one consistent unit and origin across cohorts. Forecast training and target times must align with the chosen regular grid; disable interpolation only on regular observations. The RELSA convention codes the baseline time point as -1.
  • One row per animal per time point. Average hourly telemetry to one value per interval first (the published models average heart rate, HRV, and temperature, and sum activity).
  • Preserve lexical animal IDs (including leading zeros). Duplicate IDs/times, nonnumeric measurements and infinities are rejected. Leave missing measurements empty. They are dropped from the score, never imputed — a missing value treated as "no deviation" biases severity downward.

assets/example_cohort.csv is a small synthetic cohort (6 mice, 9 days, temperature, body weight, an 0–8 clinical score, and an IL-6-like biomarker) used by every command below, so each one is runnable as written.

Raw lines 61 to 85
86## The four decisions that determine the result
88Make these explicitly and write them into the methods. Nothing else about the procedure
89matters as much.
91**1. Directionality — which variables rise under worsening?** Falling is the default (body
92weight, activity, food intake, burrowing, wheel running). Variables that *rise* must be
93declared as `--turned`: clinical scores, inflammatory biomarkers, fever, tachycardia. Get
94this wrong and the variable contributes nothing at all, silently, because deviations in the
95"wrong" direction are floored at zero. Body temperature is model-dependent — it *falls* in
96sepsis and endotoxaemia, *rises* in fever models. Nothing in the data can settle this for you:
97in the published sepsis model activity legitimately swings further above baseline than below,
98so only a variable that *never once* moves the declared way is detectable, and
99`build_reference()` warns about exactly that case.
101**2. The reference set — relative to what?** RELSA scores mean nothing without it. Choose a scientifically characterized reference and document its burden; the 2026
102forecasting study uses the group assumed to carry the greatest burden in each model. A mild reference can push new scores
103above 1; too severe compresses everything toward 0. Save it with `--save-reference` and reuse
104it with `--load-reference` so later cohorts stay on the same scale. New CLI reference files
105also store and reuse normalization, ordinal mappings, baseline selection and rounding; conflicting
106options are rejected. Legacy references lack that contract and require the original options.
108**3. Scores with a zero baseline.** A clinical score of 0 in a healthy animal cannot be
109ratio-normalized — `0/0` is undefined. Use `--score-scale score=8` to map the score's scale
110instead (healthy → 100%, worst possible → 200%), which also marks it as turned. This assumes meaningful numeric spacing between ordinal categories. An affine rescaling
111applied identically to reference and target cancels in the weight ratio before rounding; the
112category encoding, healthy anchor and reference cohort still matter. State those choices. The alternative is to keep the score out of RELSA and use it as an
113independent endpoint criterion.
115**4. Which variables are measured throughout.** Because the score averages over whichever
116variables are available, a variable that appears or disappears mid-trajectory moves the score
117by itself. An intermittent measure joining only at the endpoint can lower or raise the composite
118without any change in the other observed measures.
119`relsa_scores()` warns when composition changes; score the variables present throughout.

The four decisions that determine the result

Make these explicitly and write them into the methods. Nothing else about the procedure matters as much.

1. Directionality — which variables rise under worsening? Falling is the default (body weight, activity, food intake, burrowing, wheel running). Variables that rise must be declared as --turned: clinical scores, inflammatory biomarkers, fever, tachycardia. Get this wrong and the variable contributes nothing at all, silently, because deviations in the "wrong" direction are floored at zero. Body temperature is model-dependent — it falls in sepsis and endotoxaemia, rises in fever models. Nothing in the data can settle this for you: in the published sepsis model activity legitimately swings further above baseline than below, so only a variable that never once moves the declared way is detectable, and build_reference() warns about exactly that case.

2. The reference set — relative to what? RELSA scores mean nothing without it. Choose a scientifically characterized reference and document its burden; the 2026 forecasting study uses the group assumed to carry the greatest burden in each model. A mild reference can push new scores above 1; too severe compresses everything toward 0. Save it with --save-reference and reuse it with --load-reference so later cohorts stay on the same scale. New CLI reference files also store and reuse normalization, ordinal mappings, baseline selection and rounding; conflicting options are rejected. Legacy references lack that contract and require the original options.

3. Scores with a zero baseline. A clinical score of 0 in a healthy animal cannot be ratio-normalized — 0/0 is undefined. Use --score-scale score=8 to map the score's scale instead (healthy → 100%, worst possible → 200%), which also marks it as turned. This assumes meaningful numeric spacing between ordinal categories. An affine rescaling applied identically to reference and target cancels in the weight ratio before rounding; the category encoding, healthy anchor and reference cohort still matter. State those choices. The alternative is to keep the score out of RELSA and use it as an independent endpoint criterion.

4. Which variables are measured throughout. Because the score averages over whichever variables are available, a variable that appears or disappears mid-trajectory moves the score by itself. An intermittent measure joining only at the endpoint can lower or raise the composite without any change in the other observed measures. relsa_scores() warns when composition changes; score the variables present throughout.

Raw lines 86 to 120
121## Workflow
123### Step 1 — compute RELSA scores
125```bash
126python scripts/relsa_score.py assets/example_cohort.csv \
127 --variables weight,temp,score,il6 \
128 --normalize weight,temp,il6 \
129 --turned il6 \
130 --score-scale score=8 \
131 --baseline-time -1 \
132 --reference-group condition=endpoint \
133 --save-reference reference.json \
134 --out relsa_scores.csv
135```
137The reference model is echoed so the scale is auditable:
139```
140reference model: assets/example_cohort.csv [condition=endpoint]
141 animals=2 rows=18 baseline_time=-1.0
142 variable turned max reached max delta
143 weight no 82.40 17.60
144 temp no 92.79 7.21
145 score yes 187.50 87.50
146 il6 yes 797.72 697.72
147```
149`relsa_scores.csv` holds each variable's weight alongside the score, which is what makes a
150score explainable — here M01 deteriorating to its endpoint, M03 peaking on day 3 and
151recovering:
153```
154 id time weight temp score il6 n_vars relsa
155M01 1 0.46 0.49 0.57 0.52 4 0.51
156M01 3 0.84 0.76 1.00 0.89 4 0.88
157M01 5 1.00 1.00 1.00 1.00 4 1.00
158M03 3 0.56 0.44 0.57 0.54 4 0.53
159M03 5 0.35 0.26 0.43 0.32 4 0.35
160M03 7 0.12 0.06 0.14 0.11 4 0.11
161```
163A weight of 1.00 means that variable hit the reference maximum; `n_vars` is how many
164variables entered the score at that time point.
166Same thing from Python, when you need the objects:
168```python
169import sys; sys.path.insert(0, "scripts")
170from _common import read_relsa_table, score_to_percent
171from relsa_score import prepare, build_reference, relsa_scores
173frame = read_relsa_table("assets/example_cohort.csv")
174frame["score"] = score_to_percent(frame["score"], max_score=8) # 0-8 clinical score
175VARS, TURNED = ["weight", "temp", "score", "il6"], ["score", "il6"]
177prepared = prepare(frame, normalize=["weight", "temp", "il6"], baseline_time=-1)
178reference = build_reference(prepared[prepared.condition == "endpoint"],
179 variables=VARS, turned=TURNED, baseline_time=-1,
180 label="endpoint-reaching animals")
181scores = relsa_scores(prepared, reference)
182```

Step 1 — compute RELSA scores

python scripts/relsa_score.py assets/example_cohort.csv \
    --variables weight,temp,score,il6 \
    --normalize weight,temp,il6 \
    --turned il6 \
    --score-scale score=8 \
    --baseline-time -1 \
    --reference-group condition=endpoint \
    --save-reference reference.json \
    --out relsa_scores.csv

The reference model is echoed so the scale is auditable:

reference model: assets/example_cohort.csv [condition=endpoint]
  animals=2  rows=18  baseline_time=-1.0
  variable      turned   max reached   max delta
  weight            no         82.40       17.60
  temp              no         92.79        7.21
  score            yes        187.50       87.50
  il6              yes        797.72      697.72

relsa_scores.csv holds each variable's weight alongside the score, which is what makes a score explainable — here M01 deteriorating to its endpoint, M03 peaking on day 3 and recovering:

 id  time  weight  temp  score  il6  n_vars  relsa
M01     1    0.46  0.49   0.57 0.52       4   0.51
M01     3    0.84  0.76   1.00 0.89       4   0.88
M01     5    1.00  1.00   1.00 1.00       4   1.00
M03     3    0.56  0.44   0.57 0.54       4   0.53
M03     5    0.35  0.26   0.43 0.32       4   0.35
M03     7    0.12  0.06   0.14 0.11       4   0.11

A weight of 1.00 means that variable hit the reference maximum; n_vars is how many variables entered the score at that time point.

Same thing from Python, when you need the objects:

import sys; sys.path.insert(0, "scripts")
from _common import read_relsa_table, score_to_percent
from relsa_score import prepare, build_reference, relsa_scores

frame = read_relsa_table("assets/example_cohort.csv")
frame["score"] = score_to_percent(frame["score"], max_score=8)   # 0-8 clinical score
VARS, TURNED = ["weight", "temp", "score", "il6"], ["score", "il6"]

prepared  = prepare(frame, normalize=["weight", "temp", "il6"], baseline_time=-1)
reference = build_reference(prepared[prepared.condition == "endpoint"],
                           variables=VARS, turned=TURNED, baseline_time=-1,
                           label="endpoint-reaching animals")
scores    = relsa_scores(prepared, reference)
Raw lines 123 to 183
Runs code python scripts/relsa_score.py :126
184### Step 2 — forecast a score at a known evaluation time
186For retrospective evaluation, use recorded endpoint times and train only on earlier
187observations. This synthetic example predicts scores at designated times; it does not
188validate humane endpoint detection:
190```bash
191python scripts/forecast_relsa.py relsa_scores.csv \
192 --animals M01,M02 --endpoints M01=5 --endpoints M02=6 \
193 --group-col condition --plot-dir figs \
194 --out forecasts.csv --summary-out forecast_metrics.csv
195```
197The CSV includes the selected order, point forecast and bounds. Numerical values can change
198with the fitted model and library release. Report RMSE, PICP and MPIW together, including
199counts and forecast failures. High coverage with wide intervals can be uninformative.
201For prospective evaluation, freeze the RELSA reference set and any KDE thresholds using a separate development cohort before forecasting held-out animals. Do not estimate normalization maxima or thresholds from their future endpoint observations. Label analyses that reuse endpoint data to define the scale as retrospective; use an animal-level split so repeated observations from one animal do not cross evaluation partitions.
203For rolling retrospective evaluation, forecast each next observed time using only its history:
205```bash
206python scripts/forecast_relsa.py relsa_scores.csv --mode rolling --animals M03
207```
209Interpolation defaults to 0.1 **input time units** for published-method exploration. It creates
210no independent information, changes autocorrelation and can narrow intervals without valid
211calibration. Use `--interpolate-step 0` for regular observed data and compare interpolation
212sensitivity on held-out animals. Off-grid targets and irregular uninterpolated observations
213are rejected rather than silently relabeled. Unconverged fits are not selected. Bounds are
214Gaussian model intervals clipped at zero, conditional on fitted parameters and the frozen
215scale; they exclude reference, preprocessing and model-selection uncertainty.
217ARIMA represents linear dependence after differencing; it does not anticipate abrupt
218unobserved deterioration. Inspect both interval width and upper bound alongside observed
219welfare signs and approved criteria. No bound is an automatic intervention rule.

Step 2 — forecast a score at a known evaluation time

For retrospective evaluation, use recorded endpoint times and train only on earlier observations. This synthetic example predicts scores at designated times; it does not validate humane endpoint detection:

python scripts/forecast_relsa.py relsa_scores.csv \
    --animals M01,M02 --endpoints M01=5 --endpoints M02=6 \
    --group-col condition --plot-dir figs \
    --out forecasts.csv --summary-out forecast_metrics.csv

The CSV includes the selected order, point forecast and bounds. Numerical values can change with the fitted model and library release. Report RMSE, PICP and MPIW together, including counts and forecast failures. High coverage with wide intervals can be uninformative.

For prospective evaluation, freeze the RELSA reference set and any KDE thresholds using a separate development cohort before forecasting held-out animals. Do not estimate normalization maxima or thresholds from their future endpoint observations. Label analyses that reuse endpoint data to define the scale as retrospective; use an animal-level split so repeated observations from one animal do not cross evaluation partitions.

For rolling retrospective evaluation, forecast each next observed time using only its history:

python scripts/forecast_relsa.py relsa_scores.csv --mode rolling --animals M03

Interpolation defaults to 0.1 input time units for published-method exploration. It creates no independent information, changes autocorrelation and can narrow intervals without valid calibration. Use --interpolate-step 0 for regular observed data and compare interpolation sensitivity on held-out animals. Off-grid targets and irregular uninterpolated observations are rejected rather than silently relabeled. Unconverged fits are not selected. Bounds are Gaussian model intervals clipped at zero, conditional on fitted parameters and the frozen scale; they exclude reference, preprocessing and model-selection uncertainty.

ARIMA represents linear dependence after differencing; it does not anticipate abrupt unobserved deterioration. Inspect both interval width and upper bound alongside observed welfare signs and approved criteria. No bound is an automatic intervention rule.

Raw lines 184 to 220
Runs code python scripts/forecast_relsa.py :191
221### Step 3 — put the score in context with severity zones
223```bash
224python scripts/kde_thresholds.py relsa_scores.csv \
225 --group treatment=treated --n-thresholds 2 --plot zones.png --json zones.json
226```
228```
229KDE on 33 RELSA scores (bandwidth = 0.1502)
230 candidate thresholds (density minima): 0.703
231 density modes: 0.264, 0.866
232 normal [0.000, 0.703) n=25 (75.8%)
233 danger >= 0.703 n=8 (24.2%)
234```
236The example deliberately filters to treated animals; choose the target population explicitly.
237Thresholds are the *minima* of the score density — the sparse valleys between clusters of
238scores. A development population may include endpoint animals, survivors and shams, but sampling
239frequency, follow-up duration and their proportions change the density. Freeze that choice
240before evaluation; do not pool incompatible reference frames.
242**Check bandwidth and sampling sensitivity.** See `references/thresholds-and-zones.md`.
243An empty threshold list is legitimate. Minima are properties of the sampled score density;
244zone names do not establish welfare states. The thin-zone filter is this implementation's
245heuristic, not a published validated threshold rule.

Step 3 — put the score in context with severity zones

python scripts/kde_thresholds.py relsa_scores.csv \
    --group treatment=treated --n-thresholds 2 --plot zones.png --json zones.json
KDE on 33 RELSA scores  (bandwidth = 0.1502)
  candidate thresholds (density minima): 0.703
  density modes: 0.264, 0.866
  normal    [0.000, 0.703)  n=25 (75.8%)
  danger    >= 0.703  n=8 (24.2%)

The example deliberately filters to treated animals; choose the target population explicitly. Thresholds are the minima of the score density — the sparse valleys between clusters of scores. A development population may include endpoint animals, survivors and shams, but sampling frequency, follow-up duration and their proportions change the density. Freeze that choice before evaluation; do not pool incompatible reference frames.

Check bandwidth and sampling sensitivity. See references/thresholds-and-zones.md. An empty threshold list is legitimate. Minima are properties of the sampled score density; zone names do not establish welfare states. The thin-zone filter is this implementation's heuristic, not a published validated threshold rule.

Raw lines 221 to 246
Runs code python scripts/kde_thresholds.py :224
247## Boundaries: state these when you report
249- RELSA and forecasts support assessment; observed distress and approved humane endpoint
250 criteria take precedence. No low score authorizes delaying care or extending a procedure.
251- KDE zones are not EU severity categories. The [Commission severity framework](https://environment.ec.europa.eu/topics/chemicals/animals-science_en)
252 distinguishes prospective classification, monitoring and actual experienced severity;
253 there is no official RELSA-to-category conversion.
254- Comparisons require the same frozen reference, variable panel, encoding, baseline and
255 measurement methods. The original paper explored cross-model comparisons in a common
256 frame; independently scaled models cannot be ranked by their raw RELSA numbers.
257- The 2026 paper evaluates 13 endpoint-time forecasts. Its reported 96% PICP is consistent
258 with averaging seven model rows (six 100%, one 75%), not pooled animal coverage:
259 12/13 is 92.3%. Its 1.69 MPIW is likewise a model-row mean; this helper pools predictions.
260- False negatives and false positives can both matter. Predefine monitoring responses with
261 the study's responsible personnel; do not turn a candidate KDE minimum into an endpoint.

Boundaries: state these when you report

  • RELSA and forecasts support assessment; observed distress and approved humane endpoint criteria take precedence. No low score authorizes delaying care or extending a procedure.
  • KDE zones are not EU severity categories. The Commission severity framework distinguishes prospective classification, monitoring and actual experienced severity; there is no official RELSA-to-category conversion.
  • Comparisons require the same frozen reference, variable panel, encoding, baseline and measurement methods. The original paper explored cross-model comparisons in a common frame; independently scaled models cannot be ranked by their raw RELSA numbers.
  • The 2026 paper evaluates 13 endpoint-time forecasts. Its reported 96% PICP is consistent with averaging seven model rows (six 100%, one 75%), not pooled animal coverage: 12/13 is 92.3%. Its 1.69 MPIW is likewise a model-row mean; this helper pools predictions.
  • False negatives and false positives can both matter. Predefine monitoring responses with the study's responsible personnel; do not turn a candidate KDE minimum into an endpoint.
Raw lines 247 to 262
263## Reporting checklist
265A severity analysis is reproducible only if all of this is stated:
2671. Outcome measures, their units, and their **directionality** (which were turned, and why).
2682. The **baseline** time point or window, and which variables were normalized.
2693. Any **score mapping** applied to ordinal variables, with its scale.
2704. The **reference set**: which animals, which group, how many, and why they are assumed to
271 carry the greatest burden.
2725. Humane endpoint criteria actually applied in the study, separately from the RELSA score.
2736. For forecasts: interpolation step, the selected ARIMA order per animal, and RMSE, PICP,
274 *and* MPIW.
2757. For thresholds: the bandwidth, the number of scores, and a bandwidth sensitivity sweep.
2768. Software versions, and the statement that thresholds are model-specific and not regulatory
277 gradings.

Reporting checklist

A severity analysis is reproducible only if all of this is stated:

  1. Outcome measures, their units, and their directionality (which were turned, and why).
  2. The baseline time point or window, and which variables were normalized.
  3. Any score mapping applied to ordinal variables, with its scale.
  4. The reference set: which animals, which group, how many, and why they are assumed to carry the greatest burden.
  5. Humane endpoint criteria actually applied in the study, separately from the RELSA score.
  6. For forecasts: interpolation step, the selected ARIMA order per animal, and RMSE, PICP, and MPIW.
  7. For thresholds: the bandwidth, the number of scores, and a bandwidth sensitivity sweep.
  8. Software versions, and the statement that thresholds are model-specific and not regulatory gradings.
Raw lines 263 to 278
279## Common pitfalls
2811. **Wrong directionality** — a rising variable not listed in `--turned` contributes exactly
282 zero, silently, and no warning is possible unless it never once falls. Check the reference
283 model table yourself: `max reached` should be below 100 for a falling variable and above 100
284 for a turned one, and `max delta` should be a plausible size for that measure.
2852. **Confusing percent encodings** — 90% of baseline, -10% change and 10% loss differ.
286 RELSA needs baseline 100: convert change with `100 + change`, loss with `100 - loss`.
287 Re-normalizing baseline-100 values is redundant; using a zero-centered change as baseline is invalid.
2883. **A zero baseline** — a clinical score of 0 makes the ratio undefined; the variable becomes
289 all-NaN with a warning. Use `--score-scale`.
2904. **A reference set that does not express the burden** — a variable that never deviates in it
291 raises an error rather than dividing by zero, and one that barely deviates inflates every
292 score. Reference extrema are sensitive to outliers and measurement errors.
2935. **Changing variable composition along a trajectory** — see decision 4 above.
2946. **Reading MPIW as a good thing** — a wide interval raises PICP while destroying the
295 forecast's usefulness.
2967. **Reporting a KDE threshold without its bandwidth** — thresholds can appear or vanish as
297 bandwidth changes.
2988. **Treating the forecast as permission to wait** — the model cannot see abrupt
299 deterioration, and the humane endpoint criteria of the protocol always take precedence.
3009. **Comparing RELSA scores between models** — requires one reference frame and harmonized measurements.

Common pitfalls

  1. Wrong directionality — a rising variable not listed in --turned contributes exactly zero, silently, and no warning is possible unless it never once falls. Check the reference model table yourself: max reached should be below 100 for a falling variable and above 100 for a turned one, and max delta should be a plausible size for that measure.
  2. Confusing percent encodings — 90% of baseline, -10% change and 10% loss differ. RELSA needs baseline 100: convert change with 100 + change, loss with 100 - loss. Re-normalizing baseline-100 values is redundant; using a zero-centered change as baseline is invalid.
  3. A zero baseline — a clinical score of 0 makes the ratio undefined; the variable becomes all-NaN with a warning. Use --score-scale.
  4. A reference set that does not express the burden — a variable that never deviates in it raises an error rather than dividing by zero, and one that barely deviates inflates every score. Reference extrema are sensitive to outliers and measurement errors.
  5. Changing variable composition along a trajectory — see decision 4 above.
  6. Reading MPIW as a good thing — a wide interval raises PICP while destroying the forecast's usefulness.
  7. Reporting a KDE threshold without its bandwidth — thresholds can appear or vanish as bandwidth changes.
  8. Treating the forecast as permission to wait — the model cannot see abrupt deterioration, and the humane endpoint criteria of the protocol always take precedence.
  9. Comparing RELSA scores between models — requires one reference frame and harmonized measurements.
Raw lines 279 to 301
302## Resources
304### Scripts
306- `scripts/relsa_score.py` — the RELSA procedure: `prepare()`, `build_reference()`,
307 `relsa_scores()`, `relsa_weights()`, and a `ReferenceModel` that serialises to JSON.
308 Matches the R package's printed worked example to two decimals; native R was not run.
309- `scripts/forecast_relsa.py` — the independent foRcast-style helper: `auto_arima()` (Hyndman–Khandakar stepwise
310 AICc selection), `forecast_animal()`, `predict_endpoint()`, `rolling_forecast()`,
311 `forecast_indirect()`, `summarize()`, and trajectory plots.
312- `scripts/kde_thresholds.py` — severity zones: `bw_nrd0()` (R's bandwidth), `density_curve()`,
313 `find_thresholds()`, zone assignment, and density plots.
314- `scripts/_common.py` — RELSA-format I/O, validation, `score_to_percent()`,
315 `percent_of_baseline()`, and `forecast_metrics()` (RMSE/PICP/MPIW).

Scripts

  • scripts/relsa_score.py — the RELSA procedure: prepare(), build_reference(), relsa_scores(), relsa_weights(), and a ReferenceModel that serialises to JSON. Matches the R package's printed worked example to two decimals; native R was not run.
  • scripts/forecast_relsa.py — the independent foRcast-style helper: auto_arima() (Hyndman–Khandakar stepwise AICc selection), forecast_animal(), predict_endpoint(), rolling_forecast(), forecast_indirect(), summarize(), and trajectory plots.
  • scripts/kde_thresholds.py — severity zones: bw_nrd0() (R's bandwidth), density_curve(), find_thresholds(), zone assignment, and density plots.
  • scripts/_common.py — RELSA-format I/O, validation, score_to_percent(), percent_of_baseline(), and forecast_metrics() (RMSE/PICP/MPIW).
Raw lines 304 to 316
Reads a file scripts/relsa_score.py :306
Reads a file scripts/forecast_relsa.py :309
Reads a file scripts/kde_thresholds.py :312
Reads a file scripts/_common.py :314
317### References
319- `references/relsa-method.md` — the four steps in full, the score/zero-baseline problem, the
320 variable-composition trap, parity notes against the R package, and current upstream API/source limitations.
321- `references/forecasting.md` — ARIMA selection, why interpolation is a distortion, direct vs
322 indirect prediction, the metrics, the published Table 1, and the limits of this implementation.
323- `references/thresholds-and-zones.md` — KDE method, published thresholds, the bandwidth
324 sensitivity sweep, the regulatory boundary, and alternatives when KDE gives nothing.

References

  • references/relsa-method.md — the four steps in full, the score/zero-baseline problem, the variable-composition trap, parity notes against the R package, and current upstream API/source limitations.
  • references/forecasting.md — ARIMA selection, why interpolation is a distortion, direct vs indirect prediction, the metrics, the published Table 1, and the limits of this implementation.
  • references/thresholds-and-zones.md — KDE method, published thresholds, the bandwidth sensitivity sweep, the regulatory boundary, and alternatives when KDE gives nothing.
Raw lines 317 to 325
326### Assets
328- `assets/example_cohort.csv` — synthetic 6-mouse cohort with temperature, body weight, a
329 clinical score, and a biomarker; illustrative only, not real data.

Assets

  • assets/example_cohort.csv — synthetic 6-mouse cohort with temperature, body weight, a clinical score, and a biomarker; illustrative only, not real data.
Raw lines 326 to 330
331### Related skills
333- **experimental-design**, **statistical-power** — designing the study and sizing the groups.
334- **statsmodels**, **timesfm-forecasting** — general time-series modelling.
335- **statistical-analysis**, **scientific-visualization** — group comparisons and figures.

Related skills

  • experimental-design, statistical-power — designing the study and sizing the groups.
  • statsmodels, timesfm-forecasting — general time-series modelling.
  • statistical-analysis, scientific-visualization — group comparisons and figures.
Raw lines 331 to 336
337### Key references
339- Talbot, S. R. et al. (2022). RELSA — a multidimensional procedure for the comparative
340 assessment of well-being and the quantitative determination of severity in experimental
341 procedures. *Front. Vet. Sci.* 9:937711. R package: <https://github.com/mytalbot/RELSA>
342- Lutscher, S. et al. (2026). Refining humane endpoint detection by time-series forecasting
343 and threshold definition using a multivariate severity score. *Front. Physiol.* 17:1869563.
344- Hyndman, R. J. & Khandakar, Y. (2008). Automatic time series forecasting: the forecast
345 package for R. *J. Stat. Softw.* 27, 1–22.
346- EU Commission (2010). Directive 2010/63/EU on the protection of animals used for scientific
347 purposes.

Key references

  • Talbot, S. R. et al. (2022). RELSA — a multidimensional procedure for the comparative assessment of well-being and the quantitative determination of severity in experimental procedures. Front. Vet. Sci. 9:937711. R package: <https://github.com/mytalbot/RELSA>
  • Lutscher, S. et al. (2026). Refining humane endpoint detection by time-series forecasting and threshold definition using a multivariate severity score. Front. Physiol. 17:1869563.
  • Hyndman, R. J. & Khandakar, Y. (2008). Automatic time series forecasting: the forecast package for R. J. Stat. Softw. 27, 1–22.
  • EU Commission (2010). Directive 2010/63/EU on the protection of animals used for scientific purposes.
Raw lines 337 to 348
Boilerplate, lines 349 to 364. The same text appears in 125 other files here. Close
349## Citing Scientific Agent Skills
351This skill is part of Scientific Agent Skills by K-Dense. If it materially contributed to a
352manuscript, report, presentation, or code release, add the paper to the references or
353software section and tell the user you did so:
355> Kassis, T., Agarwal, V., He, Y., Patel, D., & Brueckner, A. M. (2026). Scientific Agent
356> Skills: A Library of Procedural Knowledge for Research Agents. arXiv:2609.00065.
357> https://doi.org/10.48550/arXiv.2609.00065
359Always cite the current version. The DOI and https://arxiv.org/abs/2609.00065 resolve to the
360latest arXiv version, so never append a version suffix such as `v1`. When network access is
361available, fetch https://arxiv.org/abs/2609.00065 (or
362http://export.arxiv.org/api/query?id_list=2609.00065) before writing the reference and take
363the author list, year, and version from that record. If the record lists a journal reference
364or publisher DOI, cite the published version instead.

From K-Dense-AI/scientific-agent-skills at 5f2bc03, 2026-10-26. Not read by a model yet. Summary and techniques by Claude Opus 5.5 on 2026-11-06.