pkgs <- c("bit64","svglite","dplyr","tidyr","stringr","forcats","ggplot2","scales","patchwork","ggrepel","gt")
invisible(lapply(pkgs, function(p){ if(!requireNamespace(p,quietly=TRUE))
install.packages(p,quiet=TRUE,repos="https://cloud.r-project.org")
suppressPackageStartupMessages(library(p,character.only=TRUE)) }))BUNDLE <- "aging_ltc_jkn_aggregates.rds"
A <- readRDS(BUNDLE)
fix64 <- function(x){
if(is.data.frame(x)){ x[] <- lapply(x, function(c) if(inherits(c,"integer64")) as.numeric(c) else c); x }
else if(is.list(x)) lapply(x, fix64)
else if(inherits(x,"integer64")) as.numeric(x) else x }
A <- fix64(A)
pt <- A$pt
OUT_DIR <- "outputs"; if(!dir.exists(OUT_DIR)) dir.create(OUT_DIR)
TBL_DIR <- "tables"; if(!dir.exists(TBL_DIR)) dir.create(TBL_DIR)
wcsv <- function(df,name) { write.csv(df, file.path(TBL_DIR, paste0(name,".csv")), row.names=FALSE); df }
## ICD-10 3-char -> disease name (built from data: FKL16A / FKP15A). Used to label dx tables/figures.
ICDLAB <- tryCatch(read.csv("icd3_label.csv", stringsAsFactors=FALSE), error=function(e) data.frame(icd3=character(),disease=character()))
addlab <- function(df) df |> left_join(ICDLAB, by="icd3") |>
mutate(disease=ifelse(is.na(disease)|disease=="","(name unavailable)",disease),
lbl=paste0(icd3, " ", ifelse(nchar(disease)>44, paste0(substr(disease,1,44),"…"), disease)))
## ---- house style (palet dongker + abu terang) ----
INK<-"#0b1f3a"; NAVY<-"#0b1f3a"; NAVY2<-"#13315c"; BLUE<-"#3d5a80"; STEEL<-"#3d5a80"
SLATE<-"#5b7aa6"; MIST<-"#9fb3c8"; LMIST<-"#c7d2dd"; LGRAY<-"#e3e8ee"; GREY<-"#64748b"
TEAL<-"#13315c"; AMBER<-"#5b7aa6"; RED<-"#274060"; GREEN<-"#13315c"; PURPLE<-"#13315c"; ORANGE<-"#5b7aa6"
PANEL<-"#f3f5f8"; GRID<-"#dbe2ea"; TXT<-"#1f3350"
COLS6 <- c(INK,STEEL,NAVY2,SLATE,MIST,LMIST)
ABAND_COLS <- c("Pra-lansia (45–59)"=LMIST,"Lansia muda (60–69)"=SLATE,
"Lansia madya (70–79)"=NAVY2,"Lansia tua (80+)"=INK)
LVL_COLS <- c("FKTP (Primer)"="#2f9e44","FKRTL Rawat Jalan"=STEEL,"FKRTL Rawat Inap"=INK) # FKTP green for contrast
ISLAND_COLS <- c("Jawa"=INK,"Sumatera"=STEEL,"Sulawesi"=NAVY2,"Kalimantan"=SLATE,"Bali-Nusra"=MIST,"Maluku-Papua"=LMIST,"Tidak diketahui"=LGRAY)
ENTRY_COLS <- c("1 FKTP (Primer)"=INK,
"2 FKRTL Rawat Jalan (langsung ke RS)"=STEEL,
"3 FKRTL Rawat Inap (langsung opname)"=LMIST)
th <- function(b=11) theme_minimal(base_size=b) + theme(
plot.title=element_text(color=INK,face="bold",size=b+2,margin=margin(b=4),family="serif"),
plot.subtitle=element_text(color=GREY,size=b-1,lineheight=1.3),
plot.caption=element_text(color=GREY,size=b-3,hjust=1),
plot.background=element_rect(fill="white",color=NA),
panel.background=element_rect(fill=PANEL,color=NA),
panel.border=element_rect(color=GRID,fill=NA,linewidth=0.4),
panel.grid.major=element_line(color=GRID,linewidth=0.3), panel.grid.minor=element_blank(),
legend.position="bottom", legend.direction="horizontal", legend.box="horizontal",
legend.key.size=unit(.55,"lines"), legend.text=element_text(size=b-2),
strip.background=element_rect(fill=INK,color=NA),
strip.text=element_text(color="white",face="bold",size=b-1))
gfmt <- function(g) g |>
gt::tab_options(heading.background.color=INK, heading.title.font.size=gt::px(13),
column_labels.background.color=STEEL, column_labels.font.weight="bold", column_labels.font.size=gt::px(11),
row.striping.include_table_body=TRUE, row.striping.background_color="#eef2f7",
table.border.top.color=INK, table.border.top.width=gt::px(3), table.width=gt::pct(100),
data_row.padding=gt::px(5), source_notes.font.size=gt::px(10)) |>
gt::tab_style(style=gt::cell_text(color="white",weight="bold"),
locations=gt::cells_column_labels(gt::everything()))
CAP <- "Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection)"
fmt <- function(x) formatC(round(as.numeric(x)), format="d", big.mark=",")
pc1 <- function(x) paste0(formatC(x, format="f", digits=1),"%")
yrx <- function() scale_x_continuous(breaks=2015:2024, labels=function(x) paste0("'",substr(x,3,4)))
ABAND_LAB <- A$meta$aband_lab
## ---- translate bundle data labels to English ----
.rc <- function(df,col,map){ if(!is.null(df)&&is.data.frame(df)&&col%in%names(df)){ v<-as.character(df[[col]]); m<-unname(map[v]); df[[col]]<-ifelse(is.na(m),v,m) }; df }
.AGE <- c("Pra-lansia (45–59)"="Pre-elderly (45–59)","Lansia muda (60–69)"="Young-old (60–69)","Lansia madya (70–79)"="Middle-old (70–79)","Lansia tua (80+)"="Oldest-old (80+)")
.CHRON <- c("Hipertensi (I10–15)"="Hypertension (I10–15)","Diabetes (E10–14)"="Diabetes (E10–14)","Penyakit Jantung Iskemik (I20–25)"="Ischaemic heart disease (I20–25)","Gagal Jantung (I50)"="Heart failure (I50)","Stroke (I60–69)"="Stroke (I60–69)","Penyakit Paru Kronik (J40–47)"="Chronic lung disease (J40–47)","Penyakit Ginjal Kronik (N17–19)"="Chronic kidney disease (N17–19)","Kanker (C00–C97)"="Cancer (C00–C97)","Osteoartritis (M15–19)"="Osteoarthritis (M15–19)","Demensia (F00–03, G30)"="Dementia (F00–03, G30)","Depresi (F32–33)"="Depression (F32–33)","Penyakit Hati Kronik (K70–76)"="Chronic liver disease (K70–76)","Gangguan Tiroid (E00–07)"="Thyroid disorders (E00–07)","Anemia (D50–64)"="Anaemia (D50–64)")
.GERI <- c("Fraktur panggul/femur (S72)"="Hip/femur fracture (S72)","Fraktur fragilitas (vertebra/pinggul/anggota gerak)"="Fragility fracture (vertebra/hip/limb)","Osteoporosis (M80–81)"="Osteoporosis (M80–81)","Stroke akut (I60–64)"="Acute stroke (I60–64)","Gejala sisa stroke (I69)"="Stroke sequelae (I69)","Demensia (F00–03, G30–31)"="Dementia (F00–03, G30–31)","Parkinsonisme (G20–21)"="Parkinsonism (G20–21)","Delirium (F05)"="Delirium (F05)","Ulkus dekubitus (L89)"="Pressure ulcer (L89)","Inkontinensia urin (R32)"="Urinary incontinence (R32)","Kerentaan/senilitas (R54)"="Frailty/senility (R54)","Sinkop/jatuh (R55, W00–19)"="Syncope/falls (R55, W00–19)","Katarak (H25–26)"="Cataract (H25–26)","Glaukoma (H40)"="Glaucoma (H40)","Gangguan pendengaran (H90–91)"="Hearing loss (H90–91)","Malnutrisi (E40–46)"="Malnutrition (E40–46)","Pneumonia (J12–18)"="Pneumonia (J12–18)","Penyakit saluran kemih (N39)"="Urinary tract disease (N39)")
.ACSC <- c("Gagal jantung (I50)"="Heart failure (I50)","Hipertensi (I10–11)"="Hypertension (I10–11)","Diabetes (E10–14)"="Diabetes (E10–14)","PPOK (J40–44)"="COPD (J40–44)","Asma (J45–46)"="Asthma (J45–46)","Pneumonia bakterial (J13–18)"="Bacterial pneumonia (J13–18)","Infeksi saluran kemih (N39)"="Urinary tract infection (N39)","Dehidrasi (E86)"="Dehydration (E86)","Angina (I20)"="Angina (I20)","Selulitis (L03–04)"="Cellulitis (L03–04)")
.COV <- c("FKTP + FKRTL"="FKTP + Hospital","FKTP saja"="FKTP only","FKRTL saja"="Hospital only","Lainnya"="Other")
.SEV <- c("1 Ringan"="1 Mild","2 Sedang"="2 Moderate","3 Berat"="3 Severe","9 Lainnya"="9 Other")
.LVL <- c("FKTP (Primer)"="FKTP (Primary)","FKRTL Rawat Jalan"="Hospital outpatient","FKRTL Rawat Inap"="Hospital inpatient")
.ENTRY <- c("1 FKTP (Primer)"="1 FKTP (Primary care)","2 FKRTL Rawat Jalan (langsung ke RS)"="2 Hospital outpatient (direct)","3 FKRTL Rawat Inap (langsung opname)"="3 Hospital inpatient (direct admission)")
.PBI <- c("PBI (disubsidi)"="PBI (subsidized)","Non-PBI"="Non-PBI")
.ISL <- c("Jawa"="Java","Sumatera"="Sumatra","Bali-Nusra"="Bali-Nusa Tenggara","Kalimantan"="Kalimantan","Sulawesi"="Sulawesi","Maluku-Papua"="Maluku-Papua","Tidak diketahui"="Unknown")
.CHAR <- c("Jenis Kelamin"="Sex","Kelompok Usia"="Age group","Segmentasi"="Membership segment","Kelas Rawat"="Ward class","Pulau"="Island")
.LEV <- c(.AGE,"Laki-laki"="Male","Perempuan"="Female","Kelas I"="Class I","Kelas II"="Class II","Kelas III"="Class III","Bukan Pekerja"="Non-worker","PBPU (Mandiri)"="PBPU (self-paying)","PPU"="PPU (salaried)")
.REFR <- c("Puskesmas"="Puskesmas (PHC)","Rumah Sakit"="Hospital","Klinik Pratama"="Primary clinic","Dokter Umum"="GP","Klinik Utama"="Specialist clinic","Apotik"="Pharmacy","Pemerintah"="Government","Lainnya"="Other")
.SEG <- c("Bukan Pekerja"="Non-worker","PBI APBN"="PBI APBN","PBI APBD"="PBI APBD","PBPU (Mandiri)"="PBPU (self-paying)","PPU"="PPU (salaried)")
ABAND_LAB <- unname(ifelse(ABAND_LAB %in% names(.AGE), .AGE[ABAND_LAB], ABAND_LAB))
if(exists("ABAND_COLS")) names(ABAND_COLS) <- ifelse(names(ABAND_COLS)%in%names(.AGE), unname(.AGE[names(ABAND_COLS)]), names(ABAND_COLS))
if(exists("ENTRY_COLS")) names(ENTRY_COLS) <- ifelse(names(ENTRY_COLS)%in%names(.ENTRY), unname(.ENTRY[names(ENTRY_COLS)]), names(ENTRY_COLS))
if(exists("LVL_COLS")) names(LVL_COLS) <- ifelse(names(LVL_COLS)%in%names(.LVL), unname(.LVL[names(LVL_COLS)]), names(LVL_COLS))
if(exists("ISLAND_COLS")) names(ISLAND_COLS) <- ifelse(names(ISLAND_COLS)%in%names(.ISL), unname(.ISL[names(ISLAND_COLS)]), names(ISLAND_COLS))
for(nm in c("strata_patient","prev_year","los_band","mm_by_band","cost_percapita_band","geriatric_by_band","entry_band","visit_rate","cost_band")) A[[nm]] <- .rc(A[[nm]],"grp",.AGE)
A$chronic_prev <- .rc(A$chronic_prev,"condition",.CHRON)
A$geriatric_prev <- .rc(A$geriatric_prev,"syndrome",.GERI)
A$geriatric_by_band <- .rc(A$geriatric_by_band,"syndrome",.GERI)
A$acsc_breakdown <- .rc(A$acsc_breakdown,"condition",.ACSC)
A$coverage <- .rc(A$coverage,"grpc",.COV)
A$severity <- .rc(A$severity,"severity",.SEV)
A$util_year <- .rc(A$util_year,"level",.LVL)
for(nm in c("entry_overall","entry_band","entry_year","entry_segment")) A[[nm]] <- .rc(A[[nm]],"entry",.ENTRY)
A$entry_segment <- .rc(A$entry_segment,"pbi",.PBI); A$equity_segment <- .rc(A$equity_segment,"pbi",.PBI); A$equity_segment <- .rc(A$equity_segment,"segmen",.SEG)
for(nm in c("geo_island","geo_prov","equity_island","served_rate_prov")) A[[nm]] <- .rc(A[[nm]],"island",.ISL)
A$referrer_type <- .rc(A$referrer_type,"perujuk",.REFR)
A$table1 <- .rc(.rc(A$table1,"char",.CHAR),"level",.LEV)
A$repr_index <- .rc(.rc(A$repr_index,"char",.CHAR),"level",.LEV)
if(!is.null(A$mm_dyads)&&"pair"%in%names(A$mm_dyads)) for(k in names(.CHRON)) A$mm_dyads$pair <- gsub(k, .CHRON[[k]], A$mm_dyads$pair, fixed=TRUE)Unit of analysis (shown on every table/figure). This document
uses three units, see the badge · Unit: …: (1) Unique
patients = persons, counted once (prevalence, demographics,
mortality, equity, multimorbidity, geriatric syndromes); (2)
Visits/Claims = per service (utilisation volume, cost, INA-CBG, top
diagnoses); (3) Inpatient episode = per admission (LOS,
readmission, ACSC).
How to read. Every population figure
is a national weighted projection (PSTV15) from the BPJS reguler sample;
raw sample counts are reported separately. The definition of older
people follows Law 13/1998: age ≥60 years, with the pre-elderly 45–59
group as a comparator. Age is computed from date of birth (PSTV03)
relative to the year of service, so elderly status is set at the time of
the claim. Claims data describe only the JKN-served population, not the
entire elderly population.
Pillar map (navigation): Foundation (cohort, definitions, Table 1, sample-design validation) · A Burden & Demography · First Contact (point of entry) · B FKTP (Primary care) · C FKRTL (Referral/Hospital) · D Referral & Connectivity · E Member↔︎Facility Geography · F Geriatric Syndromes, ACSC & End-of-life · G Multimorbidity · H Economics · I Equity.
Aim: Define the analytic population (JKN members aged ≥45 with
≥1 FKTP or FKRTL claim at that age; older people = ≥60), characterise it
against the general elderly population, and frame the claims
limitations.
Source & design: BPJS Kesehatan Sample Data
2015–2024, reguler schema (~1% household sample of JKN members,
~2.59 million individuals in ~1.07 million families). Patients
identified via PSTV01, national weight PSTV15, demographics from the
official codebook (PSTV05 sex, PSTV08 segment, PSTV07 ward class, PSTV09
province of residence, PSTV18 year of death).
cf <- A$cohort_flow
flow <- data.frame(
Stage=c("Reguler-sample members aged ≥45 (latest snapshot)",
"Analytic cohort aged ≥45 with ≥1 claim (FKTP/FKRTL)",
" of whom, patients with an FKTP claim",
" of whom, patients with an FKRTL claim",
" of whom, patients with ≥1 inpatient admission",
"OLDER-PEOPLE COHORT (≥60) with ≥1 claim"),
`Patients (raw sample)`=c(cf$member_45plus_latest_def, cf$analytic_cohort_45plus,
cf$fktp_pts, cf$fkrtl_pts, cf$inpatient_pts, cf$analytic_cohort_lansia),
check.names=FALSE) |> wcsv("L0_1_cohort_flow")
gt::gt(flow) |>
gt::tab_header(title=gt::md("**Table L0.1: Cohort construction (STROBE)** · _Unit: Unique patients (sample)_"),
subtitle=gt::md("_BPJS reguler sample 2015–2024, age computed at time of service_")) |>
gt::fmt_number(columns=2, decimals=0, use_seps=TRUE) |>
gt::data_color(columns=2, colors=scales::col_numeric(c("#e3e8ee","#0b1f3a"),domain=NULL)) |>
gt::tab_source_note(gt::md(CAP)) |> gfmt()| Table L0.1: Cohort construction (STROBE) · Unit: Unique patients (sample) | |
| BPJS reguler sample 2015–2024, age computed at time of service | |
| Stage | Patients (raw sample) |
|---|---|
| Reguler-sample members aged ≥45 (latest snapshot) | 871,994 |
| Analytic cohort aged ≥45 with ≥1 claim (FKTP/FKRTL) | 590,123 |
| of whom, patients with an FKTP claim | 552,852 |
| of whom, patients with an FKRTL claim | 334,727 |
| of whom, patients with ≥1 inpatient admission | 209,887 |
| OLDER-PEOPLE COHORT (≥60) with ≥1 claim | 246,075 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | |
Following Law No. 13/1998 and MoH Regulation 67/2015: older people = residents aged ≥60 years. Analysis groups: Pre-elderly (45–59) as comparator, Young-old (60–69), Middle-old (70–79), Oldest-old (80+). Age is computed from date of birth (PSTV03) relative to the year of service; the database age column (~89% missing) is not used.
Demographic context (population benchmarks, BPS & SKI 2023). Indonesia has been an ageing society since 2021 (≥10% of the population aged ≥60). In 2023 older people reached 11.75% of the population with an old-age dependency ratio of 17.08 per 100 working-age people; age structure: 60–69 years 63.6%, 70–79 years 26.8%, ≥80 years 8.7%; majority female (52.3%). Projections: >14% by 2030 and ~20.3% (≈72 million) by 2045. Functionally, SKI 2023 recorded 95.0% of older people as fully independent in daily activities, 2.1% with mild dependency, and 2.9% with moderate-to-total dependency, the latter being the core of long-term-care need. Measured hypertension in older people rises from 49.5% (60–64) to 64.0% (75+), yet only 8.6% of adults have ever been diagnosed by a doctor, indicating a large detection/management gap.
n1 <- A$table1_n
gt::gt(A$table1, groupname_col="char", rowname_col="level") |>
gt::tab_header(title=gt::md("**Table 1: Characteristics of older patients vs the JKN elderly population** · _Unit: Unique patients (weighted), % per column_"),
subtitle=gt::md(sprintf("_FKTP n=%s · FKRTL n=%s · Overall n=%s · Elderly GenPop n=%s (sample members)_",
fmt(n1$raw[n1$kolom=="FKTP"]), fmt(n1$raw[n1$kolom=="FKRTL"]),
fmt(n1$raw[n1$kolom=="Overall (Lansia)"]), fmt(n1$raw[n1$kolom=="Elderly GenPop"])))) |>
gt::cols_label(FKTP="FKTP %", FKRTL="FKRTL %", Overall="Overall %", GeneralPop="Elderly GenPop %") |>
gt::fmt_number(columns=c(FKTP,FKRTL,Overall,GeneralPop), decimals=1) |>
gt::sub_missing(columns=everything(), missing_text="-") |>
gt::data_color(columns=Overall, colors=scales::col_numeric(c("#e3e8ee","#0b1f3a"),domain=NULL)) |>
gt::data_color(columns=GeneralPop, colors=scales::col_numeric(c("#e3e8ee","#13315c"),domain=NULL)) |>
gt::tab_source_note(gt::md(paste(CAP,"| Elderly GenPop = all reguler-sample members ≥60"))) |> gfmt()| Table 1: Characteristics of older patients vs the JKN elderly population · Unit: Unique patients (weighted), % per column | ||||
| FKTP n=227,804 · FKRTL n=161,915 · Overall n=246,075 · Elderly GenPop n=383,356 (sample members) | ||||
| FKTP % | FKRTL % | Overall % | Elderly GenPop % | |
|---|---|---|---|---|
| Sex | ||||
| Female | 52.4 | 49.8 | 52.1 | 50.6 |
| Male | 47.6 | 50.2 | 47.9 | 49.4 |
| Age group | ||||
| Young-old (60–69) | 58.1 | 58.0 | 57.3 | 49.8 |
| Middle-old (70–79) | 29.1 | 30.8 | 29.6 | 27.7 |
| Oldest-old (80+) | 12.8 | 11.2 | 13.1 | 22.5 |
| Membership segment | ||||
| PBI APBN | 52.8 | 37.6 | 51.5 | 48.3 |
| Non-worker | 15.7 | 22.5 | 16.3 | 17.6 |
| PBPU (self-paying) | 13.8 | 21.6 | 14.5 | 12.6 |
| PBI APBD | 13.9 | 13.0 | 13.9 | 16.6 |
| PPU (salaried) | 3.9 | 5.3 | 3.8 | 4.9 |
| Ward class | ||||
| Class III | 76.0 | 64.1 | 74.8 | 73.1 |
| Class I | 15.5 | 23.9 | 16.2 | 16.6 |
| Class II | 8.5 | 12.0 | 9.0 | 10.3 |
| Island | ||||
| Jawa | 64.9 | 61.9 | 64.4 | 59.5 |
| Sumatera | 17.5 | 19.5 | 17.7 | 17.5 |
| Sulawesi | 7.5 | 7.9 | 7.5 | 7.1 |
| Bali-Nusra | 5.2 | 5.1 | 5.2 | 5.3 |
| Kalimantan | 3.8 | 4.2 | 3.9 | 4.2 |
| Maluku-Papua | 1.1 | 1.4 | 1.2 | 6.4 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | Elderly GenPop = all reguler-sample members ≥60 | ||||
sp <- A$strata_patient |> mutate(grp=as.character(grp)) |> filter(!is.na(grp)) |> wcsv("L0_2_strata_usia")
gt::gt(sp) |>
gt::cols_label(grp="Age group", weighted="Patients (weighted)", raw="Patients (sample)", pct_w="Proportion (%)") |>
gt::tab_header(title=gt::md("**Table L0.2: Age-group distribution (≥45 cohort)** · _Unit: Unique patients (weighted)_")) |>
gt::fmt_number(columns=c(weighted,raw),decimals=0,use_seps=TRUE) |>
gt::data_color(columns=weighted, colors=scales::col_numeric(c("#e3e8ee","#0b1f3a"),domain=NULL)) |>
gt::tab_source_note(gt::md(CAP)) |> gfmt()| Table L0.2: Age-group distribution (≥45 cohort) · Unit: Unique patients (weighted) | |||
| Age group | Patients (weighted) | Patients (sample) | Proportion (%) |
|---|---|---|---|
| Pre-elderly (45–59) | 35,997,105 | 344,048 | 56.6 |
| Young-old (60–69) | 15,836,344 | 148,307 | 24.9 |
| Middle-old (70–79) | 8,179,931 | 70,965 | 12.9 |
| Oldest-old (80+) | 3,622,770 | 26,803 | 5.7 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | |||
Main limitation (applies throughout the document). The figures below are JKN members who reached a facility and whose claim was recorded, not the entire elderly population. Older people who never accessed care, or were served through non-claim programs (Posyandu/Posbindu Lansia, certain drug programs), are not captured. The cohort is built from the reguler household sample, so annual trends are relatively valid, though early years are slightly under-observed (members who left or died before sampling are not seen). There are no laboratory/vital values (blood pressure, HbA1c) or complete drug-dispensing data, so disease-control and polypharmacy indicators are only proxies.
py <- A$prev_year |> filter(!is.na(grp)) |> mutate(grp=factor(grp, levels=ABAND_LAB)) |> wcsv("A1_prev_year_band")
ggplot(py, aes(yr, wt_pts, fill=grp)) +
geom_col(width=.72) +
annotate("text", x=2020.5, y=max(A$prev_year_total$wt_pts)*1.02, label="COVID-19 dip 2020–21", color=RED, size=2.8, fontface="italic") +
scale_fill_manual(values=ABAND_COLS, name=NULL) +
yrx() + scale_y_continuous(labels=label_number(scale_cut=cut_short_scale()), expand=expansion(mult=c(0,.08))) +
labs(title="Population aged ≥45 served by JKN per year, by age group · Unit: unique patients",
subtitle="Weighted unique patients (national projection). Service dipped in 2020–2021 (pandemic restrictions).",
x=NULL, y="Patients (weighted)", caption=CAP) + th()pyt <- A$prev_year_total |> left_join(A$prev_year_lansia |> rename(lansia_raw=raw_pts, lansia_wt=wt_pts), by="yr") |>
transmute(Year=yr, `Age ≥45 (sample)`=raw_pts, `Age ≥45 (weighted)`=wt_pts,
`Older ≥60 (sample)`=lansia_raw, `Older ≥60 (weighted)`=lansia_wt) |> wcsv("A1_prev_year_total")
gt::gt(pyt) |>
gt::tab_header(title=gt::md("**Table A.1: Population ≥45 & older people ≥60 served by JKN per year** · _Unit: Unique patients (weighted)_")) |>
gt::fmt_number(columns=2:5, decimals=0, use_seps=TRUE) |>
gt::tab_source_note(gt::md(CAP)) |> gfmt()| Table A.1: Population ≥45 & older people ≥60 served by JKN per year · Unit: Unique patients (weighted) | ||||
| Year | Age ≥45 (sample) | Age ≥45 (weighted) | Older ≥60 (sample) | Older ≥60 (weighted) |
|---|---|---|---|---|
| 2015 | 99,900 | 8,445,870 | 37,139 | 3,378,952 |
| 2016 | 130,838 | 10,508,558 | 47,662 | 4,080,945 |
| 2017 | 183,000 | 19,025,247 | 65,988 | 7,386,420 |
| 2018 | 210,186 | 22,215,950 | 77,598 | 8,747,018 |
| 2019 | 234,588 | 25,329,517 | 88,222 | 10,050,800 |
| 2020 | 217,600 | 22,844,849 | 83,822 | 9,208,301 |
| 2021 | 222,929 | 23,110,229 | 84,432 | 9,118,251 |
| 2022 | 252,625 | 26,229,325 | 95,644 | 10,176,227 |
| 2023 | 281,364 | 29,593,704 | 110,101 | 11,875,655 |
| 2024 | 306,558 | 32,751,039 | 121,614 | 13,331,471 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | ||||
pyr <- A$pyramid |> mutate(sex_lab=ifelse(sex==1,"Male","Female"),
wt_signed=ifelse(sex==1,-wt,wt)) |> wcsv("A2_pyramid")
ord <- pyr |> distinct(ageband) |> arrange(ageband) |> pull(ageband)
pyr <- pyr |> mutate(ageband=factor(ageband, levels=ord))
mx <- max(pyr$wt)
ggplot(pyr, aes(wt_signed, ageband, fill=sex_lab)) +
geom_col(width=.85) +
scale_fill_manual(values=c("Male"=INK,"Female"=MIST), name=NULL) +
scale_x_continuous(labels=function(x) label_number(scale_cut=cut_short_scale())(abs(x)),
limits=c(-mx,mx)) +
labs(title="Age-sex pyramid of population aged ≥45 served by JKN · Unit: unique patients",
subtitle="Weighted unique patients. Female predominance strengthens in the oldest age groups.",
x="Patients (weighted)", y="Age group (5-year)", caption=CAP) + th()gi <- A$geo_island |> mutate(island=fct_reorder(island, wt_pts)) |> wcsv("A3_geo_island")
ggplot(gi, aes(wt_pts, island, fill=island)) + geom_col(width=.7) +
geom_text(aes(label=paste0(fmt(wt_pts)," (",pc1(pct),")")), hjust=-0.04, size=3, color=NAVY, fontface="bold") +
scale_fill_manual(values=ISLAND_COLS, guide="none") +
scale_x_continuous(labels=label_number(scale_cut=cut_short_scale()), expand=expansion(mult=c(0,.2))) +
labs(title="Distribution of older people (≥60) served, by island · Unit: unique patients",
subtitle="Weighted unique patients", x="Older people (weighted)", y=NULL, caption=CAP) + th()gp <- A$geo_prov |> select(Province=prov_name, Island=island, `Sample`=raw_pts, `Weighted`=wt_pts, `%`=pct) |>
head(15) |> wcsv("A3_geo_prov")
gt::gt(gp) |>
gt::tab_header(title=gt::md("**Table A.3: 15 provinces with the most older people served** · _Unit: Unique patients (weighted)_")) |>
gt::fmt_number(columns=c(Sample,Weighted),decimals=0,use_seps=TRUE) |>
gt::data_color(columns=Weighted, colors=scales::col_numeric(c("#e3e8ee","#0b1f3a"),domain=NULL)) |>
gt::tab_source_note(gt::md(CAP)) |> gfmt()| Table A.3: 15 provinces with the most older people served · Unit: Unique patients (weighted) | ||||
| Province | Island | Sample | Weighted | % |
|---|---|---|---|---|
| Jawa Timur | Java | 36,982 | 5,240,006 | 19.0 |
| Jawa Tengah | Java | 42,528 | 5,039,944 | 18.2 |
| Jawa Barat | Java | 31,959 | 4,449,205 | 16.1 |
| DKI Jakarta | Java | 5,697 | 1,316,903 | 4.8 |
| Sumatera Utara | Sumatra | 13,548 | 1,247,214 | 4.5 |
| Sulawesi Selatan | Sulawesi | 10,385 | 1,023,546 | 3.7 |
| Banten | Java | 5,025 | 887,440 | 3.2 |
| DI Yogyakarta | Java | 5,930 | 782,127 | 2.8 |
| Lampung | Sumatra | 7,514 | 748,069 | 2.7 |
| Sumatera Selatan | Sumatra | 8,393 | 676,056 | 2.4 |
| Sumatera Barat | Sumatra | 5,929 | 625,246 | 2.3 |
| Bali | Bali-Nusa Tenggara | 6,979 | 520,849 | 1.9 |
| Nusa Tenggara Barat | Bali-Nusa Tenggara | 3,227 | 498,362 | 1.8 |
| Aceh | Sumatra | 3,921 | 491,881 | 1.8 |
| Nusa Tenggara Timur | Bali-Nusa Tenggara | 6,106 | 419,488 | 1.5 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | ||||
First contact = the earliest dated claim observed for each older person (≥60) within the 2015–2024 window. When FKTP and FKRTL claims share the same date, the contact is counted via FKTP (entry through primary care). Because the observation window is limited, this is the first observed contact, not necessarily the first contact in a lifetime.
lvy <- A$lansia_visiting_year |> wcsv("E0_lansia_visiting_year")
ggplot(lvy, aes(yr, wt_pts)) +
geom_col(width=.7, fill=TEAL) +
geom_text(aes(label=label_number(scale_cut=cut_short_scale())(wt_pts)), vjust=-0.5, size=3, color=NAVY, fontface="bold") +
yrx() + scale_y_continuous(labels=label_number(scale_cut=cut_short_scale()), expand=expansion(mult=c(0,.1))) +
labs(title="Number of older people (≥60) attending JKN services per year · Unit: unique patients",
subtitle=sprintf("Total unique older people who ever attended 2015–2024 (sample): %s persons | weighted per year below",
fmt(A$lansia_visiting_total$n_lansia_visiting)),
x=NULL, y="Older people (weighted)", caption=CAP) + th()eo <- A$entry_overall |> wcsv("E1_entry_overall")
eo2 <- eo |> mutate(entry=factor(entry, levels=names(ENTRY_COLS)))
ggplot(eo2, aes(x=pct, y="", fill=entry)) + geom_col(width=.5) +
geom_text(aes(label=paste0(pc1(pct))), position=position_stack(vjust=.5), color="white", fontface="bold", size=3.4) +
scale_fill_manual(values=ENTRY_COLS, name=NULL) +
scale_x_continuous(labels=scales::percent_format(scale=1), expand=expansion(mult=c(0,.02))) +
guides(fill=guide_legend(nrow=1)) +
labs(title="Point of first contact of older people with the JKN system · Unit: unique patients (older people)",
subtitle="Share of older people by facility type at the first observed contact",
x="Share of older people", y=NULL, caption=CAP) + th() + theme(axis.text.y=element_blank())gt::gt(eo |> mutate(entry=stringr::str_remove(entry,"^[0-9] "))) |>
gt::cols_label(entry="Point of entry (first contact)", raw="Older people (sample)", wt="Older people (weighted)", pct="%") |>
gt::tab_header(title=gt::md("**Table FC.1: Point of first contact for older people** · _Unit: Unique patients (older people)_")) |>
gt::fmt_number(columns=c(raw,wt),decimals=0,use_seps=TRUE) |>
gt::tab_source_note(gt::md(CAP)) |> gfmt()| Table FC.1: Point of first contact for older people · Unit: Unique patients (older people) | |||
| Point of entry (first contact) | Older people (sample) | Older people (weighted) | % |
|---|---|---|---|
| FKTP (Primary care) | 194,992 | 22,347,989 | 79.2 |
| Hospital outpatient (direct) | 21,173 | 2,026,888 | 8.6 |
| Hospital inpatient (direct admission) | 29,910 | 3,264,169 | 12.2 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | |||
eb <- A$entry_band |> filter(!is.na(grp)) |> mutate(grp=factor(grp,levels=ABAND_LAB),
entry=factor(entry, levels=names(ENTRY_COLS))) |> wcsv("E2_entry_band")
ggplot(eb, aes(grp, pct, fill=entry)) + geom_col(width=.7, position="fill") +
geom_text(aes(label=ifelse(pct>=5,pc1(pct),"")), position=position_fill(vjust=.5), color="white", size=3, fontface="bold") +
scale_fill_manual(values=ENTRY_COLS, name=NULL) +
scale_y_continuous(labels=scales::percent, expand=expansion(mult=c(0,.02))) +
guides(fill=guide_legend(nrow=1)) +
labs(title="Point of first contact by age group · Unit: unique patients",
subtitle="The older the group, the larger the share entering directly via hospital inpatient admission.",
x=NULL, y="Proportion", caption=CAP) + th()es <- A$entry_segment |> mutate(entry=factor(entry, levels=names(ENTRY_COLS))) |> wcsv("E3_entry_segment")
p1 <- ggplot(es, aes(pbi, pct, fill=entry)) + geom_col(width=.65, position="fill") +
scale_fill_manual(values=ENTRY_COLS, name=NULL) +
scale_y_continuous(labels=scales::percent, expand=expansion(mult=c(0,.02))) +
guides(fill=guide_legend(nrow=1)) +
labs(title="By segment (PBI vs Non-PBI)", x=NULL, y="Proportion") + th()
ey <- A$entry_year |> mutate(entry=factor(entry, levels=names(ENTRY_COLS))) |> wcsv("E4_entry_year")
p2 <- ggplot(ey, aes(yr, pct, fill=entry)) + geom_area(position="fill", color="white", linewidth=.2) +
scale_fill_manual(values=ENTRY_COLS, name=NULL) +
yrx() + scale_y_continuous(labels=scales::percent, expand=expansion(mult=c(0,.02))) +
guides(fill=guide_legend(nrow=1)) +
labs(title="By year", x=NULL, y="Proportion") + th()
(p1 | p2) + patchwork::plot_annotation(
title="Point of first contact of older people by segment and year · Unit: unique patients",
caption=CAP, theme=theme(plot.title=element_text(color=NAVY,face="bold",family="serif",size=13)))ec <- A$entry_fkrtl_class |> wcsv("E5_entry_fkrtl_class")
gt::gt(ec) |>
gt::cols_label(kelas_rs="Hospital class (when entering FKRTL directly)", raw="Visits (sample)", pct="%") |>
gt::tab_header(title=gt::md("**Table FC.2: Hospital class at first contact entering FKRTL directly** · _Unit: Unique patients (FKRTL entry)_")) |>
gt::fmt_number(columns=raw,decimals=0,use_seps=TRUE) |>
gt::tab_source_note(gt::md(CAP)) |> gfmt()| Table FC.2: Hospital class at first contact entering FKRTL directly · Unit: Unique patients (FKRTL entry) | ||
| Hospital class (when entering FKRTL directly) | Visits (sample) | % |
|---|---|---|
| RS Kelas C | 24,923 | 48.8 |
| RS Kelas B | 15,284 | 29.9 |
| RS Kelas D | 7,541 | 14.8 |
| RS Kelas A | 1,784 | 3.5 |
| RS Khusus/Lain | 1,551 | 3.0 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | ||
ed <- A$entry_dx_top |> filter(icd3!="" & !is.na(icd3) & nchar(icd3)==3) |> head(12) |> addlab() |> wcsv("E6_entry_dx_top")
gt::gt(ed |> select(icd3, disease, raw)) |>
gt::cols_label(icd3="ICD-10", disease="Disease (first-contact diagnosis)", raw="Visits (sample)") |>
gt::tab_header(title=gt::md("**Table FC.3: 12 most common diagnoses at first contact of older people (code + name)** · _Unit: Visits (sample)_")) |>
gt::fmt_number(columns=raw,decimals=0,use_seps=TRUE) |>
gt::tab_source_note(gt::md(paste(CAP,"| Z codes (administrative contacts) excluded"))) |> gfmt()| Table FC.3: 12 most common diagnoses at first contact of older people (code + name) · Unit: Visits (sample) | ||
| ICD-10 | Disease (first-contact diagnosis) | Visits (sample) |
|---|---|---|
| I10 | Essential (primary) hypertension | 18,798 |
| M79 | Myalgia | 10,833 |
| J06 | Acute upper respiratory infection, unspecified | 8,876 |
| K30 | Dyspepsia | 7,271 |
| K29 | Gastritis, unspecified | 6,830 |
| J00 | Acute nasopharyngitis [common cold] | 6,056 |
| E11 | Non-insulin-dependent diabetes mellitus with unspecified complications | 5,691 |
| R51 | Headache | 3,503 |
| H25 | Senile cataract | 3,462 |
| R50 | Fever, unspecified | 3,120 |
| A09 | Diarrhoea and gastroenteritis of presumed infectious origin | 2,858 |
| M54 | Low back pain | 2,665 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | Z codes (administrative contacts) excluded | ||
fd <- A$fktp_dx_top |> filter(icd3!="" & !is.na(icd3) & nchar(icd3)==3) |> head(15) |> addlab() |>
mutate(lbl=fct_reorder(lbl, wt)) |> wcsv("B1_fktp_dx_top")
ggplot(fd, aes(wt, lbl)) + geom_col(width=.7, fill=TEAL) +
geom_text(aes(label=fmt(wt)), hjust=-0.05, size=2.9, color=NAVY) +
scale_x_continuous(labels=label_number(scale_cut=cut_short_scale()), expand=expansion(mult=c(0,.32))) +
labs(title="15 most common FKTP diagnoses in older/pre-elderly people · Unit: visits (weighted)",
subtitle="3-digit ICD-10 code + disease name, Z codes excluded", x="Visits (weighted)", y=NULL, caption=CAP) + th()gt::gt(fd |> select(icd3, disease, raw, wt)) |>
gt::cols_label(icd3="ICD-10", disease="Disease", raw="Visits (sample)", wt="Visits (weighted)") |>
gt::tab_header(title=gt::md("**Table B.1: 15 most common FKTP diagnoses (code + disease name)** · _Unit: Visits_")) |>
gt::fmt_number(columns=c(raw,wt),decimals=0,use_seps=TRUE) |>
gt::data_color(columns=wt, colors=scales::col_numeric(c("#e3e8ee","#0b1f3a"),domain=NULL)) |>
gt::tab_source_note(gt::md(paste(CAP,"| disease names from FKP15A"))) |> gfmt()| Table B.1: 15 most common FKTP diagnoses (code + disease name) · Unit: Visits | |||
| ICD-10 | Disease | Visits (sample) | Visits (weighted) |
|---|---|---|---|
| I10 | Essential (primary) hypertension | 882,856 | 71,744,841 |
| E11 | Non-insulin-dependent diabetes mellitus with unspecified complications | 596,534 | 46,833,760 |
| M79 | Myalgia | 404,050 | 35,144,185 |
| J06 | Acute upper respiratory infection, unspecified | 393,054 | 31,226,963 |
| K30 | Dyspepsia | 280,159 | 21,390,740 |
| J00 | Acute nasopharyngitis [common cold] | 274,862 | 21,672,320 |
| K29 | Gastritis, unspecified | 232,162 | 18,779,412 |
| R51 | Headache | 160,827 | 13,811,426 |
| I11 | Hypertensive heart disease | 141,640 | 10,959,973 |
| K04 | Pulpitis | 129,696 | 11,153,512 |
| M54 | Low back pain | 107,546 | 8,103,949 |
| R50 | Fever, unspecified | 103,865 | 8,504,834 |
| J02 | Acute pharyngitis, unspecified | 95,420 | 6,919,873 |
| I50 | Congestive heart failure | 93,149 | 7,749,006 |
| I15 | Secondary hypertension | 86,315 | 5,969,524 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | disease names from FKP15A | |||
cov <- A$coverage |> wcsv("B2_coverage")
gt::gt(cov) |>
gt::cols_label(grpc="Service coverage (older people)", wt="Patients (weighted)", raw="Patients (sample)", pct="%") |>
gt::tab_header(title=gt::md("**Table B.2: FKTP vs FKRTL coverage in older people** · _Unit: Unique patients (weighted)_")) |>
gt::fmt_number(columns=c(wt,raw),decimals=0,use_seps=TRUE) |>
gt::tab_source_note(gt::md(CAP)) |> gfmt()| Table B.2: FKTP vs FKRTL coverage in older people · Unit: Unique patients (weighted) | |||
| Service coverage (older people) | Patients (weighted) | Patients (sample) | % |
|---|---|---|---|
| FKTP + Hospital | 13,081,710 | 143,644 | 47.3 |
| FKTP only | 12,562,519 | 84,160 | 45.5 |
| Hospital only | 1,994,816 | 18,271 | 7.2 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | |||
uy <- A$util_year |> wcsv("C1_util_year")
ggplot(uy, aes(yr, wt_visits, color=level)) + geom_line(linewidth=1) + geom_point(size=1.9) +
geom_text(aes(label=label_number(accuracy=0.1, scale_cut=cut_short_scale())(wt_visits)),
vjust=-0.9, size=2.5, fontface="bold", show.legend=FALSE) +
scale_color_manual(values=LVL_COLS, name=NULL) +
yrx() + scale_y_continuous(labels=label_number(scale_cut=cut_short_scale()), limits=c(0,NA), expand=expansion(mult=c(.02,.13))) +
guides(color=guide_legend(nrow=1)) +
labs(title="Visit volume by level of care per year (age ≥45) · Unit: visits",
subtitle="Weighted visits. The 2020–2021 decline reflects pandemic restrictions.",
x=NULL, y="Visits (weighted)", caption=CAP) + th()lb <- A$los_band |> filter(!is.na(grp)) |> mutate(grp=factor(grp,levels=ABAND_LAB)) |> wcsv("C2_los_band")
ggplot(lb, aes(grp, median_los, fill=grp)) + geom_col(width=.6) +
geom_errorbar(aes(ymin=q1, ymax=q3), width=.2, color=NAVY) +
geom_text(aes(label=median_los), vjust=-0.6, size=3.2, color=NAVY, fontface="bold") +
scale_fill_manual(values=ABAND_COLS, guide="none") +
scale_y_continuous(expand=expansion(mult=c(0,.12)), limits=c(0,NA)) +
labs(title="Median length of stay (LOS) by age group · Unit: inpatient episodes",
subtitle="Median (bars) with interquartile range (Q1–Q3). Days.",
x=NULL, y="Median LOS (days)", caption=CAP) + th()sev <- A$severity |> mutate(pct=round(100*wt/sum(wt),1)) |> wcsv("C3_severity")
gt::gt(sev) |>
gt::cols_label(severity="Severity (FKL23, older people)", raw="Episodes (sample)", wt="Episodes (weighted)", pct="%") |>
gt::tab_header(title=gt::md("**Table C.3: Inpatient severity in older people** · _Unit: Inpatient episodes_")) |>
gt::fmt_number(columns=c(raw,wt),decimals=0,use_seps=TRUE) |>
gt::tab_source_note(gt::md(paste(CAP,"| FKL23: 1=Mild, 2=Moderate, 3=Severe"))) |> gfmt()| Table C.3: Inpatient severity in older people · Unit: Inpatient episodes | |||
| Severity (FKL23, older people) | Episodes (sample) | Episodes (weighted) | % |
|---|---|---|---|
| 2 Moderate | 142,144 | 12,729,098 | 63.7 |
| 3 Severe | 55,411 | 5,078,601 | 25.4 |
| 9 Other | 22,583 | 2,164,340 | 10.8 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | FKL23: 1=Mild, 2=Moderate, 3=Severe | |||
idx <- A$fkrtl_inp_dx_top |> filter(icd3!="" & !is.na(icd3) & nchar(icd3)==3) |> head(15) |> addlab() |> wcsv("C4_inp_dx_top")
gt::gt(idx |> select(icd3, disease, raw, wt)) |>
gt::cols_label(icd3="ICD-10", disease="Disease (inpatient diagnosis)", raw="Episodes (sample)", wt="Episodes (weighted)") |>
gt::tab_header(title=gt::md("**Table C.4: 15 most common inpatient diagnoses in older people (code + name)** · _Unit: Inpatient episodes_")) |>
gt::fmt_number(columns=c(raw,wt),decimals=0,use_seps=TRUE) |>
gt::data_color(columns=wt, colors=scales::col_numeric(c("#e3e8ee","#0b1f3a"),domain=NULL)) |>
gt::tab_source_note(gt::md(paste(CAP,"| disease names from FKL16A, Z codes excluded"))) |> gfmt()| Table C.4: 15 most common inpatient diagnoses in older people (code + name) · Unit: Inpatient episodes | |||
| ICD-10 | Disease (inpatient diagnosis) | Episodes (sample) | Episodes (weighted) |
|---|---|---|---|
| R06 | Dyspnoea | 8,205 | 776,381 |
| I50 | Congestive heart failure | 6,793 | 635,553 |
| R10 | Other and unspecified abdominal pain | 6,614 | 603,008 |
| K30 | Dyspepsia | 6,389 | 531,945 |
| R50 | Fever, unspecified | 5,357 | 497,984 |
| E11 | Non-insulin-dependent diabetes mellitus with unspecified complications | 5,251 | 471,383 |
| I64 | Stroke, not specified as haemorrhage or infarction | 4,972 | 512,093 |
| A09 | Diarrhoea and gastroenteritis of presumed infectious origin | 4,561 | 421,629 |
| N18 | Chronic kidney disease, stage 5 | 4,497 | 364,095 |
| I10 | Essential (primary) hypertension | 4,002 | 336,761 |
| D64 | Anaemia, unspecified | 3,838 | 343,017 |
| J18 | Bronchopneumonia, unspecified | 3,778 | 349,262 |
| I63 | Cerebral infarction, unspecified | 3,283 | 317,135 |
| J44 | Chronic obstructive pulmonary disease, unspecified | 3,040 | 272,683 |
| R11 | Nausea and vomiting | 2,938 | 261,637 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | disease names from FKL16A, Z codes excluded | |||
odx <- A$fkrtl_out_dx_top |> filter(icd3!="" & !is.na(icd3) & nchar(icd3)==3) |> head(15) |> addlab() |> wcsv("C5_out_dx_top")
gt::gt(odx |> select(icd3, disease, raw, wt)) |>
gt::cols_label(icd3="ICD-10", disease="Disease (outpatient diagnosis)", raw="Visits (sample)", wt="Visits (weighted)") |>
gt::tab_header(title=gt::md("**Table C.5: 15 most common FKRTL outpatient diagnoses in older people (code + name)** · _Unit: Outpatient visits_")) |>
gt::fmt_number(columns=c(raw,wt),decimals=0,use_seps=TRUE) |>
gt::data_color(columns=wt, colors=scales::col_numeric(c("#e3e8ee","#13315c"),domain=NULL)) |>
gt::tab_source_note(gt::md(paste(CAP,"| disease names from FKL16A, Z codes excluded"))) |> gfmt()| Table C.5: 15 most common FKRTL outpatient diagnoses in older people (code + name) · Unit: Outpatient visits | |||
| ICD-10 | Disease (outpatient diagnosis) | Visits (sample) | Visits (weighted) |
|---|---|---|---|
| N18 | Chronic kidney disease, stage 5 | 128,796 | 10,607,488 |
| E11 | Non-insulin-dependent diabetes mellitus with unspecified complications | 114,785 | 10,690,608 |
| I50 | Congestive heart failure | 95,677 | 8,382,589 |
| M54 | Low back pain | 92,379 | 7,920,154 |
| I11 | Hypertensive heart disease | 83,480 | 7,373,754 |
| H25 | Senile cataract | 70,310 | 6,408,179 |
| I25 | Chronic ischaemic heart disease | 69,007 | 6,555,977 |
| I64 | Stroke, not specified as haemorrhage or infarction | 46,493 | 4,429,230 |
| J44 | Chronic obstructive pulmonary disease, unspecified | 42,571 | 3,904,093 |
| H26 | Cataract, unspecified | 41,582 | 3,765,020 |
| I63 | Cerebral infarction, unspecified | 31,649 | 2,958,375 |
| N40 | Hyperplasia of prostate | 30,961 | 3,115,673 |
| M19 | Arthrosis, unspecified | 26,517 | 2,436,244 |
| M51 | Intervertebral disc disorder, unspecified | 24,231 | 2,211,309 |
| M17 | narthrosis [arthrosis of knee] | 23,443 | 2,262,922 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | disease names from FKL16A, Z codes excluded | |||
rr <- A$referral_rate_year |> wcsv("D1_referral_rate_year")
rt <- A$referrer_type |> head(8) |> wcsv("D2_referrer_type")
gt::gt(rt |> select(perujuk, raw, pct)) |>
gt::cols_label(perujuk="Referrer type (inpatient, older people)", raw="Visits (sample)", pct="%") |>
gt::tab_header(title=gt::md("**Table D.2: Referrer type for inpatient admissions of older people (FKL28)** · _Unit: Inpatient visits_")) |>
gt::fmt_number(columns=raw,decimals=0,use_seps=TRUE) |>
gt::tab_source_note(gt::md(CAP)) |> gfmt()| Table D.2: Referrer type for inpatient admissions of older people (FKL28) · Unit: Inpatient visits | ||
| Referrer type (inpatient, older people) | Visits (sample) | % |
|---|---|---|
| Hospital | 176,047 | 83.4 |
| Puskesmas (PHC) | 21,072 | 10.0 |
| GP | 7,124 | 3.4 |
| Primary clinic | 5,162 | 2.4 |
| Specialist clinic | 1,536 | 0.7 |
| Pharmacy | 47 | 0.0 |
| Other | 14 | 0.0 |
| Other | 14 | 0.0 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | ||
rg <- A$referral_geo
rgd <- data.frame(Category=c("Within the same district/city","Cross-district (within province)","Cross-province"),
`Visits (sample)`=c(rg$same_kab, rg$cross_kab, rg$cross_prov),
`%`=round(100*c(rg$same_kab, rg$cross_kab, rg$cross_prov)/rg$tot,1), check.names=FALSE) |> wcsv("D3_referral_geo")
gt::gt(rgd) |>
gt::tab_header(title=gt::md("**Table D.3: Referral geography for inpatient admissions of older people** · _Unit: Inpatient visits_")) |>
gt::fmt_number(columns=2,decimals=0,use_seps=TRUE) |>
gt::tab_source_note(gt::md(CAP)) |> gfmt()| Table D.3: Referral geography for inpatient admissions of older people · Unit: Inpatient visits | ||
| Category | Visits (sample) | % |
|---|---|---|
| Within the same district/city | 199,367 | 94.5 |
| Cross-district (within province) | 9,533 | 4.5 |
| Cross-province | 2,132 | 1.0 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | ||
mg <- A$mf_geo
mgd <- data.frame(Indicator=c("Registered facility in the same province","Registered facility in the same district/city"),
`%`=c(mg$same_prov, mg$same_kab), check.names=FALSE) |> wcsv("E1_member_faskes")
gt::gt(mgd) |>
gt::tab_header(title=gt::md("**Table E.1: Match between residence & registered facility (older people)** · _Unit: Members_")) |>
gt::tab_source_note(gt::md(CAP)) |> gfmt()| Table E.1: Match between residence & registered facility (older people) · Unit: Members | |
| Indicator | % |
|---|---|
| Registered facility in the same province | 96.1 |
| Registered facility in the same district/city | 90.6 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | |
gpv <- A$geriatric_prev |> filter(n_pts>0) |> mutate(syndrome=fct_reorder(syndrome, pct)) |> wcsv("F1_geriatric_prev")
ggplot(gpv, aes(pct, syndrome)) + geom_col(width=.7, fill=PURPLE) +
geom_text(aes(label=paste0(pc1(pct)," (",fmt(wt_pts),")")), hjust=-0.04, size=2.8, color=NAVY) +
scale_x_continuous(expand=expansion(mult=c(0,.22)), limits=c(0,NA)) +
labs(title="Served prevalence of geriatric syndromes in older people (≥60) · Unit: unique patients",
subtitle="% of older people served with ≥1 claim for the condition (primary or secondary diagnosis). Figures in parentheses = weighted patients.",
x="% of older people served", y=NULL, caption=CAP) + th()gbb <- A$geriatric_by_band |> filter(!is.na(grp)) |>
group_by(key) |> filter(max(pct)>=1) |> ungroup() |>
mutate(grp=factor(grp,levels=ABAND_LAB)) |> wcsv("F2_geriatric_by_band")
ggplot(gbb, aes(grp, pct, group=syndrome, color=syndrome)) +
geom_line(linewidth=.8) + geom_point(size=1.6) +
scale_y_continuous(limits=c(0,NA)) +
labs(title="Gradient of geriatric syndromes by age group · Unit: unique patients",
subtitle="% served per age group (conditions with ≥1% prevalence shown). Burden rises sharply with age.",
x=NULL, y="% served", color=NULL, caption=CAP) + th() +
guides(color=guide_legend(nrow=3))ACSC (OECD HCQI framework) = conditions that should be manageable in primary care, so their inpatient admissions are potentially avoidable (heart failure, hypertension, diabetes complications, COPD, asthma, pneumonia, UTI, dehydration, angina, cellulitis). A high ACSC share signals an opportunity to strengthen primary care for older people.
ab <- A$acsc_breakdown |> filter(n_adm>0) |> mutate(condition=fct_reorder(condition, n_adm)) |> wcsv("F3_acsc_breakdown")
ov <- A$acsc_overall
ggplot(ab, aes(n_adm, condition)) + geom_col(width=.7, fill=AMBER) +
geom_text(aes(label=fmt(n_adm)), hjust=-0.05, size=2.9, color=NAVY) +
scale_x_continuous(labels=label_number(scale_cut=cut_short_scale()), expand=expansion(mult=c(0,.16))) +
labs(title="Inpatient admissions of older people for avoidable conditions (ACSC) · Unit: inpatient episodes",
subtitle=sprintf("A total of %s of %s inpatient admissions of older people (%s) are from ACSC conditions",
fmt(ov$n_acsc), fmt(ov$n_adm), pc1(ov$pct_acsc)),
x="Inpatient episodes (sample)", y=NULL, caption=CAP) + th()rd <- A$readmit
po <- A$polypharmacy_proxy
mo <- A$mort_overall
eol <- A$eol
out <- data.frame(
Indicator=c("30-day readmission (all-cause)","90-day readmission (all-cause)",
"Older people with ≥2 chronic conditions (medication-burden proxy)","Older people with ≥3 chronic conditions",
"Crude mortality of older people (PSTV18, undercount)",
"Mean admissions in year of death","Mean FKRTL cost in year of death (Rp)"),
Value=c(pc1(rd$readmit30_pct), pc1(rd$readmit90_pct), pc1(po$pct_mm2), pc1(po$pct_mm3),
pc1(mo$crude_pct), formatC(eol$mean_adm_death_yr,format="f",digits=2),
fmt(eol$mean_cost_death_yr))) |> wcsv("F4_outcomes")
gt::gt(out) |>
gt::tab_header(title=gt::md("**Table F.4: Readmission, mortality & end-of-life (older people ≥60)** · _Unit: mixed (see row)_")) |>
gt::tab_source_note(gt::md(paste(CAP,"| mortality via PSTV18 only for snapshot years, undercount; polypharmacy not directly measurable (no dispensing data), chronic-condition count used as proxy"))) |> gfmt()| Table F.4: Readmission, mortality & end-of-life (older people ≥60) · Unit: mixed (see row) | |
| Indicator | Value |
|---|---|
| 30-day readmission (all-cause) | 16.1% |
| 90-day readmission (all-cause) | 25.8% |
| Older people with ≥2 chronic conditions (medication-burden proxy) | 37.4% |
| Older people with ≥3 chronic conditions | 20.8% |
| Crude mortality of older people (PSTV18, undercount) | 16.5% |
| Mean admissions in year of death | 0.61 |
| Mean FKRTL cost in year of death (Rp) | 6,236,232 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | mortality via PSTV18 only for snapshot years, undercount; polypharmacy not directly measurable (no dispensing data), chronic-condition count used as proxy | |
md <- A$mm_dist |> mutate(mm_cat=factor(mm_cat, levels=c("0","1","2","3","4+"))) |> wcsv("G1_mm_dist")
ggplot(md, aes(mm_cat, pct, fill=mm_cat)) + geom_col(width=.7) +
geom_text(aes(label=pc1(pct)), vjust=-0.5, size=3.2, color=NAVY, fontface="bold") +
scale_fill_manual(values=c("0"=LGRAY,"1"=MIST,"2"=SLATE,"3"=NAVY2,"4+"=INK), guide="none") +
scale_y_continuous(expand=expansion(mult=c(0,.12)), limits=c(0,NA)) +
labs(title="Distribution of number of chronic diseases in older people (≥60) · Unit: unique patients",
subtitle="Across 14 chronic-disease categories (see Pillar F/G). Weighted.",
x="Number of chronic conditions", y="% of older people", caption=CAP) + th()mb <- A$mm_by_band |> filter(!is.na(grp)) |> mutate(grp=factor(grp,levels=ABAND_LAB)) |> arrange(grp) |> wcsv("G2_mm_by_band")
gt::gt(mb) |>
gt::cols_label(grp="Age group", n="Patients (sample)", mean_ncc="Mean number of conditions",
pct_mm2="≥2 conditions (%)", pct_mm3="≥3 conditions (%)") |>
gt::tab_header(title=gt::md("**Table G.2: Multimorbidity by age group** · _Unit: Unique patients_")) |>
gt::fmt_number(columns=n,decimals=0,use_seps=TRUE) |>
gt::data_color(columns=pct_mm2, colors=scales::col_numeric(c("#e3e8ee","#274060"),domain=NULL)) |>
gt::tab_source_note(gt::md(CAP)) |> gfmt()| Table G.2: Multimorbidity by age group · Unit: Unique patients | ||||
| Age group | Patients (sample) | Mean number of conditions | ≥2 conditions (%) | ≥3 conditions (%) |
|---|---|---|---|---|
| Pre-elderly (45–59) | 344,048 | 0.79 | 19.8 | 9.3 |
| Young-old (60–69) | 148,307 | 1.41 | 37.2 | 20.5 |
| Middle-old (70–79) | 70,965 | 1.52 | 39.6 | 22.4 |
| Oldest-old (80+) | 26,803 | 1.28 | 32.7 | 17.9 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | ||||
cp <- A$chronic_prev |> mutate(condition=fct_reorder(condition, pct)) |> wcsv("G3_chronic_prev")
ggplot(cp, aes(pct, condition)) + geom_col(width=.7, fill=BLUE) +
geom_text(aes(label=pc1(pct)), hjust=-0.05, size=2.9, color=NAVY) +
scale_x_continuous(expand=expansion(mult=c(0,.12)), limits=c(0,NA)) +
labs(title="Served prevalence of chronic disease in older people (≥60) · Unit: unique patients",
subtitle="% of older people served with ≥1 claim for the condition (primary or secondary diagnosis, FKTP+FKRTL)",
x="% of older people served", y=NULL, caption=CAP) + th()ht_served <- A$chronic_prev$pct[A$chronic_prev$key=="ht"]
dm_served <- A$chronic_prev$pct[A$chronic_prev$key=="dm"]dy <- A$mm_dyads |> head(12) |> wcsv("G4_mm_dyads")
gt::gt(dy) |>
gt::cols_label(pair="Pair of chronic diseases (older people)", n_pts="Patients (sample)", wt_pts="Patients (weighted)") |>
gt::tab_header(title=gt::md("**Table G.4: 12 most common chronic-disease pairs in older people** · _Unit: Unique patients_")) |>
gt::fmt_number(columns=c(n_pts,wt_pts),decimals=0,use_seps=TRUE) |>
gt::data_color(columns=wt_pts, colors=scales::col_numeric(c("#e3e8ee","#0b1f3a"),domain=NULL)) |>
gt::tab_source_note(gt::md(CAP)) |> gfmt()| Table G.4: 12 most common chronic-disease pairs in older people · Unit: Unique patients | ||
| Pair of chronic diseases (older people) | Patients (sample) | Patients (weighted) |
|---|---|---|
| Hypertension (I10–15) + Diabetes (E10–14) | 34,153 | 2,991,684 |
| Hypertension (I10–15) + Osteoarthritis (M15–19) | 24,324 | 2,032,967 |
| Hypertension (I10–15) + Ischaemic heart disease (I20–25) | 21,257 | 1,866,315 |
| Hypertension (I10–15) + Chronic lung disease (J40–47) | 20,564 | 1,728,422 |
| Hypertension (I10–15) + Heart failure (I50) | 20,322 | 1,770,193 |
| Hypertension (I10–15) + Stroke (I60–69) | 18,866 | 1,686,076 |
| Ischaemic heart disease (I20–25) + Heart failure (I50) | 14,465 | 1,289,282 |
| Diabetes (E10–14) + Osteoarthritis (M15–19) | 11,591 | 956,943 |
| Hypertension (I10–15) + Anaemia (D50–64) | 10,988 | 927,286 |
| Diabetes (E10–14) + Ischaemic heart disease (I20–25) | 10,631 | 944,642 |
| Hypertension (I10–15) + Chronic kidney disease (N17–19) | 9,783 | 869,967 |
| Diabetes (E10–14) + Heart failure (I50) | 9,675 | 851,192 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | ||
cy <- A$cost_year |> wcsv("H1_cost_year")
ggplot(cy, aes(yr, total_bil_idr/1000)) + geom_col(width=.7, fill=NAVY) +
geom_text(aes(label=formatC(total_bil_idr/1000,format="f",digits=1)), vjust=-0.5, size=2.8, color=NAVY) +
yrx() + scale_y_continuous(expand=expansion(mult=c(0,.12)), limits=c(0,NA)) +
labs(title="JKN FKRTL spending on older people (≥60) per year · Unit: claims (weighted cost)",
subtitle="Trillion Rupiah, national projection (FKL47 × PSTV15 weight)", x=NULL, y="Trillion Rp", caption=CAP) + th()cpb <- A$cost_percapita_band |> filter(!is.na(grp)) |> mutate(grp=factor(grp,levels=ABAND_LAB)) |> arrange(grp) |> wcsv("H2_cost_percapita_band")
gt::gt(cpb) |>
gt::cols_label(grp="Age group", n="Patients (sample)", wt_pts="Patients (weighted)",
tot_cost_bil="Total cost (billion Rp)", mean_cost_pp="Mean cost/patient (Rp)") |>
gt::tab_header(title=gt::md("**Table H.2: FKRTL per-capita cost by age group** · _Unit: Unique patients_")) |>
gt::fmt_number(columns=c(n,wt_pts,tot_cost_bil,mean_cost_pp),decimals=0,use_seps=TRUE) |>
gt::data_color(columns=mean_cost_pp, colors=scales::col_numeric(c("#e3e8ee","#0b1f3a"),domain=NULL)) |>
gt::tab_source_note(gt::md(CAP)) |> gfmt()| Table H.2: FKRTL per-capita cost by age group · Unit: Unique patients | ||||
| Age group | Patients (sample) | Patients (weighted) | Total cost (billion Rp) | Mean cost/patient (Rp) |
|---|---|---|---|---|
| Pre-elderly (45–59) | 344,048 | 35,997,105 | 168,057 | 11,239,001 |
| Young-old (60–69) | 148,307 | 15,836,344 | 130,063 | 14,875,759 |
| Middle-old (70–79) | 70,965 | 8,179,931 | 69,669 | 14,986,629 |
| Oldest-old (80+) | 26,803 | 3,622,770 | 21,401 | 12,705,107 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | ||||
cb <- A$cbg_top |> head(12) |> wcsv("H3_cbg_top")
gt::gt(cb) |>
gt::cols_label(cbg="INA-CBG (older people)", n_claims="Claims (sample)", total_bil_idr="Total (billion Rp)", mean_cost="Mean/claim (Rp)") |>
gt::tab_header(title=gt::md("**Table H.3: 12 INA-CBG with the most claims in older people** · _Unit: Claims_")) |>
gt::fmt_number(columns=c(n_claims,total_bil_idr,mean_cost),decimals=0,use_seps=TRUE) |>
gt::tab_source_note(gt::md(CAP)) |> gfmt()| Table H.3: 12 INA-CBG with the most claims in older people · Unit: Claims | |||
| INA-CBG (older people) | Claims (sample) | Total (billion Rp) | Mean/claim (Rp) |
|---|---|---|---|
| PENYAKIT KRONIS KECIL LAIN-LAIN | 1,259,668 | 22,069 | 199,751 |
| PROSEDUR THERAPI FISIK DAN PROSEDUR KECIL MUSKULOSKLETAL | 200,847 | 2,182 | 125,262 |
| PROSEDUR DIALISIS | 145,842 | 10,248 | 862,705 |
| 730 | 106,164 | 1,820 | 182,227 |
| PROSEDUR LAIN-LAIN PADA MATA | 68,077 | 1,339 | 236,445 |
| KONSULTASI ATAU PEMERIKSAAN LAIN-LAIN | 46,775 | 615 | 143,550 |
| PENYAKIT KRONIS KECIL LAINLAIN | 46,622 | 851 | 196,154 |
| PROSEDUR REHABILITASI | 44,325 | 604 | 159,278 |
| PERAWATAN LUKA | 38,720 | 683 | 197,655 |
| PENYAKIT AKUT KECIL LAIN-LAIN | 24,145 | 411 | 197,209 |
| PROSEDUR ULTRASOUND LAIN-LAIN | 23,366 | 1,171 | 582,906 |
| PROSEDUR OPERASI KATARAK | 22,406 | 13,047 | 6,555,477 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | |||
pareto <- A$pareto |> wcsv("H4_pareto")
gt::gt(pareto) |>
gt::cols_label(top_pct="Top % of patients (highest cost)", spend_share="Share of total spend (%)") |>
gt::tab_header(title=gt::md("**Table H.4: Spending concentration (Pareto) in older people** · _Unit: Unique patients (weighted)_")) |>
gt::tab_source_note(gt::md(paste(CAP,sprintf("| Cumulative FKRTL spending on older people ~Rp %s trillion (weighted)", formatC(A$cost_total_bil_lansia/1000,format="f",digits=1))))) |> gfmt()| Table H.4: Spending concentration (Pareto) in older people · Unit: Unique patients (weighted) | |
| Top % of patients (highest cost) | Share of total spend (%) |
|---|---|
| 1 | 15.5 |
| 5 | 36.9 |
| 10 | 51.2 |
| 20 | 68.1 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | Cumulative FKRTL spending on older people ~Rp 221.1 trillion (weighted) | |
sr <- A$served_rate_prov |> filter(!is.na(rate_per1000)) |> arrange(desc(rate_per1000)) |> head(20) |>
mutate(prov_name=fct_reorder(prov_name, rate_per1000)) |> wcsv("I1_served_rate_prov")
ggplot(sr, aes(rate_per1000, prov_name, fill=island)) + geom_col(width=.72) +
geom_text(aes(label=fmt(rate_per1000)), hjust=-0.06, size=2.7, color=NAVY) +
scale_fill_manual(values=ISLAND_COLS, name=NULL) +
scale_x_continuous(expand=expansion(mult=c(0,.14)), limits=c(0,NA)) +
labs(title="Older people served per 1,000 older members, by province · Unit: unique patients",
subtitle="Older people served (≥1 claim) ÷ registered older members (reguler sample) × 1,000",
x="Older people served per 1,000 older members", y=NULL, caption=CAP) + th()eq <- A$equity_segment |> select(segmen, pbi, n_pts, served_rate_per1000, mean_visits, pct_inp, mean_cost_pp, mean_ncc) |> wcsv("I2_equity_segment")
gt::gt(eq) |>
gt::cols_label(segmen="Membership segment", pbi="Group", n_pts="Patients (sample)",
served_rate_per1000="Served /1,000", mean_visits="Mean visits",
pct_inp="% ever admitted", mean_cost_pp="Mean cost/patient (Rp)", mean_ncc="Mean chronic conditions") |>
gt::tab_header(title=gt::md("**Table I.2: Utilisation & served rate of older people by segment (SES)** · _Unit: Unique patients_")) |>
gt::fmt_number(columns=c(n_pts,served_rate_per1000,mean_cost_pp),decimals=0,use_seps=TRUE) |>
gt::data_color(columns=served_rate_per1000, colors=scales::col_numeric(c("#e3e8ee","#13315c"),domain=NULL)) |>
gt::tab_source_note(gt::md(paste(CAP,"| PBI = subsidized-premium members; Non-PBI = self-paying/salaried"))) |> gfmt()| Table I.2: Utilisation & served rate of older people by segment (SES) · Unit: Unique patients | |||||||
| Membership segment | Group | Patients (sample) | Served /1,000 | Mean visits | % ever admitted | Mean cost/patient (Rp) | Mean chronic conditions |
|---|---|---|---|---|---|---|---|
| PBPU (self-paying) | Non-PBI | 66,267 | 630 | 36.9 | 53.0 | 19,432,831 | 1.73 |
| PBI APBN | PBI (subsidized) | 93,324 | 585 | 23.0 | 34.8 | 9,000,417 | 0.97 |
| Non-worker | Non-PBI | 47,147 | 508 | 49.6 | 55.8 | 20,254,878 | 1.93 |
| PBI APBD | PBI (subsidized) | 25,632 | 458 | 25.5 | 42.2 | 11,582,548 | 1.23 |
| PPU (salaried) | Non-PBI | 13,705 | 428 | 43.8 | 51.0 | 19,393,674 | 1.74 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | PBI = subsidized-premium members; Non-PBI = self-paying/salaried | |||||||
ri <- A$repr_index |> filter(!is.na(repr)) |> arrange(desc(abs(repr-1))) |> head(14) |> wcsv("I3_repr_index")
gt::gt(ri) |>
gt::cols_label(char="Characteristic", level="Category", Overall="Served %", GeneralPop="Elderly GenPop %", repr="Representation index") |>
gt::tab_header(title=gt::md("**Table I.3: Representation index, older people served vs general elderly population** · _Unit: Unique patients_")) |>
gt::fmt_number(columns=c(Overall,GeneralPop),decimals=1) |>
gt::fmt_number(columns=repr,decimals=2) |>
gt::data_color(columns=repr, colors=scales::col_numeric(c("#c7d2dd","#f3f5f8","#0b1f3a"),domain=c(0,2))) |>
gt::tab_source_note(gt::md(paste(CAP,"| Index >1 = over-representation among those served, <1 = under-representation"))) |> gfmt()| Table I.3: Representation index, older people served vs general elderly population · Unit: Unique patients | ||||
| Characteristic | Category | Served % | Elderly GenPop % | Representation index |
|---|---|---|---|---|
| Island | Maluku-Papua | 1.2 | 6.4 | 0.19 |
| Age group | Oldest-old (80+) | 13.1 | 22.5 | 0.58 |
| Membership segment | PPU (salaried) | 3.8 | 4.9 | 0.78 |
| Membership segment | PBI APBD | 13.9 | 16.6 | 0.84 |
| Age group | Young-old (60–69) | 57.3 | 49.8 | 1.15 |
| Membership segment | PBPU (self-paying) | 14.5 | 12.6 | 1.15 |
| Ward class | Class II | 9.0 | 10.3 | 0.87 |
| Island | Jawa | 64.4 | 59.5 | 1.08 |
| Age group | Middle-old (70–79) | 29.6 | 27.7 | 1.07 |
| Membership segment | PBI APBN | 51.5 | 48.3 | 1.07 |
| Membership segment | Non-worker | 16.3 | 17.6 | 0.93 |
| Island | Kalimantan | 3.9 | 4.2 | 0.93 |
| Island | Sulawesi | 7.5 | 7.1 | 1.06 |
| Sex | Female | 52.1 | 50.6 | 1.03 |
| Source: BPJS Kesehatan Sample Data 2015–2024 | reguler (~1% household) sample | age ≥60 (45–59 comparator) | PSTV15-weighted (national JKN-member projection) | Index >1 = over-representation among those served, <1 = under-representation | ||||
Main limitations. 1. JKN claims capture only older people who
reach a facility and whose claim is recorded, not the entire elderly
population, so all figures are a served population, not true
prevalence.
2. Non-claim program-based services (Posyandu/Posbindu
Lansia, some drug programs) are not recorded, so community
chronic-disease management is under-captured.
3. There are no
laboratory/vital values, so blood-pressure and blood-glucose control
cannot be computed. Drug-dispensing data are incomplete, so polypharmacy
and inappropriate medications are only a chronic-condition-count
proxy.
4. Mortality (PSTV18, FKL14=3) is under-captured, only
snapshot years and in-hospital deaths, so end-of-life indicators are
indicative.
5. The FKTP→FKRTL referral link (FKP02) is populated
only ~2018–2023, so the referral-via-FKTP proportion is a lower
bound.
6. Geriatric syndromes and chronic diseases depend on
diagnostic coding (primary + secondary); uncoded conditions
(e.g. frailty, malnutrition, mild sensory impairment) will be
undercounted.
Next steps. 1. Cross-validate with community benchmarks (SKI
2023/Riskesdas 2018, BPS Elderly Population Statistics) for an elderly
treatment-gap framework.
2. Integrate dispensing data
(Non-Capitation/pharmacy) to compute polypharmacy and potentially
inappropriate medications (Beers/STOPP).
3. Deeper long-term-care
analysis (dependent older people, continuity of care) linked to
functional/disability data (see ARC12.2 Disability).
4.
District-level mapping of served rate and geriatric-service availability
(hospitals with geriatric clinics, MoH Regulation 79/2014).
Indicators in this report are mapped to internationally recognised ageing and long-term-care frameworks, with feasibility from claims data: WHO Decade of Healthy Ageing 2021–2030 and WHO ICOPE (intrinsic capacity: locomotor, vitality, cognition, psychological, sensory, approximated via downstream diagnoses); OECD Health Care Quality Outcomes (avoidable admissions/ACSC, 30-day readmission, timeliness of hip-fracture surgery); OECD Long-Term Care (structural LTC recipient/workforce/spending indicators are external, not in claims); the concepts of geriatric giants and multimorbidity (Barnett 2012; Quan 2005 comorbidity algorithms); and Indonesian policy (Law 13/1998, MoH Regulation 67/2015, MoH Regulation 79/2014, Prolanis). Coding note: BPJS uses WHO ICD-10 (unspecified diabetes E14; dementia F00–F03 with G30), and sarcopenia has no WHO ICD-10 code so it cannot be computed. Polypharmacy and potentially inappropriate medications (Beers/STOPP-START) require incomplete drug-dispensing data, so only a proxy is presented.
WHO. UN Decade of Healthy Ageing: Plan of Action 2021–2030 (2020). · WHO. Integrated Care for Older People (ICOPE) Guidelines (2017). · OECD. Health at a Glance 2023 & 2025; HCQO Indicator Definitions (2023). · Barnett K et al. Epidemiology of multimorbidity. Lancet 2012;380:37–43. · Quan H et al. Coding algorithms for comorbidities in ICD-10 administrative data. Med Care 2005;43:1130–9. · Law No. 13 of 1998 on the Welfare of Older Persons. · MoH Regulation No. 67 of 2015 (health services for older people at puskesmas); MoH Regulation No. 79 of 2014 (geriatric services in hospitals). · BPS. Older Population Statistics 2023. · Ministry of Health RI. Key Results of the Indonesia Health Survey (SKI) 2023.
## consolidated multi-sheet workbook
if (requireNamespace("writexl", quietly=TRUE)) {
sheets <- list(
INDEX=data.frame(Sheet=c("cohort_flow","table1","strata_usia","prev_year","entry","geriatric","acsc",
"multimorbidity","chronic_prev","cost","equity"),
Keterangan=c("STROBE cohort flow","Characteristics vs elderly population","Age distribution",
"Older people served per year","Point of first contact","Geriatric syndromes",
"Avoidable admissions","Multimorbidity distribution","Chronic disease prevalence",
"Per-capita cost & INA-CBG","Equity by segment & province")),
cohort_flow=A$cohort_flow, table1=A$table1, strata_usia=A$strata_patient,
prev_year=A$prev_year_total, entry=A$entry_overall, geriatric=A$geriatric_prev,
acsc=A$acsc_breakdown, multimorbidity=A$mm_dist, chronic_prev=A$chronic_prev,
cost=A$cost_percapita_band, equity=A$equity_segment)
sheets <- lapply(sheets, as.data.frame)
names(sheets) <- substr(names(sheets),1,31)
writexl::write_xlsx(sheets, file.path(OUT_DIR,"tables_aging_ltc_jkn.xlsx"))
}
cat("Artifacts exported to:", normalizePath(OUT_DIR), "\n")## Artifacts exported to: /opt/project_center_mirror/ARC12. Aging and Chronic Disease Institute/12.3 Aging & LTC JKN Claims/analysis/outputs
Health-system analysis based on aggregate administrative data (BPJS Kesehatan Sample Data). All population figures are weighted projections from the sample and represent the JKN-served population.