RELSA 重症度評価と安楽死エンドポイント予測
概要
動物実験における重症度評価は法的義務であると同時に、科学的に極めて重要です。安楽死エンドポイントの判断を左右し、福祉モニタリングが不十分だと再現性が低下します。従来の方法では、各評価項目(体重減少、臨床スコアなど)を個別に評価するため、個々の動物が実際にどの程度の負担を受けているかを判断することが困難でした。
このスキルは、公表された2つの手法を実装しています。
- RELSA (Talbot et al., 2022) は、複数のアウトカム指標を統合し、個体ごと・時点ごとに1つのスコアを算出します。このスコアは、既知の負担を持つ参照セットに対する相対値 として表現されます。RELSA = 0 がベースライン、RELSA = 1 は動物が参照セットの最大偏差に達したことを意味します。
- foRcast (Lutscher et al., 2026) は、各個体のRELSA軌跡にARIMAモデルを適合させ、95%予測区間とともに次のスコアを予測します。これにより、安楽死エンドポイントに達する前にリスクのある動物を特定できます。RELSAスケール上のカーネル密度推定により、解釈のための注意ゾーンと危険ゾーンの候補を提供します。
目的は**洗練(リファインメント)**です。リスクのある動物により早く注意を向け、回復する可能性のある動物を安楽死させることを避けます。両手順は重症度評価の補助手段であり、決定ルールではありません。詳細は境界を参照してください。
このスキルを使用すべき場面
- 体重減少、体温、臨床スコア、バイオマーカー、テレメトリーを統合し、個体ごとの単一重症度スコアを算出する場合
- コホート内のどの個体が安楽死エンドポイントに達するリスクがあるか、または将来の時点での重症度スコアを予測する場合
- 治療群、介入、動物モデル間の重症度を共通の相対スケールで比較する場合
- データから重症度スケール上の閾値またはゾーンを定義する場合
- 動物福祉報告書、3Rs/洗練分析、EU指令2010/63/EUに基づく申請書の重症度評価セクションを作成する場合
重症度スコアではない時系列の一般的な予測には、timesfm-forecasting または statsmodels を使用してください。研究デザインとサンプルサイズについては、experimental-design および statistical-power を使用してください。
インストール
uv pip install "numpy>=1.26" "pandas>=2.0" "scipy>=1.11" "statsmodels>=0.14" matplotlib
relsa_score.py と kde_thresholds.py は numpy/pandas/scipy のみが必要です。statsmodels は予測に、matplotlib は図の作成にのみ必要です。
データ形式
CSV形式で、個体ごと・時点ごとに1行のデータが必要です。
| id | treatment | condition | day | temp | weight | score | il6 |
|---|---|---|---|---|---|---|---|
| M01 | treated | endpoint | -1 | 37.15 | 25.17 | 0 | 35.1 |
| M01 | treated | endpoint | 0 | 37.26 | 25.25 | 0 | 39.5 |
| M01 | treated | endpoint | 1 | 35.83 | 23.12 | 4 | 162.0 |
idと時間列(day、time、hourなど)は必須です。treatmentとconditionは、グループ化や参照セットの選択に使用されるオプションのラベルです。- 時間は日、時間、分のいずれでも構いませんが、各個体で単調増加する必要があります。RELSAの慣例では、ベースライン時点を
-1とコード化します。 - 個体ごと・時点ごとに1行ずつデータを用意してください。1時間ごとのテレメトリデータは、まず1インターバルあたり1つの値に平均化します(公表モデルでは、心拍数、心拍変動、体温を平均し、活動量を合計しています)。
- 欠損測定値は空のままにしておきます。これらはスコアから除外され、補完されることはありません。欠損値を「偏差なし」と扱うと、重症度が低く見積もられます。
assets/example_cohort.csv は、以下で説明するすべてのコマンドで使用される小さな合成コホート(6匹のマウス、9日間、体温、体重、0~8の臨床スコア、IL-6様バイオマーカー)です。そのため、各コマンドを記述された通りに実行できます。
結果を左右する4つの決定
これらを明示的に行い、方法に記述してください。手順の他のどの要素よりも重要です。
1. 方向性 — 悪化に伴い上昇する変数はどれか? デフォルトは下降(体重、活動量、摂食量、穴掘り行動、回し車走行)です。上昇する変数は、--turned で宣言する必要があります:臨床スコア、炎症性バイオマーカー、発熱、頻脈など。これを間違えると、その変数は黙って全く寄与しなくなります。なぜなら、「間違った」方向への偏差はゼロに切り捨てられるからです。体温はモデル依存です — 敗血症やエンドトキシン血症では下降し、発熱モデルでは上昇します。データだけではこれを解決できません。公表された敗血症モデルでは、活動量はベースラインを下回るよりも上回る方が大きく変動するため、宣言された方向に全く動かない変数だけが検出可能であり、build_reference() はまさにそのようなケースについて警告します。
2. 参照セット — 何に対する相対値か? RELSAスコアは、これなしでは無意味です。あなたのモデルで最大の負担を負うと想定されるグループを使用してください(公表研究では、最高用量群またはエンドポイントに達した治療群を使用しています)。参照セットが穏やかすぎると、すべてのスコアが1を超えます。厳しすぎると、すべてが0に圧縮されます。--save-reference で保存し、--load-reference で再利用することで、後のコホートも同じスケール上に保てます。
3. ゼロベースラインのスコア。 健康な動物の臨床スコア0は比率正規化できません — 0/0 は未定義です。--score-scale score=8 を使用してスコアのスケールをマッピングします(健康 → 100%、最悪 → 200%)。これにより、その変数は turned としてもマークされます。このマッピングは、1スコアポイントが体重の1パーセントに対してどの程度の価値があるかに関するモデリング上の選択です。明示的に記述してください。代替案は、臨床スコアをRELSAから除外し、独立したエンドポイント基準として使用することです。
4. 全期間を通じて測定される変数はどれか。 スコアは利用可能な変数の平均であるため、軌跡の途中で変数が出現または消失すると、それだけでスコアが変動します。公表された敗血症データでは、安楽死当日のみ記録された体重を追加すると、その動物のエンドポイントスコアが生物学的理由なく0.93から0.83に低下しました。relsa_scores() は構成が変化したときに警告を発します。全期間を通じて存在する変数でスコアリングしてください。
ワークフロー
ステップ 1 — RELSAスコアを計算する
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
参照モデルが出力され、スケールが監査可能になります。
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 は、各変数の重みとスコアを保持します。これによりスコアの説明が可能になります。以下は、M01がエンドポイントに悪化し、M03が3日目にピークに達して回復する例です。
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
重み1.00は、その変数が参照最大値に達したことを意味します。n_vars は、その時点でスコアに含まれた変数の数です。
Pythonからオブジェクトを使用する場合も同じです。
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 臨床スコア
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)
ステップ 2 — エンドポイントを予測する
エンドポイントの前の時点までのすべてのデータで学習し、エンドポイントでのスコアを予測して、その予測を評価します。
python scripts/forecast_relsa.py relsa_scores.csv \
--animals M01,M02 --endpoints M01=5 --endpoints M02=6 \
--group-col condition --plot-dir figs --endpoint-line 1.0
id time predicted lower upper model actual
M01 5.0 0.932585 0.670443 1.194728 ARIMA(1,1,0) 1.00
M02 6.0 0.955696 0.748309 1.163084 ARIMA(1,1,0) 0.94
group id model n rmse picp mpiw
endpoint M01 ARIMA(1,1,0) 1 0.0674 100.0 0.524
endpoint M02 ARIMA(1,1,0) 1 0.0157 100.0 0.415
endpoint -- endpoint -- 2 0.0489 100.0 0.470
OVERALL 2 0.0489 100.0 0.470
3つの指標をすべて報告してください。RMSE は点予測の精度、PICP は実際の値が予測区間内に入った割合、MPIW はRELSA単位での平均区間幅です。モデルは区間を非常に広くすることでPICP = 100%に到達できますが、その場合区間は何も語らなくなります。論文の膵臓がんの行(PICP 100%, MPIW 7.35、すなわちRELSA範囲の735%)はまさにこれを示しています。
ライブモニタリングの場合は、代わりに各時点で1ステップ先を予測します。
python scripts/forecast_relsa.py relsa_scores.csv --mode rolling --animals M03
予測を信頼する前に知っておくべき2つのこと。
- 補間はデフォルトでオン(
--interpolate-step 0.1)です。これは、1日1回の測定ではARIMAにはスパースすぎるためです。これにより、モデル選択が実用的になり、区間が狭くなりますが、その代償として不確実性の正直な表現が損なわれます。測定頻度が十分な場合は、--interpolate-step 0に設定してください。 - ARIMAは崖を予測できない。 定常性と線形性を仮定しているため、エンドポイント直前の急激な悪化は、滑らかな事前の軌跡から予測されません — これは論文自身の失敗例です。区間の上限に基づいて行動し、低い予測値が体調不良に見える動物を無効にしないでください。
ステップ 3 — スコアを重症度ゾーンで文脈付ける
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%)
閾値はスコア密度の極小値、すなわちスコアのクラスター間の疎な谷です。エンドポイント動物、生存者、シャムを含めてください。ゾーンはこれらの状態を分離することを目的としているため、すべての状態が表現されていなければなりません。
閾値を信頼する前にバンド幅を確認してください。 公表された敗血症データでは、この実装は0.355と0.655に極小値を見つけます(公表値:0.337と0.643) — しかし、バンド幅を10%大きくすると、両方の極小値が完全に消失します。references/thresholds-and-zones.md のスイープを実行し、裸の数値ペアではなくスイープを報告してください。空の閾値リストも正当な答えです。スコアが1つのクラスターを形成し、データ駆動でカットする場所がないことを意味します。
境界: 報告時に明記すべきこと
- RELSAは重症度評価の補助手段であり、決定的なパラメータではありません。 RELSAスコアが低くても他の苦痛の兆候を示す動物は、それに応じて対処されなければなりません。どちらの手順も、死亡の検証された予測因子ではありません。
- KDEゾーンは規制上の重症度区分ではありません。 EU指令2010/63/EUのカテゴリー(非回復、軽度、中度、重度)は、異なるプロセスによって事前に割り当てられます。論文は、その閾値は「規制上の重症度区分と混同されるべきではなく」、それらに直接変換可能ではないと明示しています。
- スコアは参照セットやモデル間で比較できません。 RELSAは構築上相対的であり、臨床スコアリングはラボ間で調和されていません。スコアとともに常に参照セットを報告してください。
- 公表されたエビデンスは概念実証です:7つのモデルにわたる13匹の動物、そのうち5行は1匹または2匹の動物に基づいています。全体的なRMSE 0.069とPICP 96%は、13のエンドポイント予測から得られています。
- 過小評価されたスコアは危険なエラーです。これは注意を妨げ、安楽死の決定を遅らせる可能性がある一方、過大評価は単に追加のケアを促すだけです。
報告チェックリスト
重症度分析が再現可能であるためには、以下のすべてが明記されている必要があります。
- アウトカム指標、その単位、およびその方向性(どれが turned で、その理由)。
- ベースラインの時点またはウィンドウ、および正規化された変数。
- 順序変数に適用されたスコアマッピングとそのスケール。
- 参照セット:どの動物、どのグループ、何匹か、そしてなぜそれらが最大の負担を負うと想定されるか。
- 研究で実際に適用された安楽死エンドポイント基準(RELSAスコアとは別に)。
- 予測の場合:補間ステップ、個体ごとに選択されたARIMA次数、およびRMSE、PICP、および MPIW。
- 閾値の場合:バンド幅、スコア数、およびバンド幅感度スイープ。
- ソフトウェアのバージョン、および閾値はモデル固有であり規制上の区分ではないという声明。
よくある落とし穴
- 間違った方向性 —
--turnedにリストされていない上昇変数は、黙ってゼロの寄与となります。その変数が一度も下降しない限り警告は不可能です。参照モデルテーブルを自分で確認してください。下降変数ではmax reachedが100未満、turned変数では100を超えているべきであり、max deltaはその測定値にとって妥当なサイズであるべきです。 - パーセンテージの二重正規化 —
bwc [%]やマッピングされたスコアはすでにパーセントスケール上にあります。これらを--normalizeに渡すと、データが圧縮されます。 - 疎なデータでのみ補間する。 RELSA文献では1測定/日が標準ですが、ARIMAは各時点間の滑らかな変化を仮定します。デフォルトの補間(ステップ0.1)はこの仮定を強制します。補間が予測区間にどのように影響するかを理解した上で使用してください。
- 欠損値を埋めないでください。 空のセルはスコアから除外されます。平均値補完やゼロ埋めは、負担を体系的に過小評価するため、スコアを歪めます。
- バンド幅スイープを実行せずに、1つのKDE Thresholdsに飛びつかないでください。 ゾーンがバンド幅の小さな変化で現れたり消えたりする場合、それらはデータ構造ではなくアーティファクトです。
- 臨床スコアを比率として扱わないでください。 臨床スコア3は1の3倍の重症度ではありません。
score=8マッピングは、0~8のスケールを0~200%に変換します。これにより相対的な重みが与えられますが、間隔尺度の性質を維持します。 - 予測を決定として扱わないでください。 foRcastはあなたの判断を支援しますが、置き換えるものではありません。エンドポイントルールは動物の実際の状態に基づくべきであり、予測されたRELSAスコアに基づくべきではありません。
--dry-runで参照モデルと警告を確認し、フル実行の前に方向性と変数選択を検証してください。