การวิเคราะห์ผลกระทบด้านงบประมาณในทางปฏิบัติ: นับคนให้ชัดก่อนคำนวณเงิน

Health EconomicsClinical Epidemiology ResearchMethodology and Research Design TH
การวิเคราะห์ผลกระทบด้านงบประมาณในทางปฏิบัติ: นับคนให้ชัดก่อนคำนวณเงิน
On this page

Read the English version

บทคัดย่อ

กองทุนสุขภาพของอำเภอแห่งหนึ่งต้องตัดสินใจว่าจะจ่ายค่าบริการวัดความดันโลหิตที่บ้านให้ผู้ป่วยหลังโรคหลอดเลือดสมองหรือไม่ บริการนี้คือเครื่องวัดความดันที่บ้านและพยาบาลโทรติดตามทุกเดือน มีต้นทุนมากกว่าการดูแลตามปกติปีละ 2,000 บาทต่อผู้ใช้หนึ่งคน วันนี้มีผู้ป่วยที่มีสิทธิ 500 คน คำตอบเร็วคือคูณคนด้วยราคาและจำนวนปี 500 × 2,000 × 3 = 3,000,000 บาท แต่คำตอบนี้ผิด เพราะกลุ่มคนเปลี่ยนไปทุกปี แต่ละปีมีผู้ป่วยรายใหม่ที่เข้าเกณฑ์ 100 คน มีคน 10% ของกลุ่มออกไปเมื่อสิ้นปี และสัดส่วนผู้ป่วยที่ใช้บริการเพิ่มจาก 20% เป็น 40% และ 60% การนับคนก่อนจึงได้ผู้ใช้บริการ 120, 256 และ 405.6 คน และเงินสดส่วนเพิ่ม 240,000, 512,000 และ 811,200 บาท รวม 1,563,200 บาทในสามปี การนับเงินสดส่วนเพิ่มของผู้จ่ายทีละปีแบบนี้เรียกว่าการวิเคราะห์ผลกระทบด้านงบประมาณ (budget impact analysis) บทความนี้คิดรายจ่ายของแต่ละปีทั้งเมื่อมีและไม่มีบริการ เก็บทุกจำนวนเป็นเงินสดในปีที่จ่ายจริง และตรวจว่าพยาบาลรับผู้ใช้บริการได้ครบตามที่งบประมาณสมมติไว้หรือไม่


อ่านภาพจากบนลงล่าง นับคนก่อนทีละปี แล้วจึงแปลงเป็นเงินสด ตัวเลขทุกตัวมีที่มาในหัวข้อด้านล่าง ภาพนี้จึงเป็นเส้นทางของบทความทั้งเรื่องในที่เดียว

กองทุนอำเภอเดียว ผู้ป่วย 500 คน และบริการปีละ 2,000 บาท

กองทุนสุขภาพของอำเภอแห่งหนึ่งในจังหวัดหนึ่งของไทยจ่ายค่าดูแลผู้ป่วยหลังโรคหลอดเลือดสมองในพื้นที่ คลินิกของอำเภอนับผู้ป่วยที่เข้าเกณฑ์ของบริการใหม่ได้ 500 คน บริการนี้คือการวัดความดันโลหิตที่บ้าน โดยมีเครื่องวัดความดันไว้ที่บ้านและพยาบาลโทรติดตามทุกเดือน ผู้ป่วยแต่ละคนที่ใช้บริการทำให้กองทุนจ่ายมากกว่าการดูแลตามปกติ ปีละ 2,000 บาท ในทุกปีที่ยังใช้บริการอยู่

คณะกรรมการกองทุนถามคำถามเดียว คือในสามปีข้างหน้า แต่ละปีต้องหาเงินสดเพิ่มเท่าไร คำตอบเร็วคือคูณจำนวนคนด้วยราคาและจำนวนปี 500 × 2,000 × 3 = 3,000,000 บาท บทความนี้จะแก้วิธีคิดแบบนี้ กลุ่มผู้ป่วยเปลี่ยนไปทุกปี และไม่ใช่ทุกคนจะเริ่มใช้บริการพร้อมกัน

การนับคนก่อนให้ผลรวม 1,563,200 บาทในสามปี ประมาณครึ่งหนึ่งของคำตอบเร็ว ตัวเลขทั้งหมดเป็นตัวอย่างสมมติเพื่อการสอน

ใครมีสิทธิ: กลุ่มที่มีคนเข้าและคนออก

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

ลองนึกถึงอ่างอาบน้ำ น้ำในอ่างคือทุกคนที่มีสิทธิ ณ ขณะหนึ่ง ก๊อกน้ำคือผู้ป่วยใหม่ 100 คนที่มีสิทธิในแต่ละปี และรูระบายคือคน 10% ของกลุ่มที่ออกไปเมื่อสิ้นปี ผู้ที่มีสิทธิ ณ ขณะหนึ่งเรียกว่ากลุ่มผู้มีสิทธิ (eligible stock) ส่วนผู้มาใหม่ในแต่ละปีเรียกว่าผู้มีสิทธิรายใหม่ (incident inflow)

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

ใครใช้บริการ: สัดส่วนที่ค่อย ๆ เพิ่ม

ไม่ใช่ทุกคนที่มีสิทธิจะใช้บริการตั้งแต่ปีแรก พยาบาลต้องได้รับการอบรม ต้องจัดหาเครื่องวัดความดัน และแพทย์ต้องเริ่มส่งผู้ป่วยเข้าบริการ กองทุนคาดว่าสัดส่วนผู้ป่วยที่ใช้บริการจะเป็น 20% ในปีที่ 1, 40% ในปีที่ 2 และ 60% ในปีที่ 3 เมื่อบริการเข้าที่เข้าทาง

สัดส่วนรายปีนี้เรียกว่าสัดส่วนผู้ใช้บริการ (uptake) ซึ่งเป็นสัดส่วนของทุกคนที่มีสิทธิในปีนั้น ไม่ใช่สัดส่วนของผู้มาใหม่ ในปีที่ 1 ผู้ป่วยใหม่ 100 คนเข้ามาร่วมกับ 500 คนเดิมเมื่อต้นปี ผู้มีสิทธิจึงเป็น 600 คน และ 600 × 0.20 = 120 คนใช้บริการ

ทุกคนที่ใช้บริการในปีหนึ่ง ไม่ว่าจะเริ่มในปีนี้หรือก่อนหน้า เรียกว่าจำนวนผู้ใช้บริการทั้งหมดในปี (treated prevalence) นี่คือจำนวนที่กองทุนต้องจ่าย เพราะ 2,000 บาทเกิดซ้ำกับผู้ใช้ทุกคนในทุกปี

ลำดับภายในหนึ่งปี

แต่ละปีมีสี่ขั้น คือเพิ่มผู้มีสิทธิใหม่เมื่อต้นปี นับผู้ใช้บริการ คิดต้นทุนของผู้ใช้คนละ 2,000 บาท แล้วจึงให้ 10% พ้นสิทธิออกจากกลุ่มเมื่อสิ้นปี ผู้ที่พ้นสิทธิยังได้ใช้บริการในปีนั้น จึงต้องนับก่อนที่จะออกไป

ลำดับนี้เปลี่ยนคำตอบได้ ในปีที่ 1 ผู้มีสิทธิ 600 คนรวม 60 คนที่จะพ้นสิทธิเมื่อสิ้นปีไว้ด้วย ถ้าให้ 60 คนนั้นออกจากกลุ่มก่อน จะนับได้เพียง 540 คน คือผู้ใช้ 540 × 0.20 = 108 คน และ 108 × 2,000 = 216,000 บาท แทนที่จะเป็น 120 × 2,000 = 240,000 บาท กองทุนจะขาดเงินเท่ากับต้นทุนของผู้ป่วยที่ใช้บริการจริง

ตัวอย่างคำนวณ: สามปีของคน แล้วจึงเป็นเงินสด

แต่ละขั้นคือหนึ่งปี ทำตามลำดับข้างต้น

  1. ปีที่ 1

    \[ (500 + 100) \times 0.20 = 120 \]

    ผู้ป่วยใหม่ 100 คนเข้ามาร่วมกับ 500 คนเมื่อต้นปี ผู้มีสิทธิจึงเป็น 600 คน และ 120 คนใช้บริการ กองทุนจ่าย 120 × 2,000 = 240,000 บาท เมื่อสิ้นปี 10% ของ 600 คนออกไป คือ 60 คน และเหลือ 540 คน

  2. ปีที่ 2

    \[ (540 + 100) \times 0.40 = 256 \]

    ผู้มีสิทธิจึงเป็น 640 คน และ 256 คนใช้บริการ กองทุนจ่าย 256 × 2,000 = 512,000 บาท เมื่อสิ้นปี 64 คนจาก 640 คนออกไป และเหลือ 576 คน

  3. ปีที่ 3

    \[ (576 + 100) \times 0.60 = 405.6 \]

    ผู้มีสิทธิจึงเป็น 676 คน และ 405.6 คนใช้บริการ กองทุนจ่าย 405.6 × 2,000 = 811,200 บาท ค่า 405.6 เป็นจำนวนคนคาดหมาย ซึ่งเป็นค่าเฉลี่ย จึงคงทศนิยมไว้ เมื่อสิ้นปี 10% ของ 676 คนออกไป คือ 67.6 คน และเหลือ 608.4 คน การนับสามปีจบที่ตรงนี้

  4. รวมทุกปี

    รวมสามปี 240,000 + 512,000 + 811,200 = 1,563,200 บาท ยอดสะสมคือ 240,000 บาท หลังปีที่ 1 และ 752,000 บาทหลังปีที่ 2

ผลลัพธ์: ในสามปี กองทุนต้องใช้เงินสดเพิ่ม 1,563,200 บาท ไม่ใช่ 3,000,000 บาทของคำตอบเร็ว คำตอบเร็วคิดต้นทุนของผู้ป่วยทั้ง 500 คนตั้งแต่วันแรก เหมือนกับว่าทุกคนใช้บริการในทุกปี

อ่านแท่งจากซ้ายไปขวา แต่ละแท่งคือทุกคนที่มีสิทธิในปีนั้น และส่วนสีน้ำเงินคือผู้ใช้บริการที่บ้าน 120, 256 และ 405.6 คน (เป็นจำนวนคาดหมาย จึงมีทศนิยมได้) ระหว่างแท่ง ลูกศรสีส้มคือ 10% ที่ออกไป ลูกศรสีดำคือผู้ที่อยู่ต่อ และผู้ป่วยใหม่ 100 คนเข้ามาที่ส่วนบนของแท่ง แท่งจึงสูงขึ้นจาก 600 เป็น 640 และ 676 ขณะที่ส่วนสีน้ำเงินเพิ่มเร็วกว่า
เริ่มที่แท่งซ้ายแล้วไล่ไปทางขวา ตัวเลขบนยอดแท่งคือยอดสะสม ส่วนบล็อกสีน้ำเงินเข้มแต่ละบล็อกคือเงินสดของปีนั้นเอง 240,000, 512,000 และ 811,200 บาท ซึ่งวางซ้อนบนบล็อกสีอ่อนที่ปีก่อนจ่ายไปแล้ว บล็อกใหญ่ขึ้นเพราะผู้ใช้เพิ่มจาก 120 เป็น 256 และ 405.6 คน ยอดรวมสามปีจึงถึง 1,563,200 บาท

ชื่อเรียก: การวิเคราะห์ผลกระทบด้านงบประมาณ

การนับเงินสดส่วนเพิ่มของผู้จ่ายรายเดียวทีละปีแบบนี้เรียกว่าการวิเคราะห์ผลกระทบด้านงบประมาณ (budget impact analysis) ผู้จ่ายคือผู้ที่จ่ายค่าดูแล ในที่นี้คือกองทุนของอำเภอ การวิเคราะห์นี้ถามว่าผู้จ่ายจ่ายไหวหรือไม่ในแต่ละปี ไม่ได้ถามว่าการเปลี่ยนแปลงนั้นคุ้มค่าหรือไม่ (Sullivan and colleagues)

เขียนเป็นสัญลักษณ์ได้ว่า ให้ $E_y$ แทนกลุ่มผู้มีสิทธิในปี $y$ หลังผู้มาใหม่เข้ามาแล้ว $I$ แทนผู้มีสิทธิรายใหม่ (100) $x$ แทนสัดส่วนที่ออกเมื่อสิ้นปี (0.10) $u_y$ แทนสัดส่วนผู้ใช้บริการของปีนั้น และ $c$ แทนต้นทุนส่วนเพิ่มต่อผู้ใช้ (2,000 บาท) ปีที่ 1 เริ่มจากผู้มีสิทธิ 500 คนในปัจจุบัน จึงได้ $E_1 = 500 + I$ ตั้งแต่ปีที่ 2 เป็นต้นไป $E_y = E_{y-1}(1-x) + I$ เงินสดส่วนเพิ่มของปีคือ $B_y = E_y u_y c$ และยอดรวมคือผลบวกของ $B_y$ ทุกปี

ตรวจจำนวนคนด้วย R

โค้ด R population.R
# Illustrative district: stroke survivors who qualify for a home blood-pressure service.
# Writes: nothing to disk; the results print in the console.
prevalent <- 500
annual_incident <- 100
exit_fraction <- 0.10
uptake <- c(0.20,0.40,0.60)
eligible <- prevalent+annual_incident
for (year in 2:length(uptake)) eligible[year] <- eligible[year-1]*(1-exit_fraction)+annual_incident
treated <- eligible*uptake
cat("Year order:",seq_along(uptake),"\n")
cat("Eligible at year start (people):",eligible,"\n")
cat("Treated prevalence (people):",treated,"\n")
cat("Year-end exits (people):",eligible*exit_fraction,"\n")
cat("Remaining stock (people):",eligible*(1-exit_fraction),"\n")
ผลลัพธ์จากการรัน population.log
Year order: 1 2 3
Eligible at year start (people): 600 640 676
Treated prevalence (people): 120 256 405.6
Year-end exits (people): 60 64 67.6
Remaining stock (people): 540 576 608.4

ต้องใช้ R ≥ 4.5.2 · tibble 3.3.1 · dplyr 1.2.1 · ggplot2 4.0.3 · ใช้ได้กับ R 4.6.1 · ดาวน์โหลด budget-impact-analysis-in-practice.R

สคริปต์ฉบับเต็ม
# Budget Impact Analysis in Practice: Count People Before Counting Money
# Follow one district's stroke survivors from eligible people to the extra cash the fund pays.
# ติดตามผู้ป่วยหลังโรคหลอดเลือดสมองของอำเภอหนึ่ง จากผู้มีสิทธิจนถึงเงินสดส่วนเพิ่มที่กองทุนจ่าย
# 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 years as an explicit table.
library(dplyr)  # Calculate people and money for each year.
library(ggplot2)  # Display the yearly cash without saving files.
stopifnot(getRversion() >= "4.5.2")

#%%@note 1. Define the illustrative inputs
# Every input is synthetic and written out once, in the order the article uses it.
# ค่าตั้งต้นทั้งหมดเป็นข้อมูลสมมติ เขียนไว้ครั้งเดียวตามลำดับที่บทความใช้
prevalent <- 500
annual_incident <- 100
exit_fraction <- 0.10
uptake <- c(0.20,0.40,0.60)
annual_incremental_cost <- 2000
current_care_cost <- 4500
nurse_places <- 300
discount_rate <- 0.03
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")

#%%@note 2. Follow the eligible people year by year
# New survivors join at the year start, the year's users are counted, then exits leave at year end.
# ผู้มีสิทธิใหม่เข้าต้นปี นับผู้ใช้บริการของปีนั้น แล้วผู้ที่ออกจึงออกเมื่อสิ้นปี
stock <- rep(prevalent + annual_incident, length(uptake))
for (year_index in 2:length(uptake)) {
  stock[year_index] <- stock[year_index - 1] * (1 - exit_fraction) + annual_incident
}
eligible <- c(600,640,676)
stopifnot(all(abs(stock - eligible) < 1e-9))
years <- tibble::tibble(year = seq_along(uptake), eligible = stock, uptake = uptake) |>
  dplyr::mutate(treated = eligible * uptake, exits = eligible * exit_fraction,
    remaining = eligible * (1 - exit_fraction),
    annual_budget = treated * annual_incremental_cost,
    cumulative_budget = cumsum(annual_budget))
stopifnot(all(abs(years$exits + years$remaining - years$eligible) < 1e-9))
treated <- years$treated
budget <- years$annual_budget

#%%@note 3. Price the two scenarios
# Everyone costs current care in both; only the service users add the extra cost.
# ทุกคนมีต้นทุนการดูแลเดิมในทั้งสองสถานการณ์ เฉพาะผู้ใช้บริการที่มีต้นทุนเพิ่ม
current <- eligible * current_care_cost
new <- (eligible - treated) * current_care_cost +
  treated * (current_care_cost + annual_incremental_cost)
stopifnot(all(abs(new - current - budget) < 1e-6))

#%%@note 4. Check timing, rounding and the nurse places
# Wrong timing and early rounding change the cash; the nurse places cap year 3; uptake moves 0.10.
# ลำดับเวลาที่ผิดและการปัดเศษก่อนเวลาเปลี่ยนเงินสด จำนวนผู้ใช้ที่พยาบาลรับได้จำกัดปีที่ 3
exits_first <- treated[1] * c(1, 1 - exit_fraction) * annual_incremental_cost
rounded <- c(treated[3], round(treated[3]))
today_value <- 1834 / (1 + discount_rate)^3
capped <- pmin(treated, nurse_places)
shift <- 0.10 * sum(eligible)
users <- sum(eligible * uptake) + c(0, -1, 1) * shift

#%%@note 5. Display the model
# Plot the yearly extra cash; the script does not save a graphics file.
# แสดงเงินสดส่วนเพิ่มรายปี โดยไม่บันทึกไฟล์ภาพ
print(ggplot2::ggplot(years, ggplot2::aes(x = year, y = annual_budget)) +
  ggplot2::geom_col() + ggplot2::labs(x = "Year", y = "Extra cash in the year (THB)"))

#%%@note 6. Numbers the article quotes
# Print exactly the short boxes in their reading order.
# พิมพ์ผลลัพธ์ตรงกับกล่องตัวอย่างตามลำดับในบทความ
cat("Year order:",seq_along(uptake),"\n")
cat("Eligible at year start (people):",eligible,"\n")
cat("Treated prevalence (people):",treated,"\n")
cat("Year-end exits (people):",eligible*exit_fraction,"\n")
cat("Remaining stock (people):",eligible*(1-exit_fraction),"\n")
cat("Annual incremental budget (THB):",big(budget),"\n")
cat("Cumulative incremental budget (THB):",big(cumsum(budget)),"\n")
cat("Shortcut, 500 x 2,000 x 3 years (THB):",big(prevalent*annual_incremental_cost*3),"\n")
cat("Cost per person (THB):",big(current_care_cost+c(0,annual_incremental_cost)),"\n")
cat("Current-care scenario (THB):",big(current),"\n")
cat("New scenario (THB):",big(new),"\n")
cat("New minus current (THB):",big(new-current),"\n")
cat("Year 1 users, exits counted after and before:",treated[1]*c(1,1-exit_fraction),"\n")
cat("Year 1, exits counted after and before (THB):",big(exits_first),"\n")
cat("Year 3 people, expected and rounded:",rounded,"\n")
cat("Year 3 budget, expected and rounded (THB):",big(rounded*annual_incremental_cost),"\n")
cat("Yearly rate back to today (%):",100*discount_rate,"\n")
cat("1,834 THB due in year 3, worth today (THB):",big(today_value),"\n")
cat("Year 3 wanted, offered, waiting:",treated[3],nurse_places,treated[3]-nurse_places,"\n")
cat("Year 3 and three years within the places (THB):",
  big(c(capped[3],sum(capped))*annual_incremental_cost),"\n")
cat("Users over three years, shift of 0.10:",users[1],shift,"\n")
cat("Users, every share 0.10 lower and higher:",users[2:3],"\n")
cat("Three years, every share 0.10 lower and higher (THB):",
  big(users[2:3]*annual_incremental_cost),"\n")
สังเกตสองบรรทัดแรก ผู้มีสิทธิ 600, 640 และ 676 คน และผู้ใช้บริการ 120, 256 และ 405.6 คน สองบรรทัดสุดท้ายคือผู้ที่ออกและผู้ที่อยู่ต่อเมื่อสิ้นแต่ละปี

ตรวจเงินสดด้วย R

โค้ด R budget.R
# Illustrative district: the extra cash the fund pays each year for the home service.
# Writes: nothing to disk; the results print in the console.
prevalent <- 500
annual_incident <- 100
exit_fraction <- 0.10
uptake <- c(0.20,0.40,0.60)
annual_incremental_cost <- 2000
eligible <- prevalent+annual_incident
for (year in 2:length(uptake)) eligible[year] <- eligible[year-1]*(1-exit_fraction)+annual_incident
budget <- eligible*uptake*annual_incremental_cost
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")
cat("Annual incremental budget (THB):",big(budget),"\n")
cat("Cumulative incremental budget (THB):",big(cumsum(budget)),"\n")
cat("Shortcut, 500 x 2,000 x 3 years (THB):",big(prevalent*annual_incremental_cost*3),"\n")
ผลลัพธ์จากการรัน budget.log
Annual incremental budget (THB): 240,000 512,000 811,200
Cumulative incremental budget (THB): 240,000 752,000 1,563,200
Shortcut, 500 x 2,000 x 3 years (THB): 3,000,000

ต้องใช้ R ≥ 4.5.2 · tibble 3.3.1 · dplyr 1.2.1 · ggplot2 4.0.3 · ใช้ได้กับ R 4.6.1 · ดาวน์โหลด budget-impact-analysis-in-practice.R

สคริปต์ฉบับเต็ม
# Budget Impact Analysis in Practice: Count People Before Counting Money
# Follow one district's stroke survivors from eligible people to the extra cash the fund pays.
# ติดตามผู้ป่วยหลังโรคหลอดเลือดสมองของอำเภอหนึ่ง จากผู้มีสิทธิจนถึงเงินสดส่วนเพิ่มที่กองทุนจ่าย
# 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 years as an explicit table.
library(dplyr)  # Calculate people and money for each year.
library(ggplot2)  # Display the yearly cash without saving files.
stopifnot(getRversion() >= "4.5.2")

#%%@note 1. Define the illustrative inputs
# Every input is synthetic and written out once, in the order the article uses it.
# ค่าตั้งต้นทั้งหมดเป็นข้อมูลสมมติ เขียนไว้ครั้งเดียวตามลำดับที่บทความใช้
prevalent <- 500
annual_incident <- 100
exit_fraction <- 0.10
uptake <- c(0.20,0.40,0.60)
annual_incremental_cost <- 2000
current_care_cost <- 4500
nurse_places <- 300
discount_rate <- 0.03
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")

#%%@note 2. Follow the eligible people year by year
# New survivors join at the year start, the year's users are counted, then exits leave at year end.
# ผู้มีสิทธิใหม่เข้าต้นปี นับผู้ใช้บริการของปีนั้น แล้วผู้ที่ออกจึงออกเมื่อสิ้นปี
stock <- rep(prevalent + annual_incident, length(uptake))
for (year_index in 2:length(uptake)) {
  stock[year_index] <- stock[year_index - 1] * (1 - exit_fraction) + annual_incident
}
eligible <- c(600,640,676)
stopifnot(all(abs(stock - eligible) < 1e-9))
years <- tibble::tibble(year = seq_along(uptake), eligible = stock, uptake = uptake) |>
  dplyr::mutate(treated = eligible * uptake, exits = eligible * exit_fraction,
    remaining = eligible * (1 - exit_fraction),
    annual_budget = treated * annual_incremental_cost,
    cumulative_budget = cumsum(annual_budget))
stopifnot(all(abs(years$exits + years$remaining - years$eligible) < 1e-9))
treated <- years$treated
budget <- years$annual_budget

#%%@note 3. Price the two scenarios
# Everyone costs current care in both; only the service users add the extra cost.
# ทุกคนมีต้นทุนการดูแลเดิมในทั้งสองสถานการณ์ เฉพาะผู้ใช้บริการที่มีต้นทุนเพิ่ม
current <- eligible * current_care_cost
new <- (eligible - treated) * current_care_cost +
  treated * (current_care_cost + annual_incremental_cost)
stopifnot(all(abs(new - current - budget) < 1e-6))

#%%@note 4. Check timing, rounding and the nurse places
# Wrong timing and early rounding change the cash; the nurse places cap year 3; uptake moves 0.10.
# ลำดับเวลาที่ผิดและการปัดเศษก่อนเวลาเปลี่ยนเงินสด จำนวนผู้ใช้ที่พยาบาลรับได้จำกัดปีที่ 3
exits_first <- treated[1] * c(1, 1 - exit_fraction) * annual_incremental_cost
rounded <- c(treated[3], round(treated[3]))
today_value <- 1834 / (1 + discount_rate)^3
capped <- pmin(treated, nurse_places)
shift <- 0.10 * sum(eligible)
users <- sum(eligible * uptake) + c(0, -1, 1) * shift

#%%@note 5. Display the model
# Plot the yearly extra cash; the script does not save a graphics file.
# แสดงเงินสดส่วนเพิ่มรายปี โดยไม่บันทึกไฟล์ภาพ
print(ggplot2::ggplot(years, ggplot2::aes(x = year, y = annual_budget)) +
  ggplot2::geom_col() + ggplot2::labs(x = "Year", y = "Extra cash in the year (THB)"))

#%%@note 6. Numbers the article quotes
# Print exactly the short boxes in their reading order.
# พิมพ์ผลลัพธ์ตรงกับกล่องตัวอย่างตามลำดับในบทความ
cat("Year order:",seq_along(uptake),"\n")
cat("Eligible at year start (people):",eligible,"\n")
cat("Treated prevalence (people):",treated,"\n")
cat("Year-end exits (people):",eligible*exit_fraction,"\n")
cat("Remaining stock (people):",eligible*(1-exit_fraction),"\n")
cat("Annual incremental budget (THB):",big(budget),"\n")
cat("Cumulative incremental budget (THB):",big(cumsum(budget)),"\n")
cat("Shortcut, 500 x 2,000 x 3 years (THB):",big(prevalent*annual_incremental_cost*3),"\n")
cat("Cost per person (THB):",big(current_care_cost+c(0,annual_incremental_cost)),"\n")
cat("Current-care scenario (THB):",big(current),"\n")
cat("New scenario (THB):",big(new),"\n")
cat("New minus current (THB):",big(new-current),"\n")
cat("Year 1 users, exits counted after and before:",treated[1]*c(1,1-exit_fraction),"\n")
cat("Year 1, exits counted after and before (THB):",big(exits_first),"\n")
cat("Year 3 people, expected and rounded:",rounded,"\n")
cat("Year 3 budget, expected and rounded (THB):",big(rounded*annual_incremental_cost),"\n")
cat("Yearly rate back to today (%):",100*discount_rate,"\n")
cat("1,834 THB due in year 3, worth today (THB):",big(today_value),"\n")
cat("Year 3 wanted, offered, waiting:",treated[3],nurse_places,treated[3]-nurse_places,"\n")
cat("Year 3 and three years within the places (THB):",
  big(c(capped[3],sum(capped))*annual_incremental_cost),"\n")
cat("Users over three years, shift of 0.10:",users[1],shift,"\n")
cat("Users, every share 0.10 lower and higher:",users[2:3],"\n")
cat("Three years, every share 0.10 lower and higher (THB):",
  big(users[2:3]*annual_incremental_cost),"\n")
สังเกตว่ายอดรายปีรวมกันได้ 1,563,200 บาทในบรรทัดที่สอง ส่วนคำตอบเร็วในบรรทัดสุดท้ายคือ 3,000,000 บาท
ตัวควบคุมเริ่มที่ค่าของอำเภอ คือผู้มีสิทธิอยู่แล้ว 500 คน ผู้มีสิทธิใหม่ปีละ 100 คน สัดส่วนที่ออก 0.10 สัดส่วนผู้ใช้บริการ 0.20, 0.40 และ 0.60 และ 2,000 บาทต่อผู้ใช้ ทางซ้าย แท่งสีเทาคือทุกคนที่มีสิทธิ ส่วนสีน้ำเงินคือผู้ใช้บริการ ลองเพิ่มสัดส่วนผู้ใช้บริการในปีที่ 3 จะมีเพียงแท่งสีส้มแท่งสุดท้ายและปลายเส้นยอดสะสมสีเขียวที่สูงขึ้น ลองเลื่อนสัดส่วนที่ออกให้สูงกว่า 0.10 มาก จนแต่ละปีมีคนออกมากกว่า 100 คนที่เข้ามา แท่งสีเทาจะเตี้ยลงทุกปี

เมื่อมีและไม่มีบริการ: ผลต่างเท่านั้นคืองบประมาณ

กองทุนจ่ายค่าการดูแลตามปกติอยู่แล้ว คือการมาตรวจที่คลินิกและยาลดความดัน สมมติว่าการดูแลตามปกติมีต้นทุน 4,500 บาท ต่อผู้ป่วยหนึ่งคนต่อปี ผู้ป่วยที่ใช้บริการที่บ้านจึงมีต้นทุน 4,500 + 2,000 = 6,500 บาท

ทีนี้ลองคิดรายจ่ายของปีเดียวกันสองแบบ เมื่อไม่มีบริการ ผู้ป่วยทั้ง 600 คนได้รับการดูแลตามปกติ และปีที่ 1 มีต้นทุน 600 × 4,500 = 2,700,000 บาท เมื่อมีบริการ ผู้ป่วยทุกคนยังมีต้นทุนการดูแลตามปกติ 4,500 บาท และผู้ใช้ 120 คนเพิ่มคนละ 2,000 บาท ดังนั้นรายจ่ายเมื่อมีบริการเป็น 2,700,000 + 240,000 = 2,940,000 บาท ผลต่างจึงเท่ากับ 240,000 บาทที่นับไว้ข้างต้นพอดี

ปีที่ 2 และ 3 คิดแบบเดียวกัน เมื่อไม่มีบริการ ปีที่ 2 มีต้นทุน 640 × 4,500 = 2,880,000 และปีที่ 3 มีต้นทุน 676 × 4,500 = 3,042,000 บาท เมื่อมีบริการจะเพิ่มอีก 512,000 และ 811,200 บาท ต้นทุนการดูแลตามปกติหักล้างกันเพราะอยู่ทั้งสองฝั่ง

สองแบบนี้เรียกว่าสถานการณ์ปัจจุบัน (current scenario) และสถานการณ์ใหม่ (new scenario) ผลกระทบด้านงบประมาณคือสถานการณ์ใหม่ลบสถานการณ์ปัจจุบันทีละปี อ่านตารางทีละแถว เงินสดส่วนเพิ่มของปีนั้นคือคอลัมน์มีบริการลบคอลัมน์ไม่มีบริการ

รายจ่ายเมื่อมีและไม่มีบริการ เป็นเงินสดในปีที่จ่าย

ปีผู้ใช้บริการไม่มีบริการ (บาท)มีบริการ (บาท)เงินสดส่วนเพิ่มในปีนั้น (บาท)ยอดสะสม (บาท)
11202,700,0002,940,000240,000240,000
22562,880,0003,392,000512,000752,000
3405.63,042,0003,853,200811,2001,563,200

ตรวจสองสถานการณ์ด้วย R

โค้ด R scenarios.R
# Illustrative district: total spending with and without the home service, year by year.
# Writes: nothing to disk; the results print in the console.
eligible <- c(600,640,676)
uptake <- c(0.20,0.40,0.60)
annual_incremental_cost <- 2000
current_care_cost <- 4500
treated <- eligible*uptake
current <- eligible*current_care_cost
new <- (eligible-treated)*current_care_cost+treated*(current_care_cost+annual_incremental_cost)
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")
cat("Cost per person (THB):",big(current_care_cost+c(0,annual_incremental_cost)),"\n")
cat("Current-care scenario (THB):",big(current),"\n")
cat("New scenario (THB):",big(new),"\n")
cat("New minus current (THB):",big(new-current),"\n")
ผลลัพธ์จากการรัน scenarios.log
Cost per person (THB): 4,500 6,500
Current-care scenario (THB): 2,700,000 2,880,000 3,042,000
New scenario (THB): 2,940,000 3,392,000 3,853,200
New minus current (THB): 240,000 512,000 811,200

ต้องใช้ R ≥ 4.5.2 · tibble 3.3.1 · dplyr 1.2.1 · ggplot2 4.0.3 · ใช้ได้กับ R 4.6.1 · ดาวน์โหลด budget-impact-analysis-in-practice.R

สคริปต์ฉบับเต็ม
# Budget Impact Analysis in Practice: Count People Before Counting Money
# Follow one district's stroke survivors from eligible people to the extra cash the fund pays.
# ติดตามผู้ป่วยหลังโรคหลอดเลือดสมองของอำเภอหนึ่ง จากผู้มีสิทธิจนถึงเงินสดส่วนเพิ่มที่กองทุนจ่าย
# 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 years as an explicit table.
library(dplyr)  # Calculate people and money for each year.
library(ggplot2)  # Display the yearly cash without saving files.
stopifnot(getRversion() >= "4.5.2")

#%%@note 1. Define the illustrative inputs
# Every input is synthetic and written out once, in the order the article uses it.
# ค่าตั้งต้นทั้งหมดเป็นข้อมูลสมมติ เขียนไว้ครั้งเดียวตามลำดับที่บทความใช้
prevalent <- 500
annual_incident <- 100
exit_fraction <- 0.10
uptake <- c(0.20,0.40,0.60)
annual_incremental_cost <- 2000
current_care_cost <- 4500
nurse_places <- 300
discount_rate <- 0.03
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")

#%%@note 2. Follow the eligible people year by year
# New survivors join at the year start, the year's users are counted, then exits leave at year end.
# ผู้มีสิทธิใหม่เข้าต้นปี นับผู้ใช้บริการของปีนั้น แล้วผู้ที่ออกจึงออกเมื่อสิ้นปี
stock <- rep(prevalent + annual_incident, length(uptake))
for (year_index in 2:length(uptake)) {
  stock[year_index] <- stock[year_index - 1] * (1 - exit_fraction) + annual_incident
}
eligible <- c(600,640,676)
stopifnot(all(abs(stock - eligible) < 1e-9))
years <- tibble::tibble(year = seq_along(uptake), eligible = stock, uptake = uptake) |>
  dplyr::mutate(treated = eligible * uptake, exits = eligible * exit_fraction,
    remaining = eligible * (1 - exit_fraction),
    annual_budget = treated * annual_incremental_cost,
    cumulative_budget = cumsum(annual_budget))
stopifnot(all(abs(years$exits + years$remaining - years$eligible) < 1e-9))
treated <- years$treated
budget <- years$annual_budget

#%%@note 3. Price the two scenarios
# Everyone costs current care in both; only the service users add the extra cost.
# ทุกคนมีต้นทุนการดูแลเดิมในทั้งสองสถานการณ์ เฉพาะผู้ใช้บริการที่มีต้นทุนเพิ่ม
current <- eligible * current_care_cost
new <- (eligible - treated) * current_care_cost +
  treated * (current_care_cost + annual_incremental_cost)
stopifnot(all(abs(new - current - budget) < 1e-6))

#%%@note 4. Check timing, rounding and the nurse places
# Wrong timing and early rounding change the cash; the nurse places cap year 3; uptake moves 0.10.
# ลำดับเวลาที่ผิดและการปัดเศษก่อนเวลาเปลี่ยนเงินสด จำนวนผู้ใช้ที่พยาบาลรับได้จำกัดปีที่ 3
exits_first <- treated[1] * c(1, 1 - exit_fraction) * annual_incremental_cost
rounded <- c(treated[3], round(treated[3]))
today_value <- 1834 / (1 + discount_rate)^3
capped <- pmin(treated, nurse_places)
shift <- 0.10 * sum(eligible)
users <- sum(eligible * uptake) + c(0, -1, 1) * shift

#%%@note 5. Display the model
# Plot the yearly extra cash; the script does not save a graphics file.
# แสดงเงินสดส่วนเพิ่มรายปี โดยไม่บันทึกไฟล์ภาพ
print(ggplot2::ggplot(years, ggplot2::aes(x = year, y = annual_budget)) +
  ggplot2::geom_col() + ggplot2::labs(x = "Year", y = "Extra cash in the year (THB)"))

#%%@note 6. Numbers the article quotes
# Print exactly the short boxes in their reading order.
# พิมพ์ผลลัพธ์ตรงกับกล่องตัวอย่างตามลำดับในบทความ
cat("Year order:",seq_along(uptake),"\n")
cat("Eligible at year start (people):",eligible,"\n")
cat("Treated prevalence (people):",treated,"\n")
cat("Year-end exits (people):",eligible*exit_fraction,"\n")
cat("Remaining stock (people):",eligible*(1-exit_fraction),"\n")
cat("Annual incremental budget (THB):",big(budget),"\n")
cat("Cumulative incremental budget (THB):",big(cumsum(budget)),"\n")
cat("Shortcut, 500 x 2,000 x 3 years (THB):",big(prevalent*annual_incremental_cost*3),"\n")
cat("Cost per person (THB):",big(current_care_cost+c(0,annual_incremental_cost)),"\n")
cat("Current-care scenario (THB):",big(current),"\n")
cat("New scenario (THB):",big(new),"\n")
cat("New minus current (THB):",big(new-current),"\n")
cat("Year 1 users, exits counted after and before:",treated[1]*c(1,1-exit_fraction),"\n")
cat("Year 1, exits counted after and before (THB):",big(exits_first),"\n")
cat("Year 3 people, expected and rounded:",rounded,"\n")
cat("Year 3 budget, expected and rounded (THB):",big(rounded*annual_incremental_cost),"\n")
cat("Yearly rate back to today (%):",100*discount_rate,"\n")
cat("1,834 THB due in year 3, worth today (THB):",big(today_value),"\n")
cat("Year 3 wanted, offered, waiting:",treated[3],nurse_places,treated[3]-nurse_places,"\n")
cat("Year 3 and three years within the places (THB):",
  big(c(capped[3],sum(capped))*annual_incremental_cost),"\n")
cat("Users over three years, shift of 0.10:",users[1],shift,"\n")
cat("Users, every share 0.10 lower and higher:",users[2:3],"\n")
cat("Three years, every share 0.10 lower and higher (THB):",
  big(users[2:3]*annual_incremental_cost),"\n")
สังเกตว่าบรรทัดสุดท้าย คือสถานการณ์ใหม่ลบสถานการณ์ปัจจุบัน ซ้ำกับเงินสดส่วนเพิ่มรายปี เพราะต้นทุนการดูแลตามปกติ 4,500 บาทหักล้างกันไปแล้ว

จ่ายให้ผู้ใช้ทุกคนทุกปี และคงทศนิยมไว้

ค่า 2,000 บาทเกิดซ้ำ เพราะค่าบริการเครื่องวัดความดันและการโทรของพยาบาลทุกเดือนเกิดขึ้นทุกปีที่ผู้ป่วยยังใช้บริการ เงินสดของแต่ละปีจึงครอบคลุมผู้ใช้ทุกคนในปีนั้น ไม่ใช่เฉพาะผู้มาใหม่หรือส่วนที่เพิ่มจากปีก่อน ในปีที่ 3 กองทุนจ่ายให้ผู้ใช้ 405.6 คน ซึ่งรวมผู้ที่เริ่มในปีที่ 1 และ 2 แล้วยังใช้ต่อด้วย

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

ค่า 405.6 เป็นจำนวนคาดหมาย คือค่าเฉลี่ยจากความเป็นไปได้หลายแบบของปีนั้น จึงไม่จำเป็นต้องเป็นจำนวนเต็ม ให้คงค่านี้ไว้จนถึงขั้นสุดท้าย ถ้าปัดเป็น 406 ก่อน ต้นทุนจะเป็น 812,000 บาท แทนที่จะเป็น 811,200 บาท ซึ่งเป็นความต่างที่ไม่ได้มาจากข้อเท็จจริงใดของผู้ป่วยเลย

ตรวจลำดับเวลาและการปัดเศษด้วย R

โค้ด R timing.R
# Illustrative district: what changes if exits leave too early or people are rounded too soon.
# Writes: nothing to disk; the results print in the console.
eligible <- c(600,640,676)
uptake <- c(0.20,0.40,0.60)
annual_incremental_cost <- 2000
exit_fraction <- 0.10
treated <- eligible*uptake
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")
cat("Year 1 users, exits counted after and before:",treated[1]*c(1,1-exit_fraction),"\n")
cat("Year 1, exits counted after and before (THB):",
  big(treated[1]*c(1,1-exit_fraction)*annual_incremental_cost),"\n")
cat("Year 3 people, expected and rounded:",treated[3],round(treated[3]),"\n")
cat("Year 3 budget, expected and rounded (THB):",
  big(c(treated[3],round(treated[3]))*annual_incremental_cost),"\n")
ผลลัพธ์จากการรัน timing.log
Year 1 users, exits counted after and before: 120 108
Year 1, exits counted after and before (THB): 240,000 216,000
Year 3 people, expected and rounded: 405.6 406
Year 3 budget, expected and rounded (THB): 811,200 812,000

ต้องใช้ R ≥ 4.5.2 · tibble 3.3.1 · dplyr 1.2.1 · ggplot2 4.0.3 · ใช้ได้กับ R 4.6.1 · ดาวน์โหลด budget-impact-analysis-in-practice.R

สคริปต์ฉบับเต็ม
# Budget Impact Analysis in Practice: Count People Before Counting Money
# Follow one district's stroke survivors from eligible people to the extra cash the fund pays.
# ติดตามผู้ป่วยหลังโรคหลอดเลือดสมองของอำเภอหนึ่ง จากผู้มีสิทธิจนถึงเงินสดส่วนเพิ่มที่กองทุนจ่าย
# 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 years as an explicit table.
library(dplyr)  # Calculate people and money for each year.
library(ggplot2)  # Display the yearly cash without saving files.
stopifnot(getRversion() >= "4.5.2")

#%%@note 1. Define the illustrative inputs
# Every input is synthetic and written out once, in the order the article uses it.
# ค่าตั้งต้นทั้งหมดเป็นข้อมูลสมมติ เขียนไว้ครั้งเดียวตามลำดับที่บทความใช้
prevalent <- 500
annual_incident <- 100
exit_fraction <- 0.10
uptake <- c(0.20,0.40,0.60)
annual_incremental_cost <- 2000
current_care_cost <- 4500
nurse_places <- 300
discount_rate <- 0.03
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")

#%%@note 2. Follow the eligible people year by year
# New survivors join at the year start, the year's users are counted, then exits leave at year end.
# ผู้มีสิทธิใหม่เข้าต้นปี นับผู้ใช้บริการของปีนั้น แล้วผู้ที่ออกจึงออกเมื่อสิ้นปี
stock <- rep(prevalent + annual_incident, length(uptake))
for (year_index in 2:length(uptake)) {
  stock[year_index] <- stock[year_index - 1] * (1 - exit_fraction) + annual_incident
}
eligible <- c(600,640,676)
stopifnot(all(abs(stock - eligible) < 1e-9))
years <- tibble::tibble(year = seq_along(uptake), eligible = stock, uptake = uptake) |>
  dplyr::mutate(treated = eligible * uptake, exits = eligible * exit_fraction,
    remaining = eligible * (1 - exit_fraction),
    annual_budget = treated * annual_incremental_cost,
    cumulative_budget = cumsum(annual_budget))
stopifnot(all(abs(years$exits + years$remaining - years$eligible) < 1e-9))
treated <- years$treated
budget <- years$annual_budget

#%%@note 3. Price the two scenarios
# Everyone costs current care in both; only the service users add the extra cost.
# ทุกคนมีต้นทุนการดูแลเดิมในทั้งสองสถานการณ์ เฉพาะผู้ใช้บริการที่มีต้นทุนเพิ่ม
current <- eligible * current_care_cost
new <- (eligible - treated) * current_care_cost +
  treated * (current_care_cost + annual_incremental_cost)
stopifnot(all(abs(new - current - budget) < 1e-6))

#%%@note 4. Check timing, rounding and the nurse places
# Wrong timing and early rounding change the cash; the nurse places cap year 3; uptake moves 0.10.
# ลำดับเวลาที่ผิดและการปัดเศษก่อนเวลาเปลี่ยนเงินสด จำนวนผู้ใช้ที่พยาบาลรับได้จำกัดปีที่ 3
exits_first <- treated[1] * c(1, 1 - exit_fraction) * annual_incremental_cost
rounded <- c(treated[3], round(treated[3]))
today_value <- 1834 / (1 + discount_rate)^3
capped <- pmin(treated, nurse_places)
shift <- 0.10 * sum(eligible)
users <- sum(eligible * uptake) + c(0, -1, 1) * shift

#%%@note 5. Display the model
# Plot the yearly extra cash; the script does not save a graphics file.
# แสดงเงินสดส่วนเพิ่มรายปี โดยไม่บันทึกไฟล์ภาพ
print(ggplot2::ggplot(years, ggplot2::aes(x = year, y = annual_budget)) +
  ggplot2::geom_col() + ggplot2::labs(x = "Year", y = "Extra cash in the year (THB)"))

#%%@note 6. Numbers the article quotes
# Print exactly the short boxes in their reading order.
# พิมพ์ผลลัพธ์ตรงกับกล่องตัวอย่างตามลำดับในบทความ
cat("Year order:",seq_along(uptake),"\n")
cat("Eligible at year start (people):",eligible,"\n")
cat("Treated prevalence (people):",treated,"\n")
cat("Year-end exits (people):",eligible*exit_fraction,"\n")
cat("Remaining stock (people):",eligible*(1-exit_fraction),"\n")
cat("Annual incremental budget (THB):",big(budget),"\n")
cat("Cumulative incremental budget (THB):",big(cumsum(budget)),"\n")
cat("Shortcut, 500 x 2,000 x 3 years (THB):",big(prevalent*annual_incremental_cost*3),"\n")
cat("Cost per person (THB):",big(current_care_cost+c(0,annual_incremental_cost)),"\n")
cat("Current-care scenario (THB):",big(current),"\n")
cat("New scenario (THB):",big(new),"\n")
cat("New minus current (THB):",big(new-current),"\n")
cat("Year 1 users, exits counted after and before:",treated[1]*c(1,1-exit_fraction),"\n")
cat("Year 1, exits counted after and before (THB):",big(exits_first),"\n")
cat("Year 3 people, expected and rounded:",rounded,"\n")
cat("Year 3 budget, expected and rounded (THB):",big(rounded*annual_incremental_cost),"\n")
cat("Yearly rate back to today (%):",100*discount_rate,"\n")
cat("1,834 THB due in year 3, worth today (THB):",big(today_value),"\n")
cat("Year 3 wanted, offered, waiting:",treated[3],nurse_places,treated[3]-nurse_places,"\n")
cat("Year 3 and three years within the places (THB):",
  big(c(capped[3],sum(capped))*annual_incremental_cost),"\n")
cat("Users over three years, shift of 0.10:",users[1],shift,"\n")
cat("Users, every share 0.10 lower and higher:",users[2:3],"\n")
cat("Three years, every share 0.10 lower and higher (THB):",
  big(users[2:3]*annual_incremental_cost),"\n")
สังเกตสองบรรทัดแรก ผู้ใช้ 108 คนและ 216,000 บาทเมื่อให้ผู้ที่พ้นสิทธิออกจากกลุ่มก่อน เทียบกับ 120 คนและ 240,000 บาท และ 406 ที่การปัดเศษก่อนเวลาทำจาก 405.6

เงินสดในปีที่จ่าย ไม่ใช่ความคุ้มค่า

การประเมินทางเศรษฐศาสตร์มักย่อจำนวนเงินในอนาคตให้เป็นมูลค่าวันนี้ เพราะคนและผู้จ่ายชอบจ่ายทีหลังและได้ประโยชน์เร็ว การปรับลดค่าตามเวลา (discounting) คือการแปลงต้นทุนหรือผลทางสุขภาพในอนาคตเป็นมูลค่าวันนี้ โดยลดลงตามอัตราการปรับลดในแต่ละปี ที่อัตรา 3% ต้นทุน 1,834 บาทที่ต้องจ่ายในปีที่ 3 มีค่าเท่ากับ 1,678 บาทในวันนี้ อ่านบทเรียนเต็มได้ที่ ต้นทุนของใคร ณ ปีใด: มุมมองการวิเคราะห์ ต้นทุนค่าเสียโอกาส การปรับค่าเงินเฟ้อ และการปรับลดค่าตามเวลา

งบประมาณไม่ทำแบบนั้น ในปีที่ 3 กองทุนต้องจ่ายเงินสด 811,200 บาท ไม่ว่าเงินจำนวนนี้จะมีมูลค่าวันนี้เท่าไร ทุกจำนวนในบทความนี้จึงเป็นเงินสดในปีที่จ่ายจริง

งบประมาณตอบว่ากองทุนจ่ายไหวหรือไม่ แต่ไม่ได้บอกว่าบริการนี้คุ้มค่าหรือไม่ คำถามเรื่องความคุ้มค่าชั่งต้นทุนที่เพิ่มกับสุขภาพที่ได้เพิ่ม ซึ่งการคำนวณนี้ไม่ได้นับ (Drummond and colleagues) บริการหนึ่งอาจคุ้มค่าแต่ยังใส่ในงบประมาณของปีที่ 3 ไม่ได้

ตรวจตัวเลขในย่อหน้าทบทวนด้วย R

โค้ด R cash.R
# Illustrative district: what a future cost is worth today, for contrast with cash as paid.
# Writes: nothing to disk; the results print in the console.
discount_rate <- 0.03
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")
cat("Yearly rate back to today (%):",100*discount_rate,"\n")
cat("1,834 THB due in year 3, worth today (THB):",big(1834/(1+discount_rate)^3),"\n")
ผลลัพธ์จากการรัน cash.log
Yearly rate back to today (%): 3
1,834 THB due in year 3, worth today (THB): 1,678

ต้องใช้ R ≥ 4.5.2 · tibble 3.3.1 · dplyr 1.2.1 · ggplot2 4.0.3 · ใช้ได้กับ R 4.6.1 · ดาวน์โหลด budget-impact-analysis-in-practice.R

สคริปต์ฉบับเต็ม
# Budget Impact Analysis in Practice: Count People Before Counting Money
# Follow one district's stroke survivors from eligible people to the extra cash the fund pays.
# ติดตามผู้ป่วยหลังโรคหลอดเลือดสมองของอำเภอหนึ่ง จากผู้มีสิทธิจนถึงเงินสดส่วนเพิ่มที่กองทุนจ่าย
# 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 years as an explicit table.
library(dplyr)  # Calculate people and money for each year.
library(ggplot2)  # Display the yearly cash without saving files.
stopifnot(getRversion() >= "4.5.2")

#%%@note 1. Define the illustrative inputs
# Every input is synthetic and written out once, in the order the article uses it.
# ค่าตั้งต้นทั้งหมดเป็นข้อมูลสมมติ เขียนไว้ครั้งเดียวตามลำดับที่บทความใช้
prevalent <- 500
annual_incident <- 100
exit_fraction <- 0.10
uptake <- c(0.20,0.40,0.60)
annual_incremental_cost <- 2000
current_care_cost <- 4500
nurse_places <- 300
discount_rate <- 0.03
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")

#%%@note 2. Follow the eligible people year by year
# New survivors join at the year start, the year's users are counted, then exits leave at year end.
# ผู้มีสิทธิใหม่เข้าต้นปี นับผู้ใช้บริการของปีนั้น แล้วผู้ที่ออกจึงออกเมื่อสิ้นปี
stock <- rep(prevalent + annual_incident, length(uptake))
for (year_index in 2:length(uptake)) {
  stock[year_index] <- stock[year_index - 1] * (1 - exit_fraction) + annual_incident
}
eligible <- c(600,640,676)
stopifnot(all(abs(stock - eligible) < 1e-9))
years <- tibble::tibble(year = seq_along(uptake), eligible = stock, uptake = uptake) |>
  dplyr::mutate(treated = eligible * uptake, exits = eligible * exit_fraction,
    remaining = eligible * (1 - exit_fraction),
    annual_budget = treated * annual_incremental_cost,
    cumulative_budget = cumsum(annual_budget))
stopifnot(all(abs(years$exits + years$remaining - years$eligible) < 1e-9))
treated <- years$treated
budget <- years$annual_budget

#%%@note 3. Price the two scenarios
# Everyone costs current care in both; only the service users add the extra cost.
# ทุกคนมีต้นทุนการดูแลเดิมในทั้งสองสถานการณ์ เฉพาะผู้ใช้บริการที่มีต้นทุนเพิ่ม
current <- eligible * current_care_cost
new <- (eligible - treated) * current_care_cost +
  treated * (current_care_cost + annual_incremental_cost)
stopifnot(all(abs(new - current - budget) < 1e-6))

#%%@note 4. Check timing, rounding and the nurse places
# Wrong timing and early rounding change the cash; the nurse places cap year 3; uptake moves 0.10.
# ลำดับเวลาที่ผิดและการปัดเศษก่อนเวลาเปลี่ยนเงินสด จำนวนผู้ใช้ที่พยาบาลรับได้จำกัดปีที่ 3
exits_first <- treated[1] * c(1, 1 - exit_fraction) * annual_incremental_cost
rounded <- c(treated[3], round(treated[3]))
today_value <- 1834 / (1 + discount_rate)^3
capped <- pmin(treated, nurse_places)
shift <- 0.10 * sum(eligible)
users <- sum(eligible * uptake) + c(0, -1, 1) * shift

#%%@note 5. Display the model
# Plot the yearly extra cash; the script does not save a graphics file.
# แสดงเงินสดส่วนเพิ่มรายปี โดยไม่บันทึกไฟล์ภาพ
print(ggplot2::ggplot(years, ggplot2::aes(x = year, y = annual_budget)) +
  ggplot2::geom_col() + ggplot2::labs(x = "Year", y = "Extra cash in the year (THB)"))

#%%@note 6. Numbers the article quotes
# Print exactly the short boxes in their reading order.
# พิมพ์ผลลัพธ์ตรงกับกล่องตัวอย่างตามลำดับในบทความ
cat("Year order:",seq_along(uptake),"\n")
cat("Eligible at year start (people):",eligible,"\n")
cat("Treated prevalence (people):",treated,"\n")
cat("Year-end exits (people):",eligible*exit_fraction,"\n")
cat("Remaining stock (people):",eligible*(1-exit_fraction),"\n")
cat("Annual incremental budget (THB):",big(budget),"\n")
cat("Cumulative incremental budget (THB):",big(cumsum(budget)),"\n")
cat("Shortcut, 500 x 2,000 x 3 years (THB):",big(prevalent*annual_incremental_cost*3),"\n")
cat("Cost per person (THB):",big(current_care_cost+c(0,annual_incremental_cost)),"\n")
cat("Current-care scenario (THB):",big(current),"\n")
cat("New scenario (THB):",big(new),"\n")
cat("New minus current (THB):",big(new-current),"\n")
cat("Year 1 users, exits counted after and before:",treated[1]*c(1,1-exit_fraction),"\n")
cat("Year 1, exits counted after and before (THB):",big(exits_first),"\n")
cat("Year 3 people, expected and rounded:",rounded,"\n")
cat("Year 3 budget, expected and rounded (THB):",big(rounded*annual_incremental_cost),"\n")
cat("Yearly rate back to today (%):",100*discount_rate,"\n")
cat("1,834 THB due in year 3, worth today (THB):",big(today_value),"\n")
cat("Year 3 wanted, offered, waiting:",treated[3],nurse_places,treated[3]-nurse_places,"\n")
cat("Year 3 and three years within the places (THB):",
  big(c(capped[3],sum(capped))*annual_incremental_cost),"\n")
cat("Users over three years, shift of 0.10:",users[1],shift,"\n")
cat("Users, every share 0.10 lower and higher:",users[2:3],"\n")
cat("Three years, every share 0.10 lower and higher (THB):",
  big(users[2:3]*annual_incremental_cost),"\n")
สังเกตสองบรรทัดนี้ คืออัตรา 3% และ 1,678 บาทซึ่งเป็นมูลค่าวันนี้ของต้นทุน 1,834 บาทที่ต้องจ่ายในปีที่ 3

พยาบาลรองรับสัดส่วนผู้ใช้บริการได้หรือไม่

งบประมาณสมมติว่าบริการเข้าถึงทุกคนที่นับไว้ได้ สมมติว่าพยาบาลของอำเภอติดตามผู้ใช้ได้มากที่สุดปีละ 300 คน ตามแผนงบประมาณ ปีที่ 3 มีผู้ใช้ 405.6 คน จึงมีผู้ป่วย 405.6 − 300 = 105.6 คนที่ต้องรอ

เงินสดของปีที่ 3 จึงเป็น 300 × 2,000 = 600,000 บาท ไม่ใช่ 811,200 บาท และสามปีรวมกันเป็น 240,000 + 512,000 + 600,000 = 1,352,000 บาท ให้รายงานทั้งสองตัวเลข คือเงินสดที่กองทุนจะจ่าย และผู้ป่วยที่บริการยังเข้าไม่ถึง คณะกรรมการจะได้เห็นความต้องการที่ยังไม่ได้รับบริการ ไม่ใช่เห็นเพียงยอดเงินที่ลดลง

ตรวจเครื่องวัดความดัน ช่องทางส่งต่อ และเวลาของพยาบาลก่อนเชื่อสัดส่วนผู้ใช้บริการที่เพิ่มขึ้น สัดส่วนผู้ใช้บริการเองก็เป็นการคาดการณ์ จึงควรรายงานเป็นช่วง ตามแผน ผู้ใช้รวมทุกปีคือ 120 + 256 + 405.6 = 781.6 คน ถ้าลดสัดส่วนทุกปีลง 0.10 ผู้ใช้จะหายไป 0.10 × (600 + 640 + 676) = 191.6 คน เหลือ 590 คน จึงมีต้นทุน 590 × 2,000 = 1,180,000 บาท ถ้าเพิ่มสัดส่วนทุกปีขึ้น 0.10 ผู้ใช้จะเพิ่มอีก 191.6 คน เป็น 973.2 คน และมีต้นทุน 1,946,400 บาท ก่อนคิดขีดจำกัดของพยาบาล

ตรวจขีดจำกัดของพยาบาลด้วย R

โค้ด R capacity.R
# Illustrative district: the users the nurses can follow in year 3.
# Writes: nothing to disk; the results print in the console.
eligible <- c(600,640,676)
uptake <- c(0.20,0.40,0.60)
annual_incremental_cost <- 2000
nurse_places <- 300
treated <- eligible*uptake
capped <- pmin(treated,nurse_places)
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")
cat("Year 3 wanted, offered, waiting:",treated[3],nurse_places,treated[3]-nurse_places,"\n")
cat("Year 3 and three years within the places (THB):",
  big(c(capped[3],sum(capped))*annual_incremental_cost),"\n")
ผลลัพธ์จากการรัน capacity.log
Year 3 wanted, offered, waiting: 405.6 300 105.6
Year 3 and three years within the places (THB): 600,000 1,352,000

ต้องใช้ R ≥ 4.5.2 · tibble 3.3.1 · dplyr 1.2.1 · ggplot2 4.0.3 · ใช้ได้กับ R 4.6.1 · ดาวน์โหลด budget-impact-analysis-in-practice.R

สคริปต์ฉบับเต็ม
# Budget Impact Analysis in Practice: Count People Before Counting Money
# Follow one district's stroke survivors from eligible people to the extra cash the fund pays.
# ติดตามผู้ป่วยหลังโรคหลอดเลือดสมองของอำเภอหนึ่ง จากผู้มีสิทธิจนถึงเงินสดส่วนเพิ่มที่กองทุนจ่าย
# 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 years as an explicit table.
library(dplyr)  # Calculate people and money for each year.
library(ggplot2)  # Display the yearly cash without saving files.
stopifnot(getRversion() >= "4.5.2")

#%%@note 1. Define the illustrative inputs
# Every input is synthetic and written out once, in the order the article uses it.
# ค่าตั้งต้นทั้งหมดเป็นข้อมูลสมมติ เขียนไว้ครั้งเดียวตามลำดับที่บทความใช้
prevalent <- 500
annual_incident <- 100
exit_fraction <- 0.10
uptake <- c(0.20,0.40,0.60)
annual_incremental_cost <- 2000
current_care_cost <- 4500
nurse_places <- 300
discount_rate <- 0.03
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")

#%%@note 2. Follow the eligible people year by year
# New survivors join at the year start, the year's users are counted, then exits leave at year end.
# ผู้มีสิทธิใหม่เข้าต้นปี นับผู้ใช้บริการของปีนั้น แล้วผู้ที่ออกจึงออกเมื่อสิ้นปี
stock <- rep(prevalent + annual_incident, length(uptake))
for (year_index in 2:length(uptake)) {
  stock[year_index] <- stock[year_index - 1] * (1 - exit_fraction) + annual_incident
}
eligible <- c(600,640,676)
stopifnot(all(abs(stock - eligible) < 1e-9))
years <- tibble::tibble(year = seq_along(uptake), eligible = stock, uptake = uptake) |>
  dplyr::mutate(treated = eligible * uptake, exits = eligible * exit_fraction,
    remaining = eligible * (1 - exit_fraction),
    annual_budget = treated * annual_incremental_cost,
    cumulative_budget = cumsum(annual_budget))
stopifnot(all(abs(years$exits + years$remaining - years$eligible) < 1e-9))
treated <- years$treated
budget <- years$annual_budget

#%%@note 3. Price the two scenarios
# Everyone costs current care in both; only the service users add the extra cost.
# ทุกคนมีต้นทุนการดูแลเดิมในทั้งสองสถานการณ์ เฉพาะผู้ใช้บริการที่มีต้นทุนเพิ่ม
current <- eligible * current_care_cost
new <- (eligible - treated) * current_care_cost +
  treated * (current_care_cost + annual_incremental_cost)
stopifnot(all(abs(new - current - budget) < 1e-6))

#%%@note 4. Check timing, rounding and the nurse places
# Wrong timing and early rounding change the cash; the nurse places cap year 3; uptake moves 0.10.
# ลำดับเวลาที่ผิดและการปัดเศษก่อนเวลาเปลี่ยนเงินสด จำนวนผู้ใช้ที่พยาบาลรับได้จำกัดปีที่ 3
exits_first <- treated[1] * c(1, 1 - exit_fraction) * annual_incremental_cost
rounded <- c(treated[3], round(treated[3]))
today_value <- 1834 / (1 + discount_rate)^3
capped <- pmin(treated, nurse_places)
shift <- 0.10 * sum(eligible)
users <- sum(eligible * uptake) + c(0, -1, 1) * shift

#%%@note 5. Display the model
# Plot the yearly extra cash; the script does not save a graphics file.
# แสดงเงินสดส่วนเพิ่มรายปี โดยไม่บันทึกไฟล์ภาพ
print(ggplot2::ggplot(years, ggplot2::aes(x = year, y = annual_budget)) +
  ggplot2::geom_col() + ggplot2::labs(x = "Year", y = "Extra cash in the year (THB)"))

#%%@note 6. Numbers the article quotes
# Print exactly the short boxes in their reading order.
# พิมพ์ผลลัพธ์ตรงกับกล่องตัวอย่างตามลำดับในบทความ
cat("Year order:",seq_along(uptake),"\n")
cat("Eligible at year start (people):",eligible,"\n")
cat("Treated prevalence (people):",treated,"\n")
cat("Year-end exits (people):",eligible*exit_fraction,"\n")
cat("Remaining stock (people):",eligible*(1-exit_fraction),"\n")
cat("Annual incremental budget (THB):",big(budget),"\n")
cat("Cumulative incremental budget (THB):",big(cumsum(budget)),"\n")
cat("Shortcut, 500 x 2,000 x 3 years (THB):",big(prevalent*annual_incremental_cost*3),"\n")
cat("Cost per person (THB):",big(current_care_cost+c(0,annual_incremental_cost)),"\n")
cat("Current-care scenario (THB):",big(current),"\n")
cat("New scenario (THB):",big(new),"\n")
cat("New minus current (THB):",big(new-current),"\n")
cat("Year 1 users, exits counted after and before:",treated[1]*c(1,1-exit_fraction),"\n")
cat("Year 1, exits counted after and before (THB):",big(exits_first),"\n")
cat("Year 3 people, expected and rounded:",rounded,"\n")
cat("Year 3 budget, expected and rounded (THB):",big(rounded*annual_incremental_cost),"\n")
cat("Yearly rate back to today (%):",100*discount_rate,"\n")
cat("1,834 THB due in year 3, worth today (THB):",big(today_value),"\n")
cat("Year 3 wanted, offered, waiting:",treated[3],nurse_places,treated[3]-nurse_places,"\n")
cat("Year 3 and three years within the places (THB):",
  big(c(capped[3],sum(capped))*annual_incremental_cost),"\n")
cat("Users over three years, shift of 0.10:",users[1],shift,"\n")
cat("Users, every share 0.10 lower and higher:",users[2:3],"\n")
cat("Three years, every share 0.10 lower and higher (THB):",
  big(users[2:3]*annual_incremental_cost),"\n")
สังเกตผู้ป่วย 105.6 คนที่ต้องรอ และเงิน 600,000 และ 1,352,000 บาทภายในขีดจำกัดของพยาบาล

ตรวจช่วงของสัดส่วนผู้ใช้บริการด้วย R

โค้ด R range.R
# Illustrative district: the three-year cash if every year's uptake is 0.10 lower or higher.
# Writes: nothing to disk; the results print in the console.
eligible <- c(600,640,676)
uptake <- c(0.20,0.40,0.60)
annual_incremental_cost <- 2000
shift <- 0.10*sum(eligible)
users <- sum(eligible*uptake)+c(0,-1,1)*shift
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")
cat("Users over three years, shift of 0.10:",users[1],shift,"\n")
cat("Users, every share 0.10 lower and higher:",users[2:3],"\n")
cat("Three years, every share 0.10 lower and higher (THB):",
  big(users[2:3]*annual_incremental_cost),"\n")
ผลลัพธ์จากการรัน range.log
Users over three years, shift of 0.10: 781.6 191.6
Users, every share 0.10 lower and higher: 590 973.2
Three years, every share 0.10 lower and higher (THB): 1,180,000 1,946,400

ต้องใช้ R ≥ 4.5.2 · tibble 3.3.1 · dplyr 1.2.1 · ggplot2 4.0.3 · ใช้ได้กับ R 4.6.1 · ดาวน์โหลด budget-impact-analysis-in-practice.R

สคริปต์ฉบับเต็ม
# Budget Impact Analysis in Practice: Count People Before Counting Money
# Follow one district's stroke survivors from eligible people to the extra cash the fund pays.
# ติดตามผู้ป่วยหลังโรคหลอดเลือดสมองของอำเภอหนึ่ง จากผู้มีสิทธิจนถึงเงินสดส่วนเพิ่มที่กองทุนจ่าย
# 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 years as an explicit table.
library(dplyr)  # Calculate people and money for each year.
library(ggplot2)  # Display the yearly cash without saving files.
stopifnot(getRversion() >= "4.5.2")

#%%@note 1. Define the illustrative inputs
# Every input is synthetic and written out once, in the order the article uses it.
# ค่าตั้งต้นทั้งหมดเป็นข้อมูลสมมติ เขียนไว้ครั้งเดียวตามลำดับที่บทความใช้
prevalent <- 500
annual_incident <- 100
exit_fraction <- 0.10
uptake <- c(0.20,0.40,0.60)
annual_incremental_cost <- 2000
current_care_cost <- 4500
nurse_places <- 300
discount_rate <- 0.03
big <- function(value) formatC(value, format = "f", digits = 0, big.mark = ",")

#%%@note 2. Follow the eligible people year by year
# New survivors join at the year start, the year's users are counted, then exits leave at year end.
# ผู้มีสิทธิใหม่เข้าต้นปี นับผู้ใช้บริการของปีนั้น แล้วผู้ที่ออกจึงออกเมื่อสิ้นปี
stock <- rep(prevalent + annual_incident, length(uptake))
for (year_index in 2:length(uptake)) {
  stock[year_index] <- stock[year_index - 1] * (1 - exit_fraction) + annual_incident
}
eligible <- c(600,640,676)
stopifnot(all(abs(stock - eligible) < 1e-9))
years <- tibble::tibble(year = seq_along(uptake), eligible = stock, uptake = uptake) |>
  dplyr::mutate(treated = eligible * uptake, exits = eligible * exit_fraction,
    remaining = eligible * (1 - exit_fraction),
    annual_budget = treated * annual_incremental_cost,
    cumulative_budget = cumsum(annual_budget))
stopifnot(all(abs(years$exits + years$remaining - years$eligible) < 1e-9))
treated <- years$treated
budget <- years$annual_budget

#%%@note 3. Price the two scenarios
# Everyone costs current care in both; only the service users add the extra cost.
# ทุกคนมีต้นทุนการดูแลเดิมในทั้งสองสถานการณ์ เฉพาะผู้ใช้บริการที่มีต้นทุนเพิ่ม
current <- eligible * current_care_cost
new <- (eligible - treated) * current_care_cost +
  treated * (current_care_cost + annual_incremental_cost)
stopifnot(all(abs(new - current - budget) < 1e-6))

#%%@note 4. Check timing, rounding and the nurse places
# Wrong timing and early rounding change the cash; the nurse places cap year 3; uptake moves 0.10.
# ลำดับเวลาที่ผิดและการปัดเศษก่อนเวลาเปลี่ยนเงินสด จำนวนผู้ใช้ที่พยาบาลรับได้จำกัดปีที่ 3
exits_first <- treated[1] * c(1, 1 - exit_fraction) * annual_incremental_cost
rounded <- c(treated[3], round(treated[3]))
today_value <- 1834 / (1 + discount_rate)^3
capped <- pmin(treated, nurse_places)
shift <- 0.10 * sum(eligible)
users <- sum(eligible * uptake) + c(0, -1, 1) * shift

#%%@note 5. Display the model
# Plot the yearly extra cash; the script does not save a graphics file.
# แสดงเงินสดส่วนเพิ่มรายปี โดยไม่บันทึกไฟล์ภาพ
print(ggplot2::ggplot(years, ggplot2::aes(x = year, y = annual_budget)) +
  ggplot2::geom_col() + ggplot2::labs(x = "Year", y = "Extra cash in the year (THB)"))

#%%@note 6. Numbers the article quotes
# Print exactly the short boxes in their reading order.
# พิมพ์ผลลัพธ์ตรงกับกล่องตัวอย่างตามลำดับในบทความ
cat("Year order:",seq_along(uptake),"\n")
cat("Eligible at year start (people):",eligible,"\n")
cat("Treated prevalence (people):",treated,"\n")
cat("Year-end exits (people):",eligible*exit_fraction,"\n")
cat("Remaining stock (people):",eligible*(1-exit_fraction),"\n")
cat("Annual incremental budget (THB):",big(budget),"\n")
cat("Cumulative incremental budget (THB):",big(cumsum(budget)),"\n")
cat("Shortcut, 500 x 2,000 x 3 years (THB):",big(prevalent*annual_incremental_cost*3),"\n")
cat("Cost per person (THB):",big(current_care_cost+c(0,annual_incremental_cost)),"\n")
cat("Current-care scenario (THB):",big(current),"\n")
cat("New scenario (THB):",big(new),"\n")
cat("New minus current (THB):",big(new-current),"\n")
cat("Year 1 users, exits counted after and before:",treated[1]*c(1,1-exit_fraction),"\n")
cat("Year 1, exits counted after and before (THB):",big(exits_first),"\n")
cat("Year 3 people, expected and rounded:",rounded,"\n")
cat("Year 3 budget, expected and rounded (THB):",big(rounded*annual_incremental_cost),"\n")
cat("Yearly rate back to today (%):",100*discount_rate,"\n")
cat("1,834 THB due in year 3, worth today (THB):",big(today_value),"\n")
cat("Year 3 wanted, offered, waiting:",treated[3],nurse_places,treated[3]-nurse_places,"\n")
cat("Year 3 and three years within the places (THB):",
  big(c(capped[3],sum(capped))*annual_incremental_cost),"\n")
cat("Users over three years, shift of 0.10:",users[1],shift,"\n")
cat("Users, every share 0.10 lower and higher:",users[2:3],"\n")
cat("Three years, every share 0.10 lower and higher (THB):",
  big(users[2:3]*annual_incremental_cost),"\n")
สังเกตผู้ใช้ 781.6 คนตามแผน และ 191.6 คนที่การเลื่อน 0.10 เปลี่ยนไป แล้วดูช่วงตั้งแต่ 1,180,000 ถึง 1,946,400 บาท

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

Budget impact analysis (การวิเคราะห์ผลกระทบด้านงบประมาณ)
เงินสดส่วนเพิ่มที่ผู้จ่ายรายหนึ่งต้องใช้ในแต่ละปีถ้ารับการเปลี่ยนแปลงหนึ่ง ในที่นี้คือ 1,563,200 บาทในสามปี
Eligible stock (กลุ่มผู้มีสิทธิ)
ทุกคนที่มีสิทธิ ณ ขณะหนึ่ง ในที่นี้คือ 600 คนเมื่อต้นปีแรก
Incident inflow (ผู้มีสิทธิรายใหม่)
คนใหม่ที่มีสิทธิในแต่ละปี ในที่นี้คือปีละ 100 คน
Uptake (สัดส่วนผู้ใช้บริการ)
สัดส่วนของทุกคนที่มีสิทธิในปีนั้นที่ใช้บริการ ในที่นี้คือ 20%, 40% และ 60%
Treated prevalence (จำนวนผู้ใช้บริการทั้งหมดในปี)
ทุกคนที่ใช้บริการในปีหนึ่ง ทั้งผู้เริ่มใหม่และผู้ใช้ต่อเนื่อง ปีที่ 3 มีผู้ใช้ 405.6 คน
Recurring and one-off cost (ต้นทุนที่เกิดซ้ำและต้นทุนครั้งเดียว)
ต้นทุนที่เกิดซ้ำเกิดทุกปีที่ใช้บริการ ส่วนต้นทุนครั้งเดียวจ่ายครั้งเดียวเมื่อผู้ใช้เริ่มบริการ
Current and new scenario (สถานการณ์ปัจจุบันและสถานการณ์ใหม่)
รายจ่ายเมื่อไม่มีและมีการเปลี่ยนแปลง ผลกระทบด้านงบประมาณคือสถานการณ์ใหม่ลบสถานการณ์ปัจจุบัน

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

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

ลองใช้ในแอป

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

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

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

  1. Sullivan Sean D., Mauskopf Josephine A., Augustovski Federico, Jaime Caro J., Lee Karen M., Minchin Mark, Orlewska Ewa, Penna Pete, Rodriguez Barrios Jose-Manuel, Shau Wen-Yi. Budget Impact Analysis-Principles of Good Practice: Report of the ISPOR 2012 Budget Impact Analysis Good Practice II Task Force. Value in Health. 2014;17(1):5-14. doi:10.1016/j.jval.2013.08.2291. https://doi.org/10.1016/j.jval.2013.08.2291
  2. Michael F. Drummond, Mark J. Sculpher, Karl Claxton, Greg L. Stoddart, George W. Torrance. Methods for the Economic Evaluation of Health Care Programmes. Oxford University Press; 2015. ISBN 9780199665884. https://openlibrary.org/isbn/9780199665884
  3. Rattanavipapong Waranya, Worakijthamrongchai Thanaboon, Soboon Budsadee, Luankongsomchit Vilawan, Kongmuangpuk Mananchaya, Isaranuwatchai Wanrudee, Teerawattananon Yot, Nilanont Yongchai. Economic evaluation of endovascular treatment for acute ischaemic stroke in Thailand. BMJ Open. 2022;12(9):e064403. doi:10.1136/bmjopen-2022-064403. https://doi.org/10.1136/bmjopen-2022-064403

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

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

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

  • นับคนก่อนนับเงิน: ผู้มีสิทธิ 500 คนกลายเป็น 600, 640 และ 676 คน เมื่อมีคนใหม่ 100 คนเข้ามาและ 10% ออกไปทุกปี
  • ใช้สัดส่วนผู้ใช้บริการกับทุกคนที่มีสิทธิในปีนั้น ได้ผู้ใช้ 120, 256 และ 405.6 คน และคิดต้นทุนที่เกิดซ้ำคนละ 2,000 บาทให้ทุกคน
  • ภายในหนึ่งปี ให้เพิ่มผู้มาใหม่ นับผู้ใช้และคิดต้นทุน แล้วจึงให้ผู้ที่พ้นสิทธิออกจากกลุ่ม ลำดับแบบอื่นทำให้ปีที่ 1 ลดจาก 240,000 เหลือ 216,000 บาท
  • งบประมาณคือสถานการณ์ใหม่ลบสถานการณ์ปัจจุบัน เป็นเงินสดในปีที่จ่าย: 1,563,200 บาทในสามปี ไม่ใช่ 3,000,000 บาท
  • ตรวจกำลังการให้บริการ: ถ้าพยาบาลรับได้ 300 คนในปีที่ 3 เงินสดปีนั้นจะเป็น 600,000 บาท รวมสามปี 1,352,000 บาท และผู้ป่วย 105.6 คนต้องรอ

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

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

ความคิดเห็น

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

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