0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ニボルマブの投与間隔を2倍にすると、濃度の山と谷はどう変わる?

0
Posted at

ニボルマブの公表レジメンには、240 mgを2週ごと、480 mgを4週ごとに投与する方式があります[1]。

1日あたりの投与量は、どちらも約17.1 mg。同じ濃度曲線になるのでしょうか。公開PopPKモデルをPythonで計算してみます。

図で見る

ニボルマブの2週投与と4週投与の濃度曲線

青は240 mgを2週ごと、破線は480 mgを4週ごとです。4週投与では、一度に入る量が多いため山が高くなり、次の投与まで長く待つため谷が低くなります。

数字で比べる

以下は今回の計算値です。単位はmg/Lで、µg/mLと同じ数値になります。

投与方式 28日間の平均濃度 同期間の最高濃度 最終日の投与直前濃度
240 mg・2週ごと 101.9 147.2 81.4
480 mg・4週ごと 101.9 199.0 67.2

平均濃度はほぼ同じですが、最高濃度は約35%高く、最終日の投与直前濃度は約17%低くなりました。

線形のPKで、薬の消失速度がほぼ落ち着いた時期では、1日あたりの投与量が同じなら平均濃度も近くなります。それでも、平均が同じことと、山や谷が同じことは別です。

何を計算したか

BajajらのPopPKモデル[2]の基準条件(80 kg、女性、eGFR 90、PS 0、白人)を使いました。個人間のばらつきと測定誤差は加えていません。集団の平均曲線でも、実測患者の結果でもなく、基準条件での一本の予測曲線です。

投与は日0から始め、各回30分の点滴としました。比較期間は日364〜392の28日間です。時刻に応じて薬の消失速度が変わる原モデルの式を残し、初回投与時の消失速度へ固定していません。厳密な定常状態と同一とは扱いません。

元の研究では集団を使った曝露比較も行われています[1]。今回の小さな計算は、そこで報告された集団の数値を再現する解析ではありません。

再計算用コード

Python 3.12、NumPy 1.26.4、SciPy 1.14.0、Matplotlib 3.9.1で実行しました。コードをsimulate.pyとして保存し、python simulate.pyで図と数値を生成できます。実装を見たい場合だけ、以下を開いてください。

Pythonコードを表示
"""Typical-reference simulation of the Bajaj 2017 nivolumab PopPK model."""
from pathlib import Path
import json
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt

ROOT = Path(__file__).resolve().parent
START, END = 364.0, 392.0
V1, V2, Q = 3.63, 2.78, 32.1 * 24 / 1000


def clearance(day):
    hour = day * 24
    return 9.4 * 24 / 1000 * np.exp(-0.295 * hour**3.15 / (1410**3.15 + hour**3.15))


def simulate(dose, interval, duration=0.5 / 24, rtol=1e-9):
    state = np.zeros(4)  # central, peripheral, cumulative AUC, eliminated amount
    times, states = [0.0], [state.copy()]
    for day in np.arange(0.0, END, interval):
        for left, right, rate in [(day, day + duration, dose / duration),
                                  (day + duration, min(day + interval, END), 0.0)]:
            def rhs(t, y):
                c1, c2 = y[0] / V1, y[1] / V2
                cl = clearance(t)
                return [rate - cl*c1 - Q*(c1-c2), Q*(c1-c2), c1, cl*c1]
            solution = solve_ivp(rhs, (left, right), state, rtol=rtol, atol=1e-10,
                                 t_eval=np.linspace(left, right, 121), max_step=1)
            if not solution.success:
                raise RuntimeError(solution.message)
            state = solution.y[:, -1]
            times.extend(solution.t[1:])
            states.extend(solution.y.T[1:])
    t, y = np.array(times), np.array(states)
    window = t >= START
    auc_start = np.interp(START, t, y[:, 2])
    summary = dict(dose_mg=dose, interval_day=interval,
                   cavg=float((y[-1, 2]-auc_start)/(END-START)),
                   cmax=float(np.max(y[window, 0]/V1)),
                   predose=float(y[-1, 0]/V1))
    return t, y, summary


def main():
    results, curves = [], []
    for dose, interval in [(240, 14), (480, 28)]:
        t, y, result = simulate(dose, interval)
        results.append(result)
        curves.append((t, y))
    ratio = {key: results[1][key]/results[0][key] for key in ['cavg', 'cmax', 'predose']}
    (ROOT/'results').mkdir(exist_ok=True)
    (ROOT/'figures').mkdir(exist_ok=True)
    report = dict(window_day=[START, END], typical_reference=True,
                  interindividual_and_residual_variation=False, results=results, ratios=ratio)
    (ROOT/'results'/'summary.json').write_text(json.dumps(report, indent=2), encoding='utf-8')
    plt.rcParams.update({'font.size': 12, 'axes.spines.top': False, 'axes.spines.right': False})
    fig, ax = plt.subplots(figsize=(8, 4.8), layout='constrained')
    for (t, y), label, color, style in zip(curves, ['240 mg every 2 weeks', '480 mg every 4 weeks'],
                                          ['#0072B2', '#D55E00'], ['-', '--']):
        window = t >= START
        ax.plot(t[window]-START, y[window, 0]/V1, label=label, color=color, ls=style, lw=2)
    ax.set(xlim=(0, 28), ylim=(0, None), xlabel='Days from treatment day 364',
           ylabel='Serum concentration (mg/L)')
    ax.set_title('Nivolumab: two dosing intervals', pad=30, fontsize=14, loc='left')
    ax.text(0, 1.02, 'One reference profile; days 364–392; 30-minute infusions',
            transform=ax.transAxes, fontsize=10)
    ax.legend(frameon=False, loc='upper right', fontsize=10)
    ax.grid(axis='y', color='#dddddd', lw=0.6)
    fig.savefig(ROOT/'figures'/'nivolumab-interval.png', dpi=180)
    print(json.dumps(report, indent=2))


if __name__ == '__main__':
    main()

この図から言えること

投与方式を比べるときは、平均濃度だけでなく、最高濃度と投与直前濃度も見ると違いが分かります。

ただし、この図だけでは効果や安全性の差は分かりません。 今回は濃度を比べる学習用の計算であり、患者の投与量や間隔を決めるためのものではありません。

参考文献

  1. Long GV, et al. Assessment of nivolumab exposure and clinical safety of 480 mg every 4 weeks flat-dosing schedule in patients with cancer. Annals of Oncology. 2018. doi:10.1093/annonc/mdy408
  2. Bajaj G, et al. Model-Based Population Pharmacokinetic Analysis of Nivolumab in Patients With Solid Tumors. CPT: Pharmacometrics & Systems Pharmacology. 2017. doi:10.1002/psp4.12143
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?