1. Adherence, Method (MPR, PDC)
2. Multiple Medication
3. Code (R, SQL)
1. Adherence, Method (MPR, PDC)
- Adherence, 복약순응도, 치료지속도, 관련 자강두천 Method
- MPR, Medication Possession Ratio
- 겹치는 기간 고려 상관없이 모든 약물 처방일 수를 분자로 사용
- PDC, Proportion of Days Covered
- 겹치는 기간을 shift 해서 일수를 보장해줌
- 분모는 설정하기 나름으로 보임
- 끝나는 날짜를 커스텀 study end date나 exposure 또는 era end date등으로 설정
- ex) drug_era로 하면 MPR = sum(days supply) / end_date - start_date
- ChatGPT
약물 보유 비율(Medication Possession Ratio, MPR):
약물 보유 비율(MPR)은 환자가 특정 기간 동안 처방받은 약물을 지키는 정도를 평가하는 데 사용되는 지표입니다. 이는 약국에서 공급된 약물의 날짜 공급량을 측정 기간 동안의 날 수로 나누어 계산됩니다. 결과는 특정 기간 동안 환자가 약물에 접근할 수 있는 시간의 비율을 나타내는 백분율입니다.
예를 들어, 고혈압에 대한 처방약물을 받은 환자가 30일분의 약물을 받았지만 27일 후에 처방전을 다시 받는 경우, 해당 기간 동안의 MPR은 90%가 됩니다(27일 공급량 / 30일).
더 높은 MPR은 처방된 약물 요법을 더 잘 준수한다는 것을 나타내며, 만성 질환 관리 및 최적의 치료 결과를 달성하는 데 중요합니다.
일수 별 보유 비율(Proportion of Days Covered, PDC):
일수 별 보유 비율(PDC)도 처방 약물 복용 순응도를 평가하는 데 사용되는 방법 중 하나입니다. MPR과 마찬가지로 특정 기간 동안 환자가 처방받은 약물에 접근한 시간의 비율을 계산합니다. 그러나 PDC는 약물 처방을 중첩하여 고려합니다. 다시 말해, 환자가 약물을 조기에 다시 받을 경우 중첩된 일 수를 순응도에 포함합니다.
예를 들어, 당뇨병 약물을 처방받은 환자가 30일분의 약물을 매 25일마다 다시 받으면 PDC는 125%가 됩니다(누적 일수 별 보유 일수 / 측정 기간의 총 일수).
MPR과 PDC 모두 의료 제공자와 연구자에게 처방받은 약물 요법을 따르는 환자의 순응도를 평가하는 중요한 도구입니다. 이를 통해 추가적인 지원이나 교육이 필요한 환자를 식별하여 최종적으로 더 나은 건강 결과를 이룰 수 있습니다.
2. Multiple Medication
- Single Medication에서는 간단히 구할 수 있지만 Multiple Medication에서는 여러 가지 방법이 있음
- mean MPR/PDC
- any MPR/PDC
- all MPR/PDC > best way, 2017 research
- DPPR, daily polypharmacy possession ratio
- 대략 4가지 방법이 있으며 이중에서 all MPR/PDC 방법이 제일 bias가 적다라는 reference가 있음.
Measuring Multiple Medication Adherence–Which Measure When? : https://www.ispor.org/docs/default-source/publications/newsletter/measuring_multiple_medication_adherence_which_measure_when.pdf?sfvrsn=f9946e55_0
3. Code (R, SQL)
- R tutorials
# single medication, make a sample dataset
library(dplyr)
library(data.table)
# GPT function
generate_date <- function(reference_date, start_date, end_date) {
random_date <- as.Date(sample(seq(start_date, end_date, by = "days"), 1))
while (random_date >= reference_date) {
random_date <- as.Date(sample(seq(start_date, end_date, by = "days"), 1))
}
return(random_date)
}
dt <- data.table(
p_id = sample(1:1000, 20000, replace = T),
e_id = sample(10000:30000, 20000, replace = F),
type = sample(c("a","b"), 20000, replace = T)
) %>%
group_by(p_id) %>%
mutate(
# 1환자 1 end_date = index_date or cohort_end_date
dot_end_date = sample(seq(as.Date("2018-01-01"),as.Date("2022-12-31"), by="days"), length(unique(p_id)),replace = T)
) %>%
ungroup() %>%
mutate (
# days supply 없으면 drug_exposure_end - drug_exposure_start
days_supply = sample(c(60,120,180), 20000, replace = T),
)
dt2 <- dt %>%
rowwise() %>%
mutate(
drug_exposure_start_date = generate_date(dot_end_date, as.Date("2008-01-01"), as.Date("2022-12-31")),
) %>%
ungroup() %>%
mutate(
drug_exposure_end_date = drug_exposure_start_date + days_supply
) %>%
filter(dot_end_date>drug_exposure_end_date)
dt3 <- dt2 %>%
# drug exposure start date 기준으로 환자별 줄세우기
arrange(p_id,drug_exposure_start_date) %>%
group_by(p_id) %>%
mutate(
rank=row_number()
) %>%
ungroup() %>%
arrange(p_id, rank) %>%
# end lag
group_by(p_id) %>%
mutate(
lag_end = lag(drug_exposure_end_date, 1)
) %>%
ungroup() %>%
mutate(
# start - lag end 후 grace period에 맞게 조정
g_day = difftime(drug_exposure_end_date, lag_end, units = "days")
) %>%
filter(g_day<182.5)
# sequence check
dt4 <- dt3 %>%
mutate(
pre = NA,
post = NA
)
## pre rank value
for (i in 1:(nrow(dt4))) {
if (i==1) {
a = dt4[i,]
dt4[i,]$pre <- 0
}
else {
a = dt4[i,]
b = dt4[i-1,]
if ((a$p_id == b$p_id) & (a$rank-1 == b$rank)){
dt4[i,]$pre <- 1
}
else {
dt4[i,]$pre <- 0
}
}
}
## post rank value
for (i in 1:(nrow(dt4))) {
if (i==nrow(dt4)) {
a = dt4[i,]
dt4[i,]$post <- 0
}
else {
a = dt4[i,]
b = dt4[i+1,]
if ((a$p_id == b$p_id) & (a$rank+1 == b$rank)){
dt4[i,]$post <- 1
}
else {
dt4[i,]$post <- 0
}
}
}
# 둘중 하나라도 체크되면 살림
dt5 <- dt4 %>% filter(pre==1|post==1)
# 같은 id에서 pre + post ==1 부터 다음 1까지 동일한 episode
dt6 <- dt5 %>% mutate(epi:=NA)
for (i in 1:nrow(dt6)) {
if (i==1) {
j = 1
dt6[i,]$epi <- j
} else if (dt6[i,]$p_id==dt6[i-1,]$p_id&dt6[i,]$pre==1) {
dt6[i,]$epi <- j
} else if (dt6[i,]$p_id==dt6[i-1,]$p_id&dt6[i,]$pre==0) {
j = j+1
dt6[i,]$epi <- j
} else {
j = 1
dt6[i,]$epi <- j
}
}
# max episode만 추출
dt_res <- dt6 %>%
group_by(p_id) %>%
filter(epi == max(epi)) %>%
ungroup()
# 뽑힌 episode의 exposure end date로 부터 index date까지 grace period가 넘어가는 것들 제외
dt_res2 <- dt_res %>%
mutate(
g_index_end = difftime(dot_end_date, drug_exposure_end_date, units = "days")
) %>%
filter(g_index_end<182.5)
# DoT, MPR 계산
dt_res_fin <- dt_res2 %>%
group_by(p_id) %>%
summarise(
p_id,
# min = min(drug_exposure_start_date),
dot = as.integer(difftime(dot_end_date, min(drug_exposure_start_date),units = "days")),
sum(days_supply),
mpr = sum(days_supply) / dot *100,
mpr = ifelse(mpr>=100, 100, round(mpr))
) %>% ungroup() %>% unique() %>% data.table()
dt_res_fin
- PostgreSQL
with /* Step1. 코호트 대상자의 전체 기간 약물 추출, 약물포함하는 코호트 번호만 where절에 넣음 */ /* where절의 코호트 번호 확인! */
cohort_g2a1 as (
select *
from result_schema.cohort
where cohort_num = id
)
,HTN1_PLAIN_amlodipine as (
select distinct a.*, b.cohort_start_date, b.cohort_end_date, 'HTN1_PLAIN_amlodipine' as drug_type
from (select distinct t1.person_id
,t1.drug_concept_id
,t1.drug_exposure_start_date, t1.drug_exposure_end_date
from schema.drug_exposure t1
inner join (/* 포함 컨셉 - 단일제 */
select distinct descendant_concept_id as dx_id
from schema.concept_ancestor
where ancestor_concept_id in (1332418)
) t2 on t1.drug_concept_id = t2.dx_id
left join (/*제외 컨셉 - 복합제 */
select distinct descendant_concept_id as dx_id
from schema.concept_ancestor
where ancestor_concept_id in
(1332529, 1332531, 2064617, 2064621, 2064625, 19128163, 21062424, 21101680, 21131351)) t3
on t1.drug_concept_id = t3.dx_id
where t3.dx_id is null and t1.drug_exposure_start_date<'2023-01-01' and t1.drug_exposure_end_date<'2023-01-01') a
inner join cohort_g2a1 b on a.person_id=b.subject_id
order by a.person_id, b.cohort_start_date /* ID, 날짜순 정렬 */
)
,DL5__PLAIN_atorvastatin as (
select distinct a.*, b.cohort_start_date, b.cohort_end_date, 'DL5__PLAIN_atorvastatin' as drug_type
from (select distinct t1.person_id
,t1.drug_concept_id
,t1.drug_exposure_start_date, t1.drug_exposure_end_date
from schema.drug_exposure t1
inner join (/* DRC O - 단일제 */
select distinct descendant_concept_id as dx_id
from schema.concept_ancestor
where ancestor_concept_id in (1545958)) t2 on t1.drug_concept_id = t2.dx_id
left join (/*제외 컨셉 - 복합제 */
select distinct descendant_concept_id as dx_id
from schema.concept_ancestor
where ancestor_concept_id in
(1332497, 19128162, 19128163, 40008941, 40144458,
43260315, 43260317, 43260318, 43271238)) t3
on t1.drug_concept_id = t3.dx_id
where t3.dx_id is null and t1.drug_exposure_start_date<'2023-01-01' and t1.drug_exposure_end_date<'2023-01-01') a
inner join (select * from result_schema.cohort t where t.cohort_num = id) b on a.person_id = b.subject_id
)
/* Step2. 각 그룹의 마지막 episode 생성 (Grace period = 60일 기준) */
, epi_htn1 as (
select a.*
, dense_rank() over (PARTITION BY a.person_id order by a.epi_cum desc) as epi_last /* 내림차순-마지막 episode */
from
(select aa.*
, sum(aa.epi1) over (partition by aa.person_id order by aa.person_id ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as epi_cum /* 누적합계 */
from (select a.person_id, a.drug_exposure_start_date, a.drug_exposure_end_date
,lag(a.drug_exposure_end_date) over (PARTITION BY a.person_id ORDER BY a.drug_exposure_end_date) as lag_end_date
, a.drug_exposure_start_date - lag(a.drug_exposure_end_date) over (PARTITION BY a.person_id ORDER BY a.drug_exposure_end_date) as strt_end_gap
, case when (a.drug_exposure_start_date - lag(a.drug_exposure_end_date) over (PARTITION BY a.person_id ORDER BY a.drug_exposure_end_date)) is null then 0
when (a.drug_exposure_start_date - lag(a.drug_exposure_end_date) over (PARTITION BY a.person_id ORDER BY a.drug_exposure_end_date)) <=60 then 0
when (a.drug_exposure_start_date - lag(a.drug_exposure_end_date) over (PARTITION BY a.person_id ORDER BY a.drug_exposure_end_date)) >60 then 1
end as epi1 /* 60일 간격 check */
from HTN1_PLAIN_amlodipine a
order by a.person_id, a.drug_exposure_start_date) aa
order by aa.person_id, aa.drug_exposure_start_date) a
order by a.person_id, a.drug_exposure_start_date
)
, epi_dl5 as (
select a.*
, dense_rank() over (PARTITION BY a.person_id order by a.epi_cum desc) as epi_last /* 내림차순-마지막 episode */
from
(select aa.*
, sum(aa.epi1) over (partition by aa.person_id order by aa.person_id ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as epi_cum /* 누적합계 */
from (select a.person_id, a.drug_exposure_start_date, a.drug_exposure_end_date
,lag(a.drug_exposure_end_date) over (PARTITION BY a.person_id ORDER BY a.drug_exposure_end_date) as lag_end_date
, a.drug_exposure_start_date - lag(a.drug_exposure_end_date) over (PARTITION BY a.person_id ORDER BY a.drug_exposure_end_date) as strt_end_gap
, case when (a.drug_exposure_start_date - lag(a.drug_exposure_end_date) over (PARTITION BY a.person_id ORDER BY a.drug_exposure_end_date)) is null then 0
when (a.drug_exposure_start_date - lag(a.drug_exposure_end_date) over (PARTITION BY a.person_id ORDER BY a.drug_exposure_end_date)) <=60 then 0
when (a.drug_exposure_start_date - lag(a.drug_exposure_end_date) over (PARTITION BY a.person_id ORDER BY a.drug_exposure_end_date)) >60 then 1
end as epi1 /* 60일 간격 check */
from DL5__PLAIN_atorvastatin a
order by a.person_id, a.drug_exposure_start_date) aa
order by aa.person_id, aa.drug_exposure_start_date) a
order by a.person_id, a.drug_exposure_start_date
)
/* Step3. 2개 약물의 겹치는 날짜 episode 가져옴 */
, overlap_ab as (
select a.person_id
, a.a_strt_date
, a.b_strt_date
, a.a_end_date
, a.b_end_date
, case /* 겹치는 날짜 정리 1 - 시작일 */
when a.a_strt_date <= a.b_strt_date then a.b_strt_date /* start date는 마지막 날짜 */
when a.a_strt_date > a.b_strt_date then a.a_strt_date end as combi_strt
, case /* 겹치는 날짜 정리 2 - 종료일 */
when a.a_end_date <= a.b_end_date then a.a_end_date /* end date는 빠른 날짜 */
when a.a_end_date > a.b_end_date then a.b_end_date end as combi_end
from (
select a.person_id
, a.drug_exposure_start_date as a_strt_date
, a.drug_exposure_end_date as a_end_date
, b.drug_exposure_start_date as b_strt_date
, b.drug_exposure_end_date as b_end_date
from (select * from epi_htn1 where epi_last = 1) as a
inner join (select * from epi_dl5 where epi_last=1) as b
on a.person_id = b.person_id /* ID 기준 join */
order by a.person_id, a.drug_exposure_start_date) a
WHERE (a.a_strt_date, a.a_end_date) OVERLAPS (a.b_strt_date, a.b_end_date) /* episode간 overlap되는 case만 추출 */
)
/* Step4. 실제 episode 날짜, combi_날짜까지 */
select a.*,
b.cohort_start_date, b.cohort_end_date /*확인용 */ ,
a.combi_end - combi_strt +1 as days_supply
from overlap_ab a
left join cohort_g2a1 b on a.person_id=b.subject_id
;
- SQL 문을 wraping 하여 Rscript로 MPR 계산
# query
g2a1 <- execute_query(sql_test)
# deidentification
g2a1[,dp_id := lapply(.SD, anonymize), .SDcols = "person_id"] %>% select(!person_id)
head(g2a1)
# DoT, MPR 계산
res_g2a1 <- g2a1 %>%
group_by(dp_id) %>%
summarise(
dp_id,
dot = as.integer(difftime(max(combi_end), min(combi_strt),units = "days"))+1,
sum_days_supply = sum(days_supply),
mpr = sum_days_supply / dot *100,
mpr = ifelse(mpr>=100, 100,
ifelse(mpr<0,0, round(mpr)))
)%>%
ungroup() %>%
unique() %>%
data.table()
cat("Group mean MPR:",mean(res_g2a1$mpr, na.rm=T))