ข้อมูลนำเข้าของแบบจำลองที่ตรวจสอบได้: สร้างตารางพารามิเตอร์ที่สืบย้อนถึงที่มา

Health EconomicsClinical Epidemiology ResearchMethodology and Research Design TH
ข้อมูลนำเข้าของแบบจำลองที่ตรวจสอบได้: สร้างตารางพารามิเตอร์ที่สืบย้อนถึงที่มา
On this page

Read the English version

บทคัดย่อ

โรงพยาบาลชุมชนแห่งหนึ่งวางแผนผ่าตัดลำไส้แบบนัดล่วงหน้า 400 ครั้ง และคณะกรรมการการเงินถามว่าการนอนโรงพยาบาลหนึ่งครั้งจะมีต้นทุนเฉลี่ยเท่าไร นักวิเคราะห์ได้รับแบบจำลองต้นทุนต่อมาพร้อมตัวเลขสี่ตัวที่ไม่มีคำอธิบาย คือ 0.25, 12,000, 2,000 และ 0.70 ถ้าอ่านแบบหนึ่งจะได้ 4,500 CU ต่อผู้ป่วย อ่านอีกแบบได้ 5,000 และถ้าใช้ 0.70 ที่พิมพ์ไว้เองจะได้ 4,400 บทความนี้สร้างบันทึกที่ตัดสินเรื่องนี้ได้ คือตารางพารามิเตอร์ที่มีหนึ่งแถวต่อข้อมูลนำเข้าหนึ่งค่า แต่ละแถวระบุปริมาณที่แน่ชัด ผู้ป่วยกลุ่มใด ช่วงเวลาใด หน่วย ที่มา และวิธีที่แบบจำลองใช้ตัวเลขนั้น ตารางตัดสินกรณีนี้ที่ 4,500 CU ต่อผู้ป่วย หรือ 1,800,000 CU สำหรับการผ่าตัด 400 ครั้ง ความเสี่ยงค่าเดียวป้อนให้ทั้งสองเส้นทาง สัดส่วนที่ไม่มีภาวะแทรกซ้อนจึงคำนวณขึ้นเสมอ ไม่พิมพ์ไว้เอง สุดท้ายบทความแยกความต่างระหว่างผู้ป่วยแต่ละคนออกจากความมั่นใจที่เรามีต่อค่าเฉลี่ยของพวกเขา


อ่านจากบนลงล่าง ตัวเลขสี่ตัวชุดเดียวกันให้ค่าใช้จ่ายได้สามแบบ จนกว่าแต่ละตัวจะมีแถวที่บอกความหมาย ตัวเลขทุกตัวมีที่มาอธิบายในหัวข้อด้านล่าง

ตัวเลขสี่ตัวกับคณะกรรมการที่รอคำตอบ

โรงพยาบาลชุมชนแห่งหนึ่งวางแผนผ่าตัดลำไส้แบบนัดล่วงหน้า 400 ครั้งในปีหน้า คณะกรรมการการเงินของโรงพยาบาลถามคำถามเดียวว่า การนอนโรงพยาบาลหนึ่งครั้งจะมีต้นทุนเฉลี่ยเท่าไร นักวิเคราะห์ที่สร้างแบบจำลองต้นทุนไว้ลาออกไปแล้ว นักวิเคราะห์คนใหม่เปิดไฟล์และพบเซลล์สี่ช่อง คือ 0.25, 12,000, 2,000 และ 0.70 ซึ่งมีเพียงป้ายกำกับว่า "risk", "cost high", "cost low" และ "other risk"

ต้นทุนคิดเป็น CU ซึ่งเป็นหน่วยเงินสมมติ ต่อผู้ป่วยหนึ่งคนตลอดการนอนโรงพยาบาล เซลล์สี่ช่องนี้อ่านได้สามแบบ และแต่ละแบบให้ยอดค่าใช้จ่ายรวมที่คณะกรรมการจะได้เห็นต่างกัน

  • ถ้า 12,000 คือต้นทุนรวมเฉลี่ยของผู้ป่วยที่มีภาวะแทรกซ้อน และ 2,000 คือต้นทุนเฉลี่ยของผู้ป่วยที่ไม่มี ผู้ป่วยที่ไม่มีภาวะแทรกซ้อนคือ 0.75 ที่เหลือจาก 0.25 การนอนโรงพยาบาลหนึ่งครั้งจึงมีต้นทุนเฉลี่ย 0.25 × 12,000 + 0.75 × 2,000 = 3,000 + 1,500 = 4,500 CU ต่อผู้ป่วย ซึ่งรวมเป็น 1,800,000 CU สำหรับการผ่าตัด 400 ครั้ง
  • ถ้า 12,000 คือต้นทุนส่วนที่ภาวะแทรกซ้อนบวกเพิ่มเข้าไปบนการนอนปกติที่มีต้นทุน 2,000 ค่าเฉลี่ยจะเป็น 2,000 + 0.25 × 12,000 = 5,000 CU หรือยอดรวมทั้งหมด 2,000,000 CU
  • ถ้านำ 0.70 ซึ่งมีคนพิมพ์ใส่เซลล์ไว้เอง โดยไม่ได้คำนวณจาก 0.25 มาใช้เป็นสัดส่วนของผู้ป่วยที่ไม่มีภาวะแทรกซ้อน ค่าเฉลี่ยจะเป็น 0.25 × 12,000 + 0.70 × 2,000 = 4,400 CU หรือยอดรวม 1,760,000 CU สาเหตุคือ 0.25 กับ 0.70 รวมกันได้เพียง 0.95 ผู้ป่วยบางส่วนจึงไม่อยู่ในเส้นทางใดเลยและไม่ถูกนับต้นทุน

ในไฟล์ไม่มีอะไรบอกว่าการอ่านแบบไหนถูก คณะกรรมการจึงอาจได้รับยอดค่าใช้จ่ายรวมแบบใดก็ได้ในสามแบบนี้ ทางแก้ไม่ใช่ตัวเลขที่ดีกว่า แต่เป็นบันทึกที่เขียนไว้ว่าตัวเลขแต่ละตัวหมายถึงอะไร บันทึกนั้นคือตารางพารามิเตอร์ (parameter table) ซึ่งมีหนึ่งแถวต่อข้อมูลนำเข้าหนึ่งค่า บอกว่าตัวเลขวัดอะไร ของใคร ในช่วงเวลาใด มาจากไหน และแบบจำลองใช้มันอย่างไร

เทียบแท่งทั้งสาม เซลล์สี่ช่องชุดเดียวกันให้ยอดค่าใช้จ่ายรวม 1,800,000, 2,000,000 และ 1,760,000 CU สำหรับการผ่าตัด 400 ครั้ง มีเพียงแท่งสีเขียวที่ตรงกับความหมายของตัวเลข ความหมายจึงเป็นตัวตัดสินยอดค่าใช้จ่าย ไม่ใช่การคำนวณ

ตั้งชื่อปริมาณก่อนใส่ค่า

พารามิเตอร์ (parameter) คือข้อมูลนำเข้าหนึ่งค่าของแบบจำลอง เช่น ความเสี่ยงที่จะเกิดภาวะแทรกซ้อน ส่วน estimand คือปริมาณที่แน่ชัดที่หลักฐานควรประมาณ ซึ่งเขียนเป็นคำพูดให้ครบ สำหรับความเสี่ยงในกรณีนี้ ต้องระบุเหตุการณ์ (ภาวะแทรกซ้อนใดก็ได้หลังผ่าตัด) ประชากร (ผู้ใหญ่ที่ผ่าตัดลำไส้แบบนัดล่วงหน้าที่โรงพยาบาลนี้) สถานที่ (หอผู้ป่วยศัลยกรรม) และช่วงเวลา (ตั้งแต่ผ่าตัดจนกลับบ้าน) (Briggs และคณะ)

ต้นทุนทั้งสองค่าก็ต้องระบุอย่างเดียวกัน และในกรณีนี้ถ้อยคำเป็นตัวตัดสินยอดค่าใช้จ่าย ถ้าเขียนว่า "ต้นทุนรวมเฉลี่ยต่อผู้ป่วยที่มีภาวะแทรกซ้อน ตั้งแต่รับไว้จนกลับบ้าน" จะได้การอ่านแบบแรก คือ 4,500 CU ส่วนถ้าเขียนว่า "ต้นทุนส่วนเพิ่มเฉลี่ยที่ภาวะแทรกซ้อนก่อให้เกิด" จะได้แบบที่สอง คือ 5,000 CU ตัวเลข 12,000 เหมือนกันทั้งสองแบบ ต่างกันเพียง estimand

ให้พารามิเตอร์แต่ละตัวมีชื่อรหัสที่คงที่ เรียกว่าตัวระบุ (identifier) เช่น p_event และใช้ชื่อนั้นทุกจุดที่ต้องการปริมาณเดียวกัน เมื่อแก้ค่าที่จุดเดียว การคำนวณทุกจุดที่ใช้ค่านั้นก็เปลี่ยนตาม

ค่าที่คำนวณจากข้อมูลนำเข้าซึ่งมีชื่อประจำตัวอยู่แล้ว เรียกว่าค่าที่คำนวณได้ (derived value) และตารางต้องบันทึกวิธีคำนวณไว้ สัดส่วนที่ไม่มีภาวะแทรกซ้อนเป็นค่าแบบนี้ คือ p_nonevent = 1 − p_event = 0.75 การอ่านแบบที่สามจึงเกิดขึ้นไม่ได้

แยกค่าจากแหล่งข้อมูลออกจากค่าที่ใช้ในแบบจำลองเมื่อต้องแปลงค่า งานวิจัยอาจรายงานต้นทุนในปีราคาที่เก่ากว่า หรือรายงานความเสี่ยงในช่วงติดตามที่ยาวกว่าที่แบบจำลองใช้ แถวนั้นจึงต้องเก็บค่าและหน่วยเดิม ตำแหน่งที่พบในแหล่งข้อมูล และวิธีแปลงค่าไว้ เพื่อให้ผู้ตรวจสอบตามการเปลี่ยนแปลงได้โดยไม่ต้องเปิดโค้ดของแบบจำลอง (Eddy และคณะ) ในกรณีนี้ต้นทุนจากฝ่ายการเงินเป็นราคาของปีนี้และคิดต่อการนอนโรงพยาบาลหนึ่งครั้งอยู่แล้ว แถวจึงบันทึกว่าไม่ต้องแปลงค่า

รวมกันแล้วได้เป็นช่องข้อมูลที่ทุกแถวควรบันทึก ตามตารางด้านล่าง ค่าฐาน (base value) คือค่ากลางที่แบบจำลองใช้เป็นจุดเริ่มต้น มีอีกช่องหนึ่งคือค่าต่ำและค่าสูงที่เป็นไปได้ ซึ่งบอกว่าเรามั่นใจในตัวเลขนั้นแค่ไหน ช่องนี้จะเติมในตอนหลังของบทความ เมื่อคำนวณความไม่แน่นอนแล้ว

ช่องข้อมูลขั้นต่ำสำหรับตรวจสอบ

ช่องข้อมูลสิ่งที่บันทึกเหตุผล
ตัวระบุและ estimandชื่อคงที่ และเหตุการณ์ ผลลัพธ์ หรือทรัพยากรที่เขียนเป็นคำป้องกันไม่ให้ปริมาณสองตัวใช้ชื่อเดียวกัน
ประชากรและช่วงเวลาใครถูกนับ สถานที่ ตัวเปรียบเทียบถ้ามี และช่วงเวลาบอกว่าตัวเลขเข้ากับโรงพยาบาลนี้หรือไม่
หน่วยและที่มาตัวหาร สกุลเงินและปี เอกสารอ้างอิง และตำแหน่งที่พบให้ผู้ตรวจสอบเช็กหน่วยและย้อนกลับไปที่แหล่งข้อมูลได้
ค่าฐานและช่วงค่าค่ากลาง และค่าต่ำกับค่าสูงที่เป็นไปได้ตามหลักฐานบอกว่าเรามั่นใจแค่ไหน แยกจากค่าที่ผู้วิเคราะห์เลือกลองเอง
การแปลงค่าและการใช้ซ้ำสมการ ข้อมูลนำเข้าที่ใช้ และทุกการคำนวณที่นำค่านี้ไปใช้ทำให้คนอื่นคำนวณค่าที่คำนวณได้ซ้ำแล้วได้ผลเท่าเดิม

ตารางพารามิเตอร์ของการนอนโรงพยาบาลเพื่อผ่าตัดหนึ่งครั้ง

นี่คือตารางที่นักวิเคราะห์คนใหม่เขียนขึ้น หลังจากถามทีมศัลยกรรมและฝ่ายการเงินว่าแต่ละเซลล์หมายถึงอะไร ทุกแถวใช้ประชากรเดียวกัน คือผู้ใหญ่ที่ผ่าตัดลำไส้แบบนัดล่วงหน้า และช่วงเวลาเดียวกัน คือการนอนโรงพยาบาลครั้งนั้น ต้นทุนคือเงินที่โรงพยาบาลจ่าย

ต้นทุนสองค่าเป็นค่าเฉลี่ยแบบมีเงื่อนไข (conditional mean) คือค่าเฉลี่ยเฉพาะผู้ป่วยในเส้นทางเดียว ได้แก่ผู้ที่มีภาวะแทรกซ้อน หรือผู้ที่ไม่มี แบบจำลองรวมสองค่านี้เป็นค่าเฉลี่ยแบบไม่มีเงื่อนไขหนึ่งค่า คือค่าเฉลี่ยของผู้ป่วยทุกคน ซึ่งเป็นตัวเลขที่คณะกรรมการถาม ค่านี้เรียกว่าต้นทุนคาดหมาย (expected cost) และในสูตรเขียนแทนด้วย E[C] ค่าทั้งหมดเป็นตัวอย่างสมมติ และยังไม่ได้ใส่ช่วงค่า

แผนภาพหลังตารางวาดแต่ละเส้นทางเป็นกิ่งหนึ่งของต้นไม้ มีสองกิ่ง คือกิ่งของผู้ป่วยที่มีภาวะแทรกซ้อนและกิ่งของผู้ป่วยที่ไม่มี

หนึ่งแถวต่อข้อมูลนำเข้าหนึ่งค่า

ตัวระบุEstimand และหน่วยที่มาค่าฐานการแปลงค่า
p_eventความเสี่ยงที่จะเกิดภาวะแทรกซ้อนใดก็ได้ ตั้งแต่ผ่าตัดจนกลับบ้าน (สัดส่วน)การทบทวนการผ่าตัดของปีที่แล้วของโรงพยาบาล0.25ใส่ค่าตรง ใช้ร่วมกันทั้งสองเส้นทาง
cost_eventต้นทุนรวมเฉลี่ยต่อผู้ป่วยที่มีภาวะแทรกซ้อน (CU)บันทึกการเงินของโรงพยาบาล ผู้ป่วยกลุ่มเดียวกัน12,000ค่าเฉลี่ยแบบมีเงื่อนไข ใส่ค่าตรง
cost_noneventต้นทุนรวมเฉลี่ยต่อผู้ป่วยที่ไม่มีภาวะแทรกซ้อน (CU)บันทึกการเงินของโรงพยาบาล ผู้ป่วยกลุ่มเดียวกัน2,000ค่าเฉลี่ยแบบมีเงื่อนไข ใส่ค่าตรง
p_noneventความเสี่ยงที่จะไม่เกิดภาวะแทรกซ้อน ตั้งแต่ผ่าตัดจนกลับบ้าน (สัดส่วน)คำนวณได้0.751 − p_event ไม่พิมพ์ไว้เอง
expected_costต้นทุนเฉลี่ยต่อผู้ป่วยหนึ่งคน คิดจากผู้ป่วยทุกคน (CU)คำนวณได้4,500p_event × cost_event + p_nonevent × cost_nonevent
ไล่ตามลูกศรสีน้ำเงิน ชื่อเดียวคือ p_event = 0.25 ป้อนให้ทั้งสองกิ่ง และกิ่งที่ไม่มีภาวะแทรกซ้อนใช้ 1 − p_event = 0.75 สองกิ่งรวมกันได้ 1 ผู้ป่วยทุกคนจึงถูกนับครั้งเดียว ส่วนกล่องสีส้มเส้นประแสดงว่า 0.70 ที่พิมพ์ไว้เองทำให้รวมได้เพียง 0.95

ตรวจการอ่านทั้งสามแบบด้วย R

โค้ด R readings.R
# Three readings of the same four cells, in illustrative CU per patient and for 400 operations.
p_event <- 0.25
cost_event <- 12000
cost_nonevent <- 2000
p_typed <- 0.70
ops <- 400
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")
path_means <- p_event * cost_event + (1 - p_event) * cost_nonevent
extra_cost <- cost_nonevent + p_event * cost_event
typed <- p_event * cost_event + p_typed * cost_nonevent
cat("Path means (CU/patient; all):", big(path_means), ";", big(ops * path_means), "\n")
cat("Extra cost (CU/patient; all):", big(extra_cost), ";", big(ops * extra_cost), "\n")
cat("Typed (sum; CU/patient; all):", p_event + p_typed, ";", typed, ";", big(ops * typed), "\n")
ผลลัพธ์จากการรัน readings.log
Path means (CU/patient; all): 4,500 ; 1,800,000
Extra cost (CU/patient; all): 5,000 ; 2,000,000
Typed (sum; CU/patient; all): 0.95 ; 4400 ; 1,760,000

ต้องใช้ R ≥ 4.5.2 · tibble 3.3.1 · dplyr 1.2.1 · ggplot2 4.0.3 · ใช้ได้กับ R 4.6.1 · ดาวน์โหลด auditable-model-inputs-parameter-tables.R

สคริปต์ฉบับเต็ม
# Auditable Model Inputs: Build a Parameter Table You Can Trace
# Shows how a parameter table settles what four numbers mean, then gives the expected cost.
# แสดงว่าตารางพารามิเตอร์ช่วยตัดสินความหมายของตัวเลขสี่ตัว และให้ต้นทุนคาดหมายต่อผู้ป่วยหนึ่งคน
# Requires: R >= 4.5.2
# Tested: works in R 4.6.1
# tibble 3.3.1 (stable)
# dplyr 1.2.1 (stable)
# ggplot2 4.0.3 (stable)
# install.packages(c("tibble", "dplyr", "ggplot2"))
# update.packages(oldPkgs = c("tibble", "dplyr", "ggplot2"))
# remotes::install_version("tibble", "3.3.1")
# remotes::install_version("dplyr", "1.2.1")
# remotes::install_version("ggplot2", "4.0.3")
library(tibble) # Write the parameter table as an explicit input table.
library(dplyr) # Calculate each path's contribution from the table.
library(ggplot2) # Display the contributions without saving files.
stopifnot(getRversion() >= "4.5.2")

#%%@note 1. The parameter table
# One row per input: the identifier, its value and what it means; the share without a complication
# is derived from p_event, never typed. All values are illustrative.
# หนึ่งแถวต่อข้อมูลนำเข้าหนึ่งค่า ระบุชื่อ ค่า และความหมาย ส่วนสัดส่วนที่ไม่มีภาวะแทรกซ้อนคำนวณจาก
# p_event เสมอ ไม่พิมพ์แยก ค่าทั้งหมดเป็นตัวอย่างสมมติ
p_event <- 0.25
cost_event <- 12000
cost_nonevent <- 2000
p_typed <- 0.70
ops <- 400
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")
p_nonevent <- 1 - p_event
parameters <- tibble::tibble(
  identifier = c("p_event", "cost_event", "cost_nonevent", "p_nonevent"),
  value = c(p_event, cost_event, cost_nonevent, p_nonevent),
  meaning = c("Risk of any complication, operation to discharge",
              "Mean total cost per patient with a complication (CU)",
              "Mean total cost per patient without a complication (CU)",
              "Derived: 1 - p_event")
)

#%%@note 2. Three readings of the same four cells
# The same numbers give three bills, depending on what 12000 means and whether the share is typed.
# ตัวเลขชุดเดียวกันให้ค่าใช้จ่ายสามแบบ ขึ้นกับว่า 12000 หมายถึงอะไร และสัดส่วนถูกพิมพ์แยกหรือไม่
path_means <- p_event * cost_event + (1 - p_event) * cost_nonevent
extra_cost <- cost_nonevent + p_event * cost_event
typed <- p_event * cost_event + p_typed * cost_nonevent

#%%@note 3. The expected cost from the table
# Weight each path's mean cost by its probability; a probability has no unit, so the unit stays CU.
# ถ่วงต้นทุนเฉลี่ยของแต่ละเส้นทางด้วยความน่าจะเป็น ความน่าจะเป็นไม่มีหน่วย ผลจึงเป็น CU ต่อผู้ป่วย
paths <- tibble::tibble(
  path = c("Complication", "No complication"),
  probability = c(p_event, p_nonevent),
  mean_cost = c(cost_event, cost_nonevent)
) |>
  dplyr::mutate(contribution = probability * mean_cost)
stopifnot(abs(sum(paths$probability) - 1) < 1e-9)
from_event <- p_event * cost_event
from_nonevent <- p_nonevent * cost_nonevent
expected_cost <- from_event + from_nonevent
stopifnot(abs(sum(paths$contribution) - expected_cost) < 1e-9)

#%%@note 4. The bill for 400 operations, counted in patients
# Count patients on each path, then multiply by each path's mean cost.
# นับผู้ป่วยในแต่ละเส้นทาง แล้วคูณด้วยต้นทุนเฉลี่ยของเส้นทางนั้น
with_event <- ops * p_event
bill_event <- with_event * cost_event
bill_nonevent <- (ops - with_event) * cost_nonevent
total <- bill_event + bill_nonevent
stopifnot(abs(total / ops - expected_cost) < 1e-9)

#%%@note 5. Spread between patients versus uncertainty about the mean
# The SD of 36 patients' costs is the spread; the SE is how precisely the mean is known.
# SD ของต้นทุนผู้ป่วย 36 คนคือความผันแปรระหว่างคน ส่วน SE บอกว่ารู้ค่าเฉลี่ยได้แม่นเพียงใด
sd_cost <- 6000
n_event <- 36
se_count <- 2
se_cost <- sd_cost / sqrt(n_event)
cost_at <- function(cost) p_event * cost + (1 - p_event) * cost_nonevent
se_range <- cost_event + c(-se_count, se_count) * se_cost
sd_range <- cost_event + c(-se_count, se_count) * sd_cost

#%%@note 6. Display the contributions
# Plot each path's contribution to the expected cost; the script saves no file.
# แสดงส่วนที่แต่ละเส้นทางเพิ่มให้ต้นทุนคาดหมาย โดยไม่บันทึกไฟล์ภาพ
print(ggplot2::ggplot(paths, ggplot2::aes(x = path, y = contribution)) +
  ggplot2::geom_col() + ggplot2::labs(y = "Contribution to expected cost (CU/patient)"))

#%%@note 7. Numbers the article quotes
# Print the short boxes' lines in their reading order.
# พิมพ์ผลลัพธ์ตรงกับกล่องโค้ดสั้นตามลำดับในบทความ
cat("Path means (CU/patient; all):", big(path_means), ";", big(ops * path_means), "\n")
cat("Extra cost (CU/patient; all):", big(extra_cost), ";", big(ops * extra_cost), "\n")
cat("Typed (sum; CU/patient; all):", p_event + p_typed, ";", typed, ";", big(ops * typed), "\n")
cat("p_nonevent (derived); probabilities add to:", p_nonevent, ";", p_event + p_nonevent, "\n")
cat("Path contributions (CU/patient):", from_event, "+", from_nonevent, "\n")
cat("Expected cost (CU/patient):", expected_cost, "\n")
cat("Patients with / without a complication:", with_event, "/", ops - with_event, "\n")
cat("Bill by path (CU):", big(bill_event), "+", big(bill_nonevent), "=", big(total), "\n")
cat("Share of the bill from complications (%):", round(100 * bill_event / total, 2), "\n")
cat("SE (CU); mean +/- 2 SE; expected cost:", se_cost, ";", se_range, ";", cost_at(se_range), "\n")
cat("SD by mistake: mean +/- 2 SD; expected cost:", sd_range, ";", cost_at(sd_range), "\n")
เซลล์สี่ช่องชุดเดียวกัน อ่านสามแบบ ทั้งต่อผู้ป่วยหนึ่งคนและรวมการผ่าตัด 400 ครั้ง

ต้นทุนคาดหมายทีละขั้น

การคำนวณใช้ตัวระบุเดียวกับในตาราง เมื่อเปลี่ยน p_event น้ำหนักของทั้งสองเส้นทางจะเปลี่ยนพร้อมกัน สองเส้นทางจึงไม่มีทางขัดกัน

  1. หาสัดส่วนที่ไม่มีภาวะแทรกซ้อน

    \[ p_{nonevent}=1-0.25=0.75 \]

    ใช้ p_event = 0.25 ถ่วงน้ำหนักเส้นทางที่มีภาวะแทรกซ้อน ส่วนที่เหลือเรียกว่าส่วนเติมเต็ม (complement) คือ p_nonevent ใช้ถ่วงเส้นทางที่ไม่มีภาวะแทรกซ้อน ทั้งสองค่าจึงรวมกันได้ 1

  2. ตรวจหน่วยก่อนรวม

    \[ E[C]=0.25\times 12000+0.75\times 2000=3000+1500=4500 \]

    ถ่วงน้ำหนัก 12,000 CU ต่อผู้ป่วย และ 2,000 CU ต่อผู้ป่วย ด้วยความน่าจะเป็นของแต่ละเส้นทาง ความน่าจะเป็นไม่มีหน่วย ความน่าจะเป็นคูณต้นทุนต่อผู้ป่วยจึงยังเป็นต้นทุนต่อผู้ป่วย ผลรวมคือ E[C] หรือต้นทุนคาดหมาย

ผลลัพธ์: ต้นทุนคาดหมายเท่ากับ 4,500 CU ต่อผู้ป่วย เฉลี่ยจากผู้ป่วยทุกคนที่ได้รับการผ่าตัด ซึ่งตรงกับการอ่านแบบแรกและเป็นแบบที่ตารางบันทึกไว้

ตรวจตัวเลขด้วย R

โค้ด R example.R
# Expected cost per patient from the named inputs, in illustrative CU.
p_event <- 0.25
cost_event <- 12000
cost_nonevent <- 2000
p_nonevent <- 1 - p_event
from_event <- p_event * cost_event
from_nonevent <- p_nonevent * cost_nonevent
expected_cost <- from_event + from_nonevent
cat("p_nonevent (derived); probabilities add to:", p_nonevent, ";", p_event + p_nonevent, "\n")
cat("Path contributions (CU/patient):", from_event, "+", from_nonevent, "\n")
cat("Expected cost (CU/patient):", expected_cost, "\n")
ผลลัพธ์จากการรัน example.log
p_nonevent (derived); probabilities add to: 0.75 ; 1
Path contributions (CU/patient): 3000 + 1500
Expected cost (CU/patient): 4500

ต้องใช้ R ≥ 4.5.2 · tibble 3.3.1 · dplyr 1.2.1 · ggplot2 4.0.3 · ใช้ได้กับ R 4.6.1 · ดาวน์โหลด auditable-model-inputs-parameter-tables.R

สคริปต์ฉบับเต็ม
# Auditable Model Inputs: Build a Parameter Table You Can Trace
# Shows how a parameter table settles what four numbers mean, then gives the expected cost.
# แสดงว่าตารางพารามิเตอร์ช่วยตัดสินความหมายของตัวเลขสี่ตัว และให้ต้นทุนคาดหมายต่อผู้ป่วยหนึ่งคน
# Requires: R >= 4.5.2
# Tested: works in R 4.6.1
# tibble 3.3.1 (stable)
# dplyr 1.2.1 (stable)
# ggplot2 4.0.3 (stable)
# install.packages(c("tibble", "dplyr", "ggplot2"))
# update.packages(oldPkgs = c("tibble", "dplyr", "ggplot2"))
# remotes::install_version("tibble", "3.3.1")
# remotes::install_version("dplyr", "1.2.1")
# remotes::install_version("ggplot2", "4.0.3")
library(tibble) # Write the parameter table as an explicit input table.
library(dplyr) # Calculate each path's contribution from the table.
library(ggplot2) # Display the contributions without saving files.
stopifnot(getRversion() >= "4.5.2")

#%%@note 1. The parameter table
# One row per input: the identifier, its value and what it means; the share without a complication
# is derived from p_event, never typed. All values are illustrative.
# หนึ่งแถวต่อข้อมูลนำเข้าหนึ่งค่า ระบุชื่อ ค่า และความหมาย ส่วนสัดส่วนที่ไม่มีภาวะแทรกซ้อนคำนวณจาก
# p_event เสมอ ไม่พิมพ์แยก ค่าทั้งหมดเป็นตัวอย่างสมมติ
p_event <- 0.25
cost_event <- 12000
cost_nonevent <- 2000
p_typed <- 0.70
ops <- 400
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")
p_nonevent <- 1 - p_event
parameters <- tibble::tibble(
  identifier = c("p_event", "cost_event", "cost_nonevent", "p_nonevent"),
  value = c(p_event, cost_event, cost_nonevent, p_nonevent),
  meaning = c("Risk of any complication, operation to discharge",
              "Mean total cost per patient with a complication (CU)",
              "Mean total cost per patient without a complication (CU)",
              "Derived: 1 - p_event")
)

#%%@note 2. Three readings of the same four cells
# The same numbers give three bills, depending on what 12000 means and whether the share is typed.
# ตัวเลขชุดเดียวกันให้ค่าใช้จ่ายสามแบบ ขึ้นกับว่า 12000 หมายถึงอะไร และสัดส่วนถูกพิมพ์แยกหรือไม่
path_means <- p_event * cost_event + (1 - p_event) * cost_nonevent
extra_cost <- cost_nonevent + p_event * cost_event
typed <- p_event * cost_event + p_typed * cost_nonevent

#%%@note 3. The expected cost from the table
# Weight each path's mean cost by its probability; a probability has no unit, so the unit stays CU.
# ถ่วงต้นทุนเฉลี่ยของแต่ละเส้นทางด้วยความน่าจะเป็น ความน่าจะเป็นไม่มีหน่วย ผลจึงเป็น CU ต่อผู้ป่วย
paths <- tibble::tibble(
  path = c("Complication", "No complication"),
  probability = c(p_event, p_nonevent),
  mean_cost = c(cost_event, cost_nonevent)
) |>
  dplyr::mutate(contribution = probability * mean_cost)
stopifnot(abs(sum(paths$probability) - 1) < 1e-9)
from_event <- p_event * cost_event
from_nonevent <- p_nonevent * cost_nonevent
expected_cost <- from_event + from_nonevent
stopifnot(abs(sum(paths$contribution) - expected_cost) < 1e-9)

#%%@note 4. The bill for 400 operations, counted in patients
# Count patients on each path, then multiply by each path's mean cost.
# นับผู้ป่วยในแต่ละเส้นทาง แล้วคูณด้วยต้นทุนเฉลี่ยของเส้นทางนั้น
with_event <- ops * p_event
bill_event <- with_event * cost_event
bill_nonevent <- (ops - with_event) * cost_nonevent
total <- bill_event + bill_nonevent
stopifnot(abs(total / ops - expected_cost) < 1e-9)

#%%@note 5. Spread between patients versus uncertainty about the mean
# The SD of 36 patients' costs is the spread; the SE is how precisely the mean is known.
# SD ของต้นทุนผู้ป่วย 36 คนคือความผันแปรระหว่างคน ส่วน SE บอกว่ารู้ค่าเฉลี่ยได้แม่นเพียงใด
sd_cost <- 6000
n_event <- 36
se_count <- 2
se_cost <- sd_cost / sqrt(n_event)
cost_at <- function(cost) p_event * cost + (1 - p_event) * cost_nonevent
se_range <- cost_event + c(-se_count, se_count) * se_cost
sd_range <- cost_event + c(-se_count, se_count) * sd_cost

#%%@note 6. Display the contributions
# Plot each path's contribution to the expected cost; the script saves no file.
# แสดงส่วนที่แต่ละเส้นทางเพิ่มให้ต้นทุนคาดหมาย โดยไม่บันทึกไฟล์ภาพ
print(ggplot2::ggplot(paths, ggplot2::aes(x = path, y = contribution)) +
  ggplot2::geom_col() + ggplot2::labs(y = "Contribution to expected cost (CU/patient)"))

#%%@note 7. Numbers the article quotes
# Print the short boxes' lines in their reading order.
# พิมพ์ผลลัพธ์ตรงกับกล่องโค้ดสั้นตามลำดับในบทความ
cat("Path means (CU/patient; all):", big(path_means), ";", big(ops * path_means), "\n")
cat("Extra cost (CU/patient; all):", big(extra_cost), ";", big(ops * extra_cost), "\n")
cat("Typed (sum; CU/patient; all):", p_event + p_typed, ";", typed, ";", big(ops * typed), "\n")
cat("p_nonevent (derived); probabilities add to:", p_nonevent, ";", p_event + p_nonevent, "\n")
cat("Path contributions (CU/patient):", from_event, "+", from_nonevent, "\n")
cat("Expected cost (CU/patient):", expected_cost, "\n")
cat("Patients with / without a complication:", with_event, "/", ops - with_event, "\n")
cat("Bill by path (CU):", big(bill_event), "+", big(bill_nonevent), "=", big(total), "\n")
cat("Share of the bill from complications (%):", round(100 * bill_event / total, 2), "\n")
cat("SE (CU); mean +/- 2 SE; expected cost:", se_cost, ";", se_range, ";", cost_at(se_range), "\n")
cat("SD by mistake: mean +/- 2 SD; expected cost:", sd_range, ";", cost_at(sd_range), "\n")
รันโค้ดนี้ใน R จะได้ตัวเลขชุดเดียวกับตัวอย่างคำนวณ

ค่าเฉลี่ยมาจากไหน

ต้นทุนคาดหมายคือค่าเฉลี่ยถ่วงน้ำหนัก ต้นทุนของแต่ละเส้นทางถ่วงด้วยสัดส่วนของผู้ป่วยที่ไปทางนั้น เส้นทางที่มีภาวะแทรกซ้อนสมทบ 3,000 CU ต่อผู้ป่วย ส่วนอีกเส้นทางสมทบ 1,500 CU รวมกันได้ 4,500 CU ตามที่คำนวณไว้

การนับเป็นจำนวนผู้ป่วยให้ข้อสรุปเดียวกันในแบบที่คณะกรรมการเห็นภาพง่าย ในการผ่าตัด 400 ครั้ง คาดว่าผู้ป่วย 100 คน (400 × 0.25) จะมีภาวะแทรกซ้อน ส่วนอีก 300 คนไม่มี ค่าใช้จ่ายของกลุ่มแรกคือ 100 × 12,000 = 1,200,000 CU ส่วนกลุ่มหลังคือ 300 × 2,000 = 600,000 CU รวมเป็น 1,800,000 CU ซึ่งเฉลี่ยเป็น 4,500 CU ต่อผู้ป่วยเช่นเดิม

ดังนั้น 66.67% ของยอดค่าใช้จ่ายรวมมาจากผู้ป่วยหนึ่งในสี่ที่มีภาวะแทรกซ้อน ค่าเฉลี่ยจึงสูงกว่าต้นทุนของการนอนที่ไม่มีภาวะแทรกซ้อนอยู่มาก และนี่คือเหตุผลที่ความเสี่ยงของภาวะแทรกซ้อนต้องแม่นยำ ตัวสำรวจแบบโต้ตอบด้านล่างวาดส่วนสมทบของแต่ละเส้นทางเป็นพื้นที่ ความกว้างคือสัดส่วนผู้ป่วย ความสูงคือต้นทุนต่อผู้ป่วยของเส้นทางนั้น

ตรวจการแยกค่าใช้จ่ายด้วย R

โค้ด R contributions.R
# Split the bill for 400 operations by path, in illustrative CU.
p_event <- 0.25
cost_event <- 12000
cost_nonevent <- 2000
ops <- 400
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")
with_event <- ops * p_event
bill_event <- with_event * cost_event
bill_nonevent <- (ops - with_event) * cost_nonevent
total <- bill_event + bill_nonevent
cat("Patients with / without a complication:", with_event, "/", ops - with_event, "\n")
cat("Bill by path (CU):", big(bill_event), "+", big(bill_nonevent), "=", big(total), "\n")
cat("Share of the bill from complications (%):", round(100 * bill_event / total, 2), "\n")
ผลลัพธ์จากการรัน contributions.log
Patients with / without a complication: 100 / 300
Bill by path (CU): 1,200,000 + 600,000 = 1,800,000
Share of the bill from complications (%): 66.67

ต้องใช้ R ≥ 4.5.2 · tibble 3.3.1 · dplyr 1.2.1 · ggplot2 4.0.3 · ใช้ได้กับ R 4.6.1 · ดาวน์โหลด auditable-model-inputs-parameter-tables.R

สคริปต์ฉบับเต็ม
# Auditable Model Inputs: Build a Parameter Table You Can Trace
# Shows how a parameter table settles what four numbers mean, then gives the expected cost.
# แสดงว่าตารางพารามิเตอร์ช่วยตัดสินความหมายของตัวเลขสี่ตัว และให้ต้นทุนคาดหมายต่อผู้ป่วยหนึ่งคน
# Requires: R >= 4.5.2
# Tested: works in R 4.6.1
# tibble 3.3.1 (stable)
# dplyr 1.2.1 (stable)
# ggplot2 4.0.3 (stable)
# install.packages(c("tibble", "dplyr", "ggplot2"))
# update.packages(oldPkgs = c("tibble", "dplyr", "ggplot2"))
# remotes::install_version("tibble", "3.3.1")
# remotes::install_version("dplyr", "1.2.1")
# remotes::install_version("ggplot2", "4.0.3")
library(tibble) # Write the parameter table as an explicit input table.
library(dplyr) # Calculate each path's contribution from the table.
library(ggplot2) # Display the contributions without saving files.
stopifnot(getRversion() >= "4.5.2")

#%%@note 1. The parameter table
# One row per input: the identifier, its value and what it means; the share without a complication
# is derived from p_event, never typed. All values are illustrative.
# หนึ่งแถวต่อข้อมูลนำเข้าหนึ่งค่า ระบุชื่อ ค่า และความหมาย ส่วนสัดส่วนที่ไม่มีภาวะแทรกซ้อนคำนวณจาก
# p_event เสมอ ไม่พิมพ์แยก ค่าทั้งหมดเป็นตัวอย่างสมมติ
p_event <- 0.25
cost_event <- 12000
cost_nonevent <- 2000
p_typed <- 0.70
ops <- 400
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")
p_nonevent <- 1 - p_event
parameters <- tibble::tibble(
  identifier = c("p_event", "cost_event", "cost_nonevent", "p_nonevent"),
  value = c(p_event, cost_event, cost_nonevent, p_nonevent),
  meaning = c("Risk of any complication, operation to discharge",
              "Mean total cost per patient with a complication (CU)",
              "Mean total cost per patient without a complication (CU)",
              "Derived: 1 - p_event")
)

#%%@note 2. Three readings of the same four cells
# The same numbers give three bills, depending on what 12000 means and whether the share is typed.
# ตัวเลขชุดเดียวกันให้ค่าใช้จ่ายสามแบบ ขึ้นกับว่า 12000 หมายถึงอะไร และสัดส่วนถูกพิมพ์แยกหรือไม่
path_means <- p_event * cost_event + (1 - p_event) * cost_nonevent
extra_cost <- cost_nonevent + p_event * cost_event
typed <- p_event * cost_event + p_typed * cost_nonevent

#%%@note 3. The expected cost from the table
# Weight each path's mean cost by its probability; a probability has no unit, so the unit stays CU.
# ถ่วงต้นทุนเฉลี่ยของแต่ละเส้นทางด้วยความน่าจะเป็น ความน่าจะเป็นไม่มีหน่วย ผลจึงเป็น CU ต่อผู้ป่วย
paths <- tibble::tibble(
  path = c("Complication", "No complication"),
  probability = c(p_event, p_nonevent),
  mean_cost = c(cost_event, cost_nonevent)
) |>
  dplyr::mutate(contribution = probability * mean_cost)
stopifnot(abs(sum(paths$probability) - 1) < 1e-9)
from_event <- p_event * cost_event
from_nonevent <- p_nonevent * cost_nonevent
expected_cost <- from_event + from_nonevent
stopifnot(abs(sum(paths$contribution) - expected_cost) < 1e-9)

#%%@note 4. The bill for 400 operations, counted in patients
# Count patients on each path, then multiply by each path's mean cost.
# นับผู้ป่วยในแต่ละเส้นทาง แล้วคูณด้วยต้นทุนเฉลี่ยของเส้นทางนั้น
with_event <- ops * p_event
bill_event <- with_event * cost_event
bill_nonevent <- (ops - with_event) * cost_nonevent
total <- bill_event + bill_nonevent
stopifnot(abs(total / ops - expected_cost) < 1e-9)

#%%@note 5. Spread between patients versus uncertainty about the mean
# The SD of 36 patients' costs is the spread; the SE is how precisely the mean is known.
# SD ของต้นทุนผู้ป่วย 36 คนคือความผันแปรระหว่างคน ส่วน SE บอกว่ารู้ค่าเฉลี่ยได้แม่นเพียงใด
sd_cost <- 6000
n_event <- 36
se_count <- 2
se_cost <- sd_cost / sqrt(n_event)
cost_at <- function(cost) p_event * cost + (1 - p_event) * cost_nonevent
se_range <- cost_event + c(-se_count, se_count) * se_cost
sd_range <- cost_event + c(-se_count, se_count) * sd_cost

#%%@note 6. Display the contributions
# Plot each path's contribution to the expected cost; the script saves no file.
# แสดงส่วนที่แต่ละเส้นทางเพิ่มให้ต้นทุนคาดหมาย โดยไม่บันทึกไฟล์ภาพ
print(ggplot2::ggplot(paths, ggplot2::aes(x = path, y = contribution)) +
  ggplot2::geom_col() + ggplot2::labs(y = "Contribution to expected cost (CU/patient)"))

#%%@note 7. Numbers the article quotes
# Print the short boxes' lines in their reading order.
# พิมพ์ผลลัพธ์ตรงกับกล่องโค้ดสั้นตามลำดับในบทความ
cat("Path means (CU/patient; all):", big(path_means), ";", big(ops * path_means), "\n")
cat("Extra cost (CU/patient; all):", big(extra_cost), ";", big(ops * extra_cost), "\n")
cat("Typed (sum; CU/patient; all):", p_event + p_typed, ";", typed, ";", big(ops * typed), "\n")
cat("p_nonevent (derived); probabilities add to:", p_nonevent, ";", p_event + p_nonevent, "\n")
cat("Path contributions (CU/patient):", from_event, "+", from_nonevent, "\n")
cat("Expected cost (CU/patient):", expected_cost, "\n")
cat("Patients with / without a complication:", with_event, "/", ops - with_event, "\n")
cat("Bill by path (CU):", big(bill_event), "+", big(bill_nonevent), "=", big(total), "\n")
cat("Share of the bill from complications (%):", round(100 * bill_event / total, 2), "\n")
cat("SE (CU); mean +/- 2 SE; expected cost:", se_cost, ";", se_range, ";", cost_at(se_range), "\n")
cat("SD by mistake: mean +/- 2 SD; expected cost:", sd_range, ";", cost_at(sd_range), "\n")
ค่าใช้จ่ายของการผ่าตัด 400 ครั้ง นับเป็นจำนวนผู้ป่วยในแต่ละเส้นทาง
ดูกราฟด้านซ้าย ความกว้างของแต่ละพื้นที่คือสัดส่วนผู้ป่วย (0.25 และ 0.75) ความสูงคือต้นทุนเฉลี่ย พื้นที่จึงเป็นส่วนสมทบ คือ 3,000 และ 1,500 CU แท่งด้านขวาซึ่งใช้สเกลของตัวเอง ซ้อนสองพื้นที่นี้เป็นต้นทุนคาดหมาย 4,500 CU เมื่อเลื่อน p_event ความกว้างของสองพื้นที่จะเปลี่ยนพร้อมกัน เพราะทั้งสองกิ่งอ่านชื่อเดียวกัน เมื่อเปลี่ยนเป็น 0.70 ที่พิมพ์ไว้เอง ความกว้างรวมได้ 0.95 ต้นทุนคาดหมายจึงลดเหลือ 4,400 CU

แยกชนิดของความไม่แน่นอน

คำถามถัดไปของคณะกรรมการคือ 4,500 นั้นแน่นอนแค่ไหน การตอบต้องแยกการกระจายสองชนิดออกจากกัน ปีที่แล้วมีผู้ป่วย 36 คนที่มีภาวะแทรกซ้อน และต้นทุนของพวกเขาต่างกันมาก การรั่วของรอยต่อลำไส้อาจมีต้นทุนสูงกว่าแผลติดเชื้อเล็กน้อยมาก ความต่างระหว่างผู้ป่วยแต่ละคนนี้คือความผันแปร (variability) ซึ่งวัดด้วยส่วนเบี่ยงเบนมาตรฐาน (SD) ในที่นี้คือ 6,000 CU รอบค่าเฉลี่ย 12,000

ความผันแปรไม่ลดลงเมื่องานวิจัยรับผู้ป่วยมากขึ้น เพราะผู้ป่วยต่างกันจริง ความไม่แน่นอนของค่าเฉลี่ยเป็นอีกเรื่องหนึ่ง คือหลักฐานระบุค่าเฉลี่ยได้แม่นเพียงใด และงานวิจัยที่ใหญ่กว่าก็ระบุได้แม่นกว่า เมื่อผู้ป่วย 36 คนมีต้นทุนที่มี SD เท่ากับ 6,000 CU เราจะรู้ค่าเฉลี่ยได้แม่นในระดับราว 1,000 CU ซึ่งได้จาก 6,000 หารด้วยรากที่สองของ 36 ค่านี้เรียกว่าความคลาดเคลื่อนมาตรฐาน (SE) คือ SE = SD / √n สำหรับผู้ป่วย n คน และลดลงเมื่องานวิจัยใหญ่ขึ้น (Briggs และคณะ)

ช่องช่วงค่าในตารางบอกความไม่แน่นอนของค่าเฉลี่ย จึงต้องมาจาก SE การบวกและลบสองเท่าของ SE จากค่าเฉลี่ย คือ 2 × 1,000 = 2,000 CU ใกล้เคียงกับช่วงความเชื่อมั่นที่รายงานการศึกษาให้ไว้ ต้นทุนเฉลี่ยของผู้ป่วยที่มีภาวะแทรกซ้อนจึงอยู่ระหว่าง 12,000 − 2,000 = 10,000 ถึง 12,000 + 2,000 = 14,000 CU

การขยับข้อมูลนำเข้าทีละตัวไปตามช่วงค่าโดยให้ตัวอื่นคงที่ เรียกว่าการวิเคราะห์ความไวทีละข้อมูลนำเข้า (one-way sensitivity analysis) ในกรณีนี้ขยับเฉพาะ cost_event ส่วนเส้นทางที่ไม่มีภาวะแทรกซ้อนยังสมทบ 1,500 CU เท่าเดิม ต้นทุนคาดหมายจึงเป็น 0.25 × 10,000 + 1,500 = 4,000 CU ที่ปลายล่าง และ 0.25 × 14,000 + 1,500 = 5,000 CU ที่ปลายบน

ถ้าใช้สองเท่าของ SD (2 × 6,000) แทนโดยผิดพลาด จะได้ 12,000 ± 12,000 คือช่วง 0 ถึง 24,000 CU ต้นทุนคาดหมายจึงเป็น 0.25 × 0 + 1,500 = 1,500 ถึง 0.25 × 24,000 + 1,500 = 7,500 CU ช่วงนั้นบรรยายผู้ป่วยรายคน ไม่ใช่ค่าเฉลี่ยของพวกเขา จึงทำให้ 4,500 ดูไม่แน่นอนมากเกินจริง

ช่วงค่าบอกเพียงว่าค่าที่เป็นไปได้อยู่ตรงไหน บทความตอนหลังในชุดนี้ก้าวไปอีกขั้น คือให้ค่าแต่ละค่าในช่วงมีโอกาสของมันเอง ซึ่งเรียกว่าการแจกแจง (distribution) แล้วรันแบบจำลองซ้ำหลายครั้งด้วยค่าที่สุ่มขึ้น เมื่อหลักฐานไม่มี SD หรือ SE ให้ระบุข้อนี้ไว้ในตาราง ช่วงค่าที่ผู้วิเคราะห์เลือกใช้หลังจากนั้นคือค่าที่เลือกลองเอง หรือสถานการณ์สมมติ (scenario) ไม่ใช่หลักฐาน

ความไม่แน่นอนเชิงโครงสร้าง (structural uncertainty) คือความไม่แน่ใจว่าแบบจำลองแสดงเส้นทางของผู้ป่วยไว้เหมาะสมหรือไม่ แบบจำลองนี้หยุดที่การกลับบ้าน การกลับมานอนโรงพยาบาลซ้ำเพราะพบการรั่วภายหลังจึงไม่ถูกนับ การเลือกนี้เป็นสมมติฐาน (assumption) ให้ระบุในตารางและอธิบายว่าทำไมจึงสมเหตุสมผล การใส่ช่วงค่าให้มันไม่ได้ทำให้มันกลายเป็นความไม่แน่นอนของค่าเฉลี่ย

ตรวจส่วนเติมเต็ม หน่วย และทุกจุดที่ใช้ค่าร่วมกันหลังการแก้ไขแต่ละครั้ง เพื่อให้ตารางกับแบบจำลองไม่คลาดกัน (Eddy และคณะ)

ตรวจช่วงค่าด้วย R

โค้ด R uncertainty.R
# Spread between patients (SD) versus uncertainty about the mean (SE), in illustrative CU.
p_event <- 0.25
cost_event <- 12000
cost_nonevent <- 2000
sd_cost <- 6000
n_event <- 36
se_count <- 2
se_cost <- sd_cost / sqrt(n_event)
cost_at <- function(cost) p_event * cost + (1 - p_event) * cost_nonevent
se_range <- cost_event + c(-se_count, se_count) * se_cost
sd_range <- cost_event + c(-se_count, se_count) * sd_cost
cat("SE (CU); mean +/- 2 SE; expected cost:", se_cost, ";", se_range, ";", cost_at(se_range), "\n")
cat("SD by mistake: mean +/- 2 SD; expected cost:", sd_range, ";", cost_at(sd_range), "\n")
ผลลัพธ์จากการรัน uncertainty.log
SE (CU); mean +/- 2 SE; expected cost: 1000 ; 10000 14000 ; 4000 5000
SD by mistake: mean +/- 2 SD; expected cost: 0 24000 ; 1500 7500

ต้องใช้ R ≥ 4.5.2 · tibble 3.3.1 · dplyr 1.2.1 · ggplot2 4.0.3 · ใช้ได้กับ R 4.6.1 · ดาวน์โหลด auditable-model-inputs-parameter-tables.R

สคริปต์ฉบับเต็ม
# Auditable Model Inputs: Build a Parameter Table You Can Trace
# Shows how a parameter table settles what four numbers mean, then gives the expected cost.
# แสดงว่าตารางพารามิเตอร์ช่วยตัดสินความหมายของตัวเลขสี่ตัว และให้ต้นทุนคาดหมายต่อผู้ป่วยหนึ่งคน
# Requires: R >= 4.5.2
# Tested: works in R 4.6.1
# tibble 3.3.1 (stable)
# dplyr 1.2.1 (stable)
# ggplot2 4.0.3 (stable)
# install.packages(c("tibble", "dplyr", "ggplot2"))
# update.packages(oldPkgs = c("tibble", "dplyr", "ggplot2"))
# remotes::install_version("tibble", "3.3.1")
# remotes::install_version("dplyr", "1.2.1")
# remotes::install_version("ggplot2", "4.0.3")
library(tibble) # Write the parameter table as an explicit input table.
library(dplyr) # Calculate each path's contribution from the table.
library(ggplot2) # Display the contributions without saving files.
stopifnot(getRversion() >= "4.5.2")

#%%@note 1. The parameter table
# One row per input: the identifier, its value and what it means; the share without a complication
# is derived from p_event, never typed. All values are illustrative.
# หนึ่งแถวต่อข้อมูลนำเข้าหนึ่งค่า ระบุชื่อ ค่า และความหมาย ส่วนสัดส่วนที่ไม่มีภาวะแทรกซ้อนคำนวณจาก
# p_event เสมอ ไม่พิมพ์แยก ค่าทั้งหมดเป็นตัวอย่างสมมติ
p_event <- 0.25
cost_event <- 12000
cost_nonevent <- 2000
p_typed <- 0.70
ops <- 400
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")
p_nonevent <- 1 - p_event
parameters <- tibble::tibble(
  identifier = c("p_event", "cost_event", "cost_nonevent", "p_nonevent"),
  value = c(p_event, cost_event, cost_nonevent, p_nonevent),
  meaning = c("Risk of any complication, operation to discharge",
              "Mean total cost per patient with a complication (CU)",
              "Mean total cost per patient without a complication (CU)",
              "Derived: 1 - p_event")
)

#%%@note 2. Three readings of the same four cells
# The same numbers give three bills, depending on what 12000 means and whether the share is typed.
# ตัวเลขชุดเดียวกันให้ค่าใช้จ่ายสามแบบ ขึ้นกับว่า 12000 หมายถึงอะไร และสัดส่วนถูกพิมพ์แยกหรือไม่
path_means <- p_event * cost_event + (1 - p_event) * cost_nonevent
extra_cost <- cost_nonevent + p_event * cost_event
typed <- p_event * cost_event + p_typed * cost_nonevent

#%%@note 3. The expected cost from the table
# Weight each path's mean cost by its probability; a probability has no unit, so the unit stays CU.
# ถ่วงต้นทุนเฉลี่ยของแต่ละเส้นทางด้วยความน่าจะเป็น ความน่าจะเป็นไม่มีหน่วย ผลจึงเป็น CU ต่อผู้ป่วย
paths <- tibble::tibble(
  path = c("Complication", "No complication"),
  probability = c(p_event, p_nonevent),
  mean_cost = c(cost_event, cost_nonevent)
) |>
  dplyr::mutate(contribution = probability * mean_cost)
stopifnot(abs(sum(paths$probability) - 1) < 1e-9)
from_event <- p_event * cost_event
from_nonevent <- p_nonevent * cost_nonevent
expected_cost <- from_event + from_nonevent
stopifnot(abs(sum(paths$contribution) - expected_cost) < 1e-9)

#%%@note 4. The bill for 400 operations, counted in patients
# Count patients on each path, then multiply by each path's mean cost.
# นับผู้ป่วยในแต่ละเส้นทาง แล้วคูณด้วยต้นทุนเฉลี่ยของเส้นทางนั้น
with_event <- ops * p_event
bill_event <- with_event * cost_event
bill_nonevent <- (ops - with_event) * cost_nonevent
total <- bill_event + bill_nonevent
stopifnot(abs(total / ops - expected_cost) < 1e-9)

#%%@note 5. Spread between patients versus uncertainty about the mean
# The SD of 36 patients' costs is the spread; the SE is how precisely the mean is known.
# SD ของต้นทุนผู้ป่วย 36 คนคือความผันแปรระหว่างคน ส่วน SE บอกว่ารู้ค่าเฉลี่ยได้แม่นเพียงใด
sd_cost <- 6000
n_event <- 36
se_count <- 2
se_cost <- sd_cost / sqrt(n_event)
cost_at <- function(cost) p_event * cost + (1 - p_event) * cost_nonevent
se_range <- cost_event + c(-se_count, se_count) * se_cost
sd_range <- cost_event + c(-se_count, se_count) * sd_cost

#%%@note 6. Display the contributions
# Plot each path's contribution to the expected cost; the script saves no file.
# แสดงส่วนที่แต่ละเส้นทางเพิ่มให้ต้นทุนคาดหมาย โดยไม่บันทึกไฟล์ภาพ
print(ggplot2::ggplot(paths, ggplot2::aes(x = path, y = contribution)) +
  ggplot2::geom_col() + ggplot2::labs(y = "Contribution to expected cost (CU/patient)"))

#%%@note 7. Numbers the article quotes
# Print the short boxes' lines in their reading order.
# พิมพ์ผลลัพธ์ตรงกับกล่องโค้ดสั้นตามลำดับในบทความ
cat("Path means (CU/patient; all):", big(path_means), ";", big(ops * path_means), "\n")
cat("Extra cost (CU/patient; all):", big(extra_cost), ";", big(ops * extra_cost), "\n")
cat("Typed (sum; CU/patient; all):", p_event + p_typed, ";", typed, ";", big(ops * typed), "\n")
cat("p_nonevent (derived); probabilities add to:", p_nonevent, ";", p_event + p_nonevent, "\n")
cat("Path contributions (CU/patient):", from_event, "+", from_nonevent, "\n")
cat("Expected cost (CU/patient):", expected_cost, "\n")
cat("Patients with / without a complication:", with_event, "/", ops - with_event, "\n")
cat("Bill by path (CU):", big(bill_event), "+", big(bill_nonevent), "=", big(total), "\n")
cat("Share of the bill from complications (%):", round(100 * bill_event / total, 2), "\n")
cat("SE (CU); mean +/- 2 SE; expected cost:", se_cost, ";", se_range, ";", cost_at(se_range), "\n")
cat("SD by mistake: mean +/- 2 SD; expected cost:", sd_range, ";", cost_at(sd_range), "\n")
SE ให้ช่วงของค่าเฉลี่ย ส่วน SD ที่ใช้ผิดให้ช่วงของผู้ป่วยรายคน

คำศัพท์ที่ควรแยกให้ชัด

Estimand
estimand: ปริมาณที่แน่ชัดที่หลักฐานควรประมาณ เขียนเป็นคำ ได้แก่ เหตุการณ์ ประชากร สถานที่ และช่วงเวลา
Derived value
ค่าที่คำนวณได้: ปริมาณที่คำนวณจากข้อมูลนำเข้าที่มีชื่อ โดยบันทึกวิธีคำนวณไว้ เช่น p_nonevent
Conditional mean
ค่าเฉลี่ยแบบมีเงื่อนไข: ค่าเฉลี่ยเฉพาะผู้ป่วยในเส้นทางเดียว เช่น ผู้ที่มีภาวะแทรกซ้อน
Expected (unconditional) mean
ค่าเฉลี่ยแบบไม่มีเงื่อนไขหรือค่าคาดหมาย: ค่าเฉลี่ยของผู้ป่วยทุกคน คือค่าเฉลี่ยแบบมีเงื่อนไขแต่ละค่าถ่วงด้วยความน่าจะเป็นของมัน สำหรับต้นทุนเขียนว่า E[C]
SD and SE
SD คือการกระจายระหว่างผู้ป่วย ส่วน SE คือความไม่แน่นอนของค่าเฉลี่ย และลดลงเมื่องานวิจัยใหญ่ขึ้น
Range and distribution
ช่วงค่าและการแจกแจง: ค่าต่ำและค่าสูงที่เป็นไปได้ของข้อมูลนำเข้าที่ไม่แน่นอน ส่วนการแจกแจงบอกโอกาสของค่าแต่ละค่าด้วย
One-way sensitivity analysis
การวิเคราะห์ความไวทีละข้อมูลนำเข้า: การขยับข้อมูลนำเข้าทีละตัวไปตามช่วงค่าโดยให้ตัวอื่นคงที่ เพื่อดูว่าผลลัพธ์เปลี่ยนไปเท่าใด
Structural uncertainty and assumption
ความไม่แน่นอนเชิงโครงสร้างและสมมติฐาน: ความไม่แน่นอนว่าแบบจำลองแสดงเส้นทางผู้ป่วยไว้เหมาะสมหรือไม่ เช่น การหยุดที่การกลับบ้าน ส่วนสมมติฐานคือการเลือกที่ทำไว้

เชื่อมกับบทความพื้นฐาน

ทบทวนพื้นฐานทางคลินิกและเศรษฐศาสตร์ได้ที่ การหาพารามิเตอร์และแหล่งข้อมูลอ้างอิง และ การคิดต้นทุน มุมมอง และเวลา

ลองใช้ในแอป

เปิดแอปการประเมินทางเศรษฐศาสตร์ของเราเพื่อลองเปลี่ยนค่าข้อมูลนำเข้าและดูผลลัพธ์เปลี่ยนตาม ต้องเข้าสู่ระบบและใช้แผน Pro หรือ Max

แอปเริ่มจากค่าตัวอย่างของแอปเอง ตัวเลขในแอปจึงไม่ตรงกับบทความนี้

เอกสารอ้างอิง

  1. Andrew Briggs, Karl Claxton, Mark Sculpher. Decision Modelling for Health Economic Evaluation (Handbooks for Health Economic Evaluation). Oxford University Press; 2006. ISBN 9780198526629. https://openlibrary.org/isbn/9780198526629
  2. Briggs Andrew H., Weinstein Milton C., Fenwick Elisabeth A.L., Karnon Jonathan, Sculpher Mark J., Paltiel A. David. Model Parameter Estimation and Uncertainty: A Report of the ISPOR-SMDM Modeling Good Research Practices Task Force-6. Value in Health. 2012;15(6):835-842. doi:10.1016/j.jval.2012.04.014. https://doi.org/10.1016/j.jval.2012.04.014
  3. Eddy David M., Hollingworth William, Caro J. Jaime, Tsevat Joel, McDonald Kathryn M., Wong John B.. Model Transparency and Validation: A Report of the ISPOR-SMDM Modeling Good Research Practices Task Force-7. Value in Health. 2012;15(6):843-850. doi:10.1016/j.jval.2012.04.012. https://doi.org/10.1016/j.jval.2012.04.012

ลำดับการเรียนในชุดนี้

  1. คู่มือชุดบทความการสร้างแบบจำลองเศรษฐศาสตร์สุขภาพ: จากหลักฐานสู่การตัดสินใจ
  2. การเลือกแบบจำลองเพื่อการตัดสินใจ: ให้เส้นทางทางคลินิกกำหนดโครงสร้าง
  3. ข้อมูลนำเข้าของแบบจำลองที่ตรวจสอบได้: สร้างตารางพารามิเตอร์ที่สืบย้อนถึงที่มา (บทความนี้)
  4. การค้นหาข้อมูลนำเข้าของแบบจำลองจากวรรณกรรม: เลือกค่าประมาณให้ตรงกับคำถาม
  5. อัตรา ความน่าจะเป็น และความน่าจะเป็นของการเปลี่ยนสถานะ: คำนวณโดยไม่หลงหน่วยเวลา
  6. สร้างแผนภูมิต้นไม้การตัดสินใจทีละขั้น: ตามรอยผลลัพธ์ ต้นทุน และสุขภาพ
  7. สร้างแบบจำลองมาร์คอฟแบบกลุ่มประชากรทีละขั้น: ติดตามประชากรผ่านเวลา
  8. การวิเคราะห์ผลกระทบด้านงบประมาณในทางปฏิบัติ: นับคนให้ชัดก่อนคำนวณเงิน
  9. ICER และผลประโยชน์สุทธิในรูปตัวเงินเมื่อมีหลายทางเลือก: หาขอบเขตทางเลือกที่มีประสิทธิภาพ
  10. การวิเคราะห์ความไวแบบอาศัยความน่าจะเป็น: จากการแจกแจงพารามิเตอร์สู่ CEAC
  11. การประเมินความสมเหตุสมผลและตรวจสอบแบบจำลอง: ทดสอบคำถาม สมการ และโค้ด
  12. มูลค่าคาดหมายของข้อมูลสมบูรณ์: หลักฐานที่ดีขึ้นอาจมีมูลค่าเพียงใด

ประเด็นสำคัญ

  • เขียนข้อมูลนำเข้าแต่ละค่าเป็นหนึ่งแถว บอกว่าวัดอะไร ของใคร ช่วงเวลาใด หน่วยอะไร มาจากไหน และแบบจำลองใช้อย่างไร ตัวเลข 12,000 ตัวเดียวกันให้ 4,500 หรือ 5,000 CU ต่อผู้ป่วย จนกว่าแถวของมันจะบอกว่าเป็นแบบไหน
  • ให้ปริมาณที่ใช้ร่วมกันมีชื่อเดียว และคำนวณส่วนที่เหลือจากชื่อนั้น คือ p_nonevent = 1 − p_event = 0.75 ไม่ใช่ 0.70 ที่พิมพ์ไว้เอง
  • เติมช่วงค่าจาก SE ไม่ใช่ SD โดย SE 1,000 CU ให้ 4,000 ถึง 5,000 CU ต่อผู้ป่วย ส่วน SD จะให้ 1,500 ถึง 7,500
  • ระบุการเลือกเชิงโครงสร้างและสมมติฐาน เช่น การหยุดที่การกลับบ้าน แยกจากความไม่แน่นอนของค่าเฉลี่ย

อ่านต่อในวิกิ: การคัดเลือกพารามิเตอร์ บัญชีต้นทุนอ้างอิง และฐานข้อมูลค่าอรรถประโยชน์เมื่อข้อมูลในประเทศมีจำกัด · ต้นทุนของใคร ณ ปีใด: มุมมองการวิเคราะห์ ต้นทุนค่าเสียโอกาส การปรับค่าเงินเฟ้อ และการปรับลดค่าตามเวลา

0
ถึงนักอ่านชาวไทยและต่างชาติทำความเข้าใจบริบททางการแพทย์ของผมอ่านต่อ →ถึงนักอ่านชาวไทยและต่างชาติทำความเข้าใจเนื้อหาของผมที่นอกเหนือจากการแพทย์อ่านต่อ →

ความคิดเห็น

ยังไม่มีความคิดเห็น มาเป็นคนแรกกันเลย

เข้าสู่ระบบเพื่อแสดงความคิดเห็น