薬剤師のプログラミング学習日記

プログラミングやコンピュータに関する記事を書いていきます

腎機能評価式のグラフを描く

腎機能評価の指標としてeGFR(推算糸球体濾過量)やCCr(クレアチニンクリアランス)、シスタチンC(Cys-C)などがありますが、病院等で働く薬剤師であれば、仕事中に薬の投与量のチェックをするためにこれらの値を計算することも多いかと思います。
ネットで検索すると、これらの値を計算するサイト*1やアプリケーションはたくさん出てきますが、これまでPythonでいろいろなグラフを描いてきた知識を使って、下のようなちょっとしたツールを作ってみたいと思います。

f:id:enokisaute:20200407234656p:plain



はじめに

この記事のプログラムは医学書院/週刊医学界新聞(第3212号 2017年02月20日)『図 体重と推算Ccr, eGFRの関係』のグラフの形式を参考に作成しています。プログラムの動作や値の正確性等については各自の責任においてご判断ください。

腎機能評価の指標

上にも書きましたがeGFR(推算糸球体濾過量)やCockcroft-Gault式によるCCr(クレアチニンクリアランス)、シスタチンC(Cys-C)などがあります。他にもイヌリンクリアランス、実測によるCCrなどがあるようですが、それぞれに特性がありこれらを患者さんや現場の状況、薬に応じて使い分ける必要があります。
ここでは、成人の場合のeGFRとCCrについて、簡単にその特性を確認しておきます。

CCr(Cockcroft-Gault(CG)式)

<計算式> 
 \rm{CCr(mL/min) = \displaystyle{\frac{(140 -\mbox{年齢}) × \mbox{体重(kg)}}{72 × Cre(mg/dL)}}\ \ \ \ \ \ \ \ \ }※ 女性は\rm{×0.85}

・性別、年齢、体重、血清クレアチニン(Cre)を用いて計算する
・身長が考慮されておらず、肥満患者では腎機能が過大評価されてしまう。そのため、肥満患者では理想体重を用いる必要がある。
・低体重の場合、腎機能は低く出る
・加齢による影響を受けやすい。若年者では高い値が出るが、後期高齢者では実際の年齢による機能低下以上に値が低く出る。そのため加齢に伴いGFRとの乖離が大きくなる。

標準化eGFR(Creat式)

<計算式>
 \rm{\mbox{標準化}eGFR(mL/min/1.73m^2) = 194 × Cre^{-1.094} × \mbox{年齢}^{-0.287}}
 ※ 女性は\rm{×0.739}

・性別、年齢、血清クレアチニン(Cre)を用いて計算する
・体表面積が1.73\rm{m^2}の標準的な体型に補正した値
・CKD(慢性腎臓病)の重症度診断のための指標
・薬物投与設計には使えない

個別eGFR

<計算式>
\rm{\mbox{個別}eGFR(mL/min) = \mbox{標準化}eGFR × \displaystyle{\frac{\mbox{体表面積}}{1.73}}}

\rm{\mbox{体表面積}(m^2) = \mbox{体重(kg)}^{0.425}×\mbox{身長(cm)}^{0.725}×0.007184}

・患者個々のeGFR。「体表面積未補正eGFR」とも表記される。
・体格(身長・体重)を考慮した値となっており、薬物投与設計にはこれを用いるべき。ただし、一部の抗菌薬・抗がん薬などは除く。
・痩せた高齢者など筋肉量の少ない症例の場合はCG式よりも高く推算されるため、過量投与とならないよう注意が必要


その他の注意点として、
・血清クレアチニン値が0.6mg/dL未満の高齢者の場合などはCCr、eGFRどちらの推算式も適応しにくい。
・血清クレアチニン値が低いことの意味(低栄養のため? or 腎機能が良い?)を考える。

・・・一律にこれを使えば良いというものではなく、患者さんの背景やそれぞれの特性を理解した上での評価が必要ということですね。
ページ下の参考文献では、他にも”なぜ添付文書の投与量はCCrで定めているのか”についても解説されています。詳しくはそちらをご参照ください。

3つの計算式のグラフ描画

最終的にはある程度の行数のプログラムとなりそうですが、私はこういうときは最初からすべてのコードを書こうとせずに、まず最小限のところだけを書いてみることが多いです。骨格となる部分ができあがった後で、必要な機能や部品等を肉付けしていくイメージです。

import numpy as np
import matplotlib.pyplot as plt

 
class EvalRenalFunc:
    def __init__(self, age, cre, height, weight, sex):
        self.age = age
        self.cre = cre
        self.height = height
        self.weight = weight
        self.sex = sex
 
    # CCr(CG式)
    def ccr(self):
        if self.sex == '女性':
            factor = 0.85
        else:
            factor = 1.0
        return (140 - self.age) * self.weight * factor / 72.0 / self.cre
 
    # 標準化eGFR
    def stand_egfr(self):
        if self.sex == '女性':
            factor = 0.739
        else:
            factor = 1.0
        return 194 * self.cre ** -1.094 * self.age ** -0.287 * factor
 
    # 個別eGFR
    def indiv_egfr(self):
        bsa = self.bsa()
        return self.stand_egfr() * bsa / 1.73
 
    # 体表面積(Du Bois式)
    def bsa(self):
        return self.weight ** 0.425 * self.height ** 0.725 * 0.007184
 

年齢、Creなどの各パラメータが頻繁に参照・変更されることになるプログラムになりそうなので、クラスを作成することにします。defで関数を定義して作ることも可能ですが、クラスを作ることによってあちこちに同じような名前の変数が散らばることを避けることができ、コードの見通しがよくなります。

クラス自体はシンプルです。コンストラクタの引数に年齢、Cre、身長、体重、性別を取り、CG式によるCCr、標準化eGFR、個別eGFR、体表面積を返すメソッドを持っています。ただし、グラフを描画するため、体重は現時点ではNumpy配列(ndarray)でのみ受け取ることとします。
インスタンスの生成後、各メソッドは次のように呼び出します。

erf = EvalRenalFunc(age, cre, height, weight, '男性')
y1 = erf.ccr()              # CCr
y2 = erf.indiv_egfr()       # 個別eGFR
y3 = erf.stand_egfr()       # 標準化eGFR

あとはweightをxとして、これらのy1, y2, y3をax.plot()に渡してプロットします。

fig = plt.figure()
ax = fig.add_subplot(111)
 
ax.plot(weight, y1, 'blue', label='CCr')
ax.plot(weight, y2, 'red', label='個別eGFR')
ax.plot([x_s, x_e], [y3, y3], color='green', label='標準化eGFR')

続いて、体裁(軸ラベルやグリッドの表示)を整えるためのコード(ページ下のコードを参照)を書くと下のようなグラフが描けます。
f:id:enokisaute:20200401003626p:plain

次回スライダーなどの部品をこのグラフに付け足していきます。
www.yakupro.info


今回のコードです。

import numpy as np
import matplotlib.pyplot as plt
 
# 腎機能評価ツールを作る1
 
class EvalRenalFunc:
    def __init__(self, age, cre, height, weight, sex):
        self.age = age
        self.cre = cre
        self.height = height
        self.weight = weight
        self.sex = sex
 
    # CCr(CG式)
    def ccr(self):
        if self.sex == '女性':
            factor = 0.85
        else:
            factor = 1.0
        return (140 - self.age) * self.weight * factor / 72.0 / self.cre
 
    # 標準化eGFR
    def stand_egfr(self):
        if self.sex == '女性':
            factor = 0.739
        else:
            factor = 1.0
        return 194 * self.cre ** -1.094 * self.age ** -0.287 * factor
 
    # 個別eGFR
    def indiv_egfr(self):
        bsa = self.bsa()
        return self.stand_egfr() * bsa / 1.73
 
    # 体表面積(Du Bois式)
    def bsa(self):
        return self.weight ** 0.425 * self.height ** 0.725 * 0.007184
 
 
age = 70
cre = 1.0
height = 165
x_s, x_e = 20, 100      # 横軸の始点と終点
y_s, y_e = 10, 90       # 縦軸の始点と終点
weight = np.arange(x_s, x_e, 0.5)
 
erf = EvalRenalFunc(age, cre, height, weight, '男性')
y1 = erf.ccr()           # CCr
y2 = erf.indiv_egfr()    # 個別eGFR
y3 = erf.stand_egfr()    # 標準化eGFR
 
fig = plt.figure()
ax = fig.add_subplot(111)
 
ax.plot(weight, y1, 'blue', label='CCr')
ax.plot(weight, y2, 'red', label='個別eGFR')
ax.plot([x_s, x_e], [y3, y3], color='green', label='標準化eGFR')
 
# 軸の表示範囲を設定
ax.set_xlim(x_s, x_e)
ax.set_ylim(y_s, y_e)
# 軸のラベルを設定
ax.set_xlabel('体重(kg)')
ax.set_ylabel(r'$\rmmL/min$')
# グリッドを設定
ax.grid(which='major',color='black',linestyle='--')
# 凡例を配置
ax.legend()
# 縦軸が~30までの範囲を塗りつぶし
rect_x = [x_s, x_e]
rect_y = 30
ax.fill_between(rect_x, rect_y, facecolor='red', alpha=0.2)
# 右側にも縦軸があるグラフにする
ax2 = ax.twinx()
ax2.set_ylim(y_s, y_e)
ax2.set_ylabel(r'$\rmmL/min/1.73m^2$', color='green')
 
plt.show()