Kvalitetsindikatorfunksjonar
Karl Ove Hufthammer
Source:vignettes/ki-funksjonar.Rmd
ki-funksjonar.RmdInnleiing
Ved utrekning av kvalitetsindikatorar er det mange fallgruver å gå i. Me har derfor utvikla eit rammeverk for korleis dette skal gjerast. Ved hjelp av ein fastsett metodikk og eit tilhøyrande sett R-funksjonar reduserer me risikoen for at indikatorane vert rekna ut feil, og me gjer det raskare og enklare å bruka indikatorane i ulike situasjonar.
Det er greiast å visa rammeverket via nokre eksempel:
Eksempel – eittårsoppfølging
Me har eit enkelt register der pasientar vert opererte og seinare følgde opp. Nokre av variablane i registeret kan sjå slik ut:
#> # A tibble: 6 × 6
#> pas_id kjonn alder sjukehus dato_operasjon dato_oppfolging
#> <int> <fct> <dbl> <fct> <date> <date>
#> 1 11 mann 18 A 2024-11-18 2025-11-01
#> 2 13 kvinne 36 C 2026-01-22 NA
#> 3 14 mann 25 B 2025-01-28 2026-07-04
#> 4 27 kvinne 29 A 2025-05-14 2026-04-18
#> 5 28 mann 78 B 2024-04-12 2026-03-28
#> 6 29 mann 45 B 2025-01-03 NA
Pasientane skal helst følgjast opp innan eitt år etter operasjon, og me har ein eigen kvalitetsindikator for dette. Der er det blant anna definert at «eitt år» skal reknast som 365 dagar.
Første forsøk på utrekning
Viss me vart bedt om å rekna ut kvalitetsindikatoren, ville me kanskje gjort det slik:
sum(d_reg$dato_oppfolging - d_reg$dato_operasjon <= 365, na.rm = TRUE) / nrow(d_reg)
#> [1] 0.3333333Så det ser ut til at berre ein tredel av pasientane får oppfølging innan fristen.
Det er dessverre fleire problem med utrekninga. For det første er
svaret feil! Merk at pasient 13 har NA på
oppfølgingsdatoen, som tyder at ho enno ikkje er følgd opp. Sidan det
var mindre enn eitt år sidan ho vart operert (la oss anta at dagens dato
er 25. September 2026), skal ho ekskluderast frå utrekninga. Det vert ho
i utrekninga av teljaren (na.rm = TRUE), men ikkje i
utrekninga av nemnaren (nrow(d_reg)), så brøken vert
feil.
Dette er eksempel på ein feil som lett kan oppstå når utrekninga av teljaren og nemnaren vert gjort på ulike datakjelder. Spesielt er det stor risiko viss det er snakk om to ulike datarammer, så det bør ein aldri bruka.
Andre forsøk på utrekning
Problemet med to ulike datakjelder er lett å retta opp. Me kan for eksempel skriva det slik:
mean(d_reg$dato_oppfolging - d_reg$dato_operasjon <= 365, na.rm = TRUE)
#> [1] 0.5Så halvparten av pasientane får oppfølging innan fristen. Mykje betre! Problemet er at svaret framleis er feil!
Pasient 29 vert ekskludert frå utrekninga, men me ser at han vart operert for godt over eit år sidan og har ikkje fått oppfølging enno (altså per 25. September 2026). Han bør altså reknast som ein som ikkje fekk oppfølging innan fristen. Og i motsetning til pasient 13, bør han ikkje ekskluderast frå utrekninga.
Eksempel på rammeverket
Rammeverket vårt løyser (delvis) problema ovanfor, blant anna ved at ein for brøkbaserte kvalitetsindikatorar (som for eksempel målar prosentdelen av pasientar som oppfyller eit kriterium) må vera meir eksplisitt på kven som skal inngå i teljaren og nemnaren.
Ein definerer på individnivå (radnivå) om ein observasjon (pasient, operasjon eller liknande) skal inngå i utrekninga av kvalitetsindikatoren, dvs. om han skal inngå i nemnaren i brøken. Tilsvarande definerer ein på individnivå om han skal inngå i teljaren.
Reint konkret lagar ein for kvar kvalitetsindikator ein R-funksjon (kvalitetsindikatorfunksjon, KI-funksjon) som tek inn ei dataramme på eit fastsett – og dokumentert – format, samt eventuelt andre argument. KI-funksjonen gjev ut ei dataramma med (iallfall) desse to variablane (lagde til):
-
ki_krit_nevner: Logisk variabel som seier om observasjonen oppfølgde kriteria for å vera med i utrekning av nemnaren i brøken. -
ki_krit_teller: Logisk variabel som seier om observasjonen oppfølgde kriteria for å telja som eit «ja» i kvalitetsindikatoren, dvs. om han skal vera med i utrekning av teljaren i brøken.
Variabelen ki_krit_nevner må vera sann eller usann
(dvs. ikkje NA) for alle radene. Variabelen
ki_krit_teller må vera sann eller usann dersom
ki_krit_nevner er sann, og usann eller NA
dersom ki_krit_nevner er usann.
(Grunnen til at me ikkje tillèt NA-verdiar for
ki_krit_teller dersom ki_krit_nevner er sann,
er at for ein skikkeleg definert kvaltitetsindikator alltid
skal kunna avgjera om ein observasjon som inngår i
utrekninga av indikatoren oppfyller kriteria for å telja
positivt i teljaren. Hadde me godteke NA, og for eksempel
tolka dette som FALSE, hadde det vore stor risiko å gje ut
feil svar, då ein NA-verdi lett kunne vera resultat av at
KI-funksjonen ikkje var perfekt gjennomtenkt, og for eksempel feilaktig
antok at alle variablane som inngjekk i utrekningane var
ikkje-manglande. Ved å forby NA-verdiar, eliminerer me
denne risikoen.)
fixme: Informasjon om attributt for namn og forklaring på KI-ar.
Eksempel på KI-funksjon
For eksempelet ovanfor kan KI-funksjonen sjå slik ut:
ki_oppf_innan_1_aar = function(d_reg, dato_datasett) {
d_reg |>
mutate(
dagar_sidan_op = difftime(!!dato_datasett, dato_operasjon, units = "days"),
dagar_til_oppf = difftime(dato_oppfolging, dato_operasjon, units = "days"),
ki_krit_nevner = dagar_sidan_op >= 365,
ki_krit_teller = ki_krit_nevner & !is.na(dato_oppfolging) & dagar_til_oppf <= 365
)
}Køyrer me funksjonen på datasettet, får me:
ki_oppf_innan_1_aar(d_reg, dato_datasett = Sys.Date()) |>
print(width = Inf)
#> # A tibble: 6 × 10
#> pas_id kjonn alder sjukehus dato_operasjon dato_oppfolging dagar_sidan_op
#> <int> <fct> <dbl> <fct> <date> <date> <drtn>
#> 1 11 mann 18 A 2024-11-18 2025-11-01 676 days
#> 2 13 kvinne 36 C 2026-01-22 NA 246 days
#> 3 14 mann 25 B 2025-01-28 2026-07-04 605 days
#> 4 27 kvinne 29 A 2025-05-14 2026-04-18 499 days
#> 5 28 mann 78 B 2024-04-12 2026-03-28 896 days
#> 6 29 mann 45 B 2025-01-03 NA 630 days
#> dagar_til_oppf ki_krit_nevner ki_krit_teller
#> <drtn> <lgl> <lgl>
#> 1 348 days TRUE TRUE
#> 2 NA days FALSE FALSE
#> 3 522 days TRUE FALSE
#> 4 339 days TRUE TRUE
#> 5 715 days TRUE FALSE
#> 6 NA days TRUE FALSETing å merka seg:
- Om ein pasient inngår i kvalitetsindikatoren er avhengig av kva dato
datasettet er frå. Her har me for enkelheits skuld brukt dagens dato
(
Sys.Date()), men i praksis må ein sjølvsagt bruka den reelle nedlastingsdatoen for datasettet. - Det er bevisst at me ikkje har sett
Sys.Date()som standardverdi i funksjonen. Då kunne ein venja seg til å ikkje eksplisitt oppgje dato, fordi ein oftast har ferske data, som ville ført til feil resultat dei få gongane ein såg på historiske data. - I definisjonen av
ki_krit_tellerkrev me først atki_krit_nevnerer sann. Dette er lurt, då det reduserer risikoen for at teljaren vert feil definert. I ein del tilfelle kan det òg gjera definisjonen avki_krit_tellerein del enklare/kortare. (Det gjeld dei tilfella kriteria for inngå i nemnaren er veldig omfattande, med for eksempel bruk av mange variablar, mens kriteria for å inngå i teljaren er dei same pluss berre eitt eller nokre få tillegskriterium.) - Me brukar
difftime()med eksplisitt definisjon av tidseining (units = "days"). Her ville R-kodendato_oppfolging - dato_operasjonhatt nøyaktig same effekt, men det er generelt ein mykje meir risikabel kode. Han ville ikkje fungera likt dersom datovariablane ein gong i framtida vart endra til tidsvariablar (POSIXct-objekt), dvs. om ein også registrerte klokkeslett for operasjon eller oppfølging. Men han ville oftast fungerte likt, så det hadde vore ein feil som hadde vore lett å oversjå. Det er nemleg slik at kodenx - yikkje alltid gjev ut talet på dagar mellomxogynår variablane er tidsvariablar; han gjev av og til ut dagar, av og til timar, av og til minutt og av og til sekund, avhengig av tidsavstanden. Derfor bør ein alltid brukadifftime()med eksplisitt val avunitsnår ein reknar med tids- eller datovariablar.
Bruk av KI-funksjonen
Når ein har definert ein KI-funksjon, gjev rammeverket oss fleire
verktøy for bruk av funksjonen. Oftast vil ein rett og slett
rekna ut kvalitetsindikatoren. Det gjer ein enkelt med ein
aggregeringsfunksjon. For binomiske/proporsjonsbaserte KI-funksjonar er
dette aggreger_ki_prop():
d_reg |>
ki_oppf_innan_1_aar(dato_datasett = Sys.Date()) |>
aggreger_ki_prop()
#> # A tibble: 1 × 5
#> est ki_teller ki_nevner konfint_nedre konfint_ovre
#> <dbl> <int> <int> <dbl> <dbl>
#> 1 0.4 2 5 0.118 0.769Her får me ut både estimatet av kvalitetsindikatoren (rett svar var altså 40 %), tala som inngår i teljaren og nemnaren og eit 95 %-konfidensintervall for indikatoren. Nokre ting å merka seg:
- I praksis ville ein nok gjerne allerie ha lagra resultatet
av
ki_oppf_innan_1_aar()køyrd på datasettet som eit eige objekt, og så køyrdaggreger_ki_prop()på dette igjen. Men me viser her ei «røyr-køyring» for å illustrera at R-rammeverket er utforma slik at det skal kunna brukast i ein «tidyverse-dataflyt». - Sjølv om det var 6 rader, vart nemnaren berre 5, sidan det var éin pasient som var så nyleg opererert at han ikkje kunne inngå i utrekning av kvalitetsindikatoren.
- Konfidensintervallet utrekna er Wilson-/skår-intervallet, som
fungerer veldig godt for både verdiar av indikatoren nær 0 %,
100 % og andre verdiar, og for både små og store nemnarar, og
er derfor det me anbefaler for generell bruk i
kvalitetsindikatorsamanheng (sjå òg https://doi.org/10.1214/ss/1009213286).
Konfidensintervalla ein får ut frå
binom.test()-funksjonen (Clopper–Pearson-intervalla) vil vera breiare, og kan vera svært konservative i nokre tilfelle. - Det er sjølvsagt mogleg å velja konfidensnivå, dersom ein for eksempel vil ha 90 %- eller 99 %-konfidensintervall.
- Dersom det hadde vore noko logisk feil med definisjonane av
ki_krit_teller- ogki_krit_nevner-variablane, for eksempel omki_krit_tellerpå ei rad var sann menski_krit_nevnervar usann – noko som tyder på ein feil i programmeringa av KI-funksjonen – ville funksjonen ha gjeve ei feilmelding og nekta å rekna ut kvalitetsindikatoren.
Aggregering på gruppenivå
Ofte vil ein visa kvalitetsindikatorar stratifisert på sjukehus, diagnose eller tidsperiode, eller ein ein kombinasjon av dette. Det fiksar rammeverket for oss. La oss første laga eit objekt med inndata på individnivå.
d_oppf1 = d_reg |>
ki_oppf_innan_1_aar(dato_datasett = Sys.Date())Vil me visa resultata på sjukehusnivå, treng me berre gruppera
d_oppf1 på vanleg vis før me køyrer
aggregeringsfunksjonen:
d_oppf1 |>
group_by(sjukehus) |>
aggreger_ki_prop()
#> # A tibble: 3 × 6
#> sjukehus est ki_teller ki_nevner konfint_nedre konfint_ovre
#> <fct> <dbl> <int> <int> <dbl> <dbl>
#> 1 A 1 2 2 0.342 1
#> 2 B 0 0 3 0 0.561
#> 3 C NA 0 0 NA NATing å merka seg:
- Funksjonen handterer fint tilfelle der nemnaren vert 0. Då vert både
indikatoren og tilhøyrande konfidensgrenser
NA(og ikkje for eksempelNaN, som0/0vert i R, eller ei feilmelding, sombinom.test(0,0)gjev). - Rekkjefølgja sjukehusa vert vist på (her: ABC), svarar til nivåa til grupperingsvariabelen/-ane. Dette er ikkje nødvendigvis lik rekkjefølgja som radene opphavleg står i (her: ACB).
- Om det manglar observasjonar for nokre av nivåa til
grupperingsvariablane, følgjer utrekninga dei vanlege reglane for
.drop-argumentet igroup_by()-funksjonen. Men ein får ei åtvaring dersom.drop = FALSEog det manglar slike observasjonar. (fixme: Bør me ha dette? Viss ja, bør me ha det for alle grupper der nevner er 0?)
Her er eksempel på det siste punktet. Me fjernar pasientane for
B-sjukehuset og reknar ut indikatoren på nytt, med to ulike verdiar for
.drop:
d_oppf1_utan_b = filter(d_oppf1, sjukehus != "B")
d_oppf1_utan_b |>
group_by(sjukehus, .drop = TRUE) |> # Standardverdi
aggreger_ki_prop()
#> # A tibble: 2 × 6
#> sjukehus est ki_teller ki_nevner konfint_nedre konfint_ovre
#> <fct> <dbl> <int> <int> <dbl> <dbl>
#> 1 A 1 2 2 0.342 1
#> 2 C NA 0 0 NA NA
d_oppf1_utan_b |>
group_by(sjukehus, .drop = FALSE) |>
aggreger_ki_prop()
#> Warning in aggreger_ki_prop(group_by(d_oppf1_utan_b, sjukehus, .drop = FALSE)):
#> Det finnes grupper uten observasjoner i grupperingsvariabel
#> # A tibble: 3 × 6
#> sjukehus est ki_teller ki_nevner konfint_nedre konfint_ovre
#> <fct> <dbl> <int> <int> <dbl> <dbl>
#> 1 A 1 2 2 0.342 1
#> 2 B NA 0 0 NA NA
#> 3 C NA 0 0 NA NAFleksible KI-funksjonar
Det er òg mogleg å laga KI-funksjonar for ein familie av kvalitetsindikatorar. Her er eit eksempel.
Tidlegare såg me på oppfølging innan eitt år. I praksis bør eittårsoppfølginga skje etter cirka eitt år. Ein pasient som vart følgd opp seks veker etter operasjonen, bør derfor ikkje reknast til å ha fått eittårsoppfølging. Me innfører derfor omgrepet normtid. Ein person som har blitt følgd opp ved «1 år ± x dagar» (der x for eksempel er 30), vert rekna til å vera følgd opp innan normtid.
Me kan òg generalisera utrekninga til å gjelda oppfølging på vilkårlege andre tidspunkt (seksmånadsoppfølging, toårsoppfølging etc.):
ki_oppf_innan_normtid = function(d_reg, dato_datasett, oppf_aar, slingringsmonn_dagar = 30) {
# Talet på dagar etter operasjon som skal reknast som
# mål-tidspunkt (ideelt tidspunkt) for oppfølging,
# pluss nedre og øvre grense for kva som er akseptabelt
# (dvs. innanfor normtid)
oppf_maal = oppf_aar * 365
nedre_grense = oppf_maal - slingringsmonn_dagar
ovre_grense = oppf_maal + slingringsmonn_dagar
d_reg |>
mutate(
dagar_sidan_op = difftime(!!dato_datasett, dato_operasjon, units = "days"),
dagar_til_oppf = difftime(dato_oppfolging, dato_operasjon, units = "days"),
ki_krit_nevner = dagar_sidan_op >= ovre_grense,
ki_krit_teller = ki_krit_nevner & !is.na(dato_oppfolging) &
(dagar_til_oppf >= nedre_grense) & (dagar_til_oppf <= ovre_grense)
)
}I dette registeret brukar me vanlegvis eit slingringsmonn på 30 dagar
for alle normtider, så det er sett som standardverdi
(slingringsmonn_dagar = 30). Her er eksempel på bruk:
# Oppfølging innan 1 år ± 30 dagar
d_reg |>
ki_oppf_innan_normtid(dato_datasett = Sys.Date(), oppf_aar = 1) |>
aggreger_ki_prop()
#> # A tibble: 1 × 5
#> est ki_teller ki_nevner konfint_nedre konfint_ovre
#> <dbl> <int> <int> <dbl> <dbl>
#> 1 0.4 2 5 0.118 0.769
# Oppfølging innan 2 år ± 60 dagar, gruppert på sjukehus
d_reg |>
ki_oppf_innan_normtid(
dato_datasett = Sys.Date(),
oppf_aar = 2, slingringsmonn_dagar = 60
) |>
group_by(sjukehus) |>
aggreger_ki_prop()
#> # A tibble: 3 × 6
#> sjukehus est ki_teller ki_nevner konfint_nedre konfint_ovre
#> <fct> <dbl> <int> <int> <dbl> <dbl>
#> 1 A NA 0 0 NA NA
#> 2 B 1 1 1 0.207 1
#> 3 C NA 0 0 NA NAViss me skal bruka utrekningane av eitt- og toårsoppfølging i fleire samanhengar, kan det vera greitt å definera eigne funksjonar for dette, med dei ulike argumenta sett til standardverdiar. Viss me alltid arbeider med ferske data (for eksempel direkte frå ein database), kan me for eksempel definera funksjonane slik:
ki_oppf_innan_1_aar = function(d_reg) {
ki_oppf_innan_normtid(d_reg, dato_datasett = Sys.Date(), oppf_aar = 1)
}
ki_oppf_innan_2_aar = function(d_reg) {
ki_oppf_innan_normtid(d_reg, dato_datasett = Sys.Date(), oppf_aar = 2)
}Me brukar dei nydefinerte funksjonane som vanlege KI-funksjonar:
d_reg |>
ki_oppf_innan_1_aar() |>
aggreger_ki_prop()
#> # A tibble: 1 × 5
#> est ki_teller ki_nevner konfint_nedre konfint_ovre
#> <dbl> <int> <int> <dbl> <dbl>
#> 1 0.4 2 5 0.118 0.769Viss det berre er eitt eller nokre få argument me vil låsa fast, kan
me heller bruka argumentellipse (...) for dei resterande
argumenta:
ki_oppf_innan_2_aar = function(...) {
ki_oppf_innan_normtid(..., oppf_aar = 2)
}Fleire inndatasett
I eksempla me har sett til no, har KI-funksjonane tatt eitt inndatasett (pluss nokre ekstraargument), og dette inndatasettet vart tilføyd nokre kolonnar. Men det er ingenting i vegen for at ein KI-funksjon kan ta fleire inndatasett. La oss sjå på eit eksempel.
For oppfølgingsindikatorane var det eigentleg urealistisk å ha berre éin variabel med oppfølgingsdato. Pasientane kan jo ha fleire oppfølgingar (ved seks månadar, eitt år, to år, …). Eit meir realistisk eksempel vil vera at me har to datasett, eitt for operasjonar og eitt for oppfølgingar, og ein koplingsnøkkel (pasient- og/eller operasjons-ID) mellom desse.
Viss me føreset at kvar pasient har berre éin operasjon men kan ha vilkårleg mange oppfølgingar, kan datasetta sjå slik ut:
d_oper # Operasjonar
#> # A tibble: 6 × 5
#> pas_id kjonn alder sjukehus dato_operasjon
#> <int> <fct> <dbl> <fct> <date>
#> 1 11 mann 18 A 2024-11-18
#> 2 13 kvinne 36 C 2026-01-22
#> 3 14 mann 25 B 2025-01-28
#> 4 27 kvinne 29 A 2025-05-14
#> 5 28 mann 78 B 2024-04-12
#> 6 29 mann 45 B 2025-01-03
d_oppf # Oppfølgingar
#> # A tibble: 7 × 2
#> pas_id dato_oppfolging
#> <int> <date>
#> 1 11 2025-11-01
#> 2 11 2026-09-17
#> 3 14 2026-07-04
#> 4 27 2026-04-18
#> 5 28 2025-04-02
#> 6 28 2026-03-28
#> 7 28 2026-08-05Ein fleksibel funksjon for oppfølging innan normtid kan sjå slik ut:
ki_oppf_innan_normtid = function(d_oper, d_oppf, dato_datasett, oppf_aar, slingringsmonn_dagar = 30) {
# Talet på dagar etter operasjon som skal reknast som
# mål-tidspunkt (ideelt tidspunkt) for oppfølging,
# pluss nedre og øvre grense for kva som er akseptabelt
# (dvs. innanfor normtid)
oppf_maal = oppf_aar * 365
nedre_grense = oppf_maal - slingringsmonn_dagar
ovre_grense = oppf_maal + slingringsmonn_dagar
# For kvar operasjon, legg til info om eventuelle oppfølgingar
d_reg = d_oper |>
left_join(d_oppf, by = "pas_id", relationship = "one-to-many")
# For kvar oppfølging, rekn ut kriterievariablar
d_ki_per_oppf = d_reg |>
mutate(
dagar_sidan_op = difftime(!!dato_datasett, dato_operasjon, units = "days"),
dagar_til_oppf = difftime(dato_oppfolging, dato_operasjon, units = "days"),
ki_krit_nevner = dagar_sidan_op >= ovre_grense,
ki_krit_teller = ki_krit_nevner & !is.na(dato_oppfolging) &
(dagar_til_oppf >= nedre_grense) & (dagar_til_oppf <= ovre_grense)
)
# For kvar operasjon som *burde* vore følgt opp no, rekn ut kriterievariablar
d_ki = d_ki_per_oppf |>
filter(ki_krit_nevner) |>
group_by(!!!syms(names(d_oper)), ki_krit_nevner) |>
summarise(ki_krit_teller = any(ki_krit_teller)) |>
ungroup()
# Returner datasett med indikatorvariablar
d_ki
}- For enkeltheits skuld gjev funksjonen her berre ut rader for
pasientar der indikatoren kan reknast ut, altså der
ki_krit_nevnerer sann. Det gjer ting hakket enklare, men han kan òg lett utvidast til å gje ut rader for alle operasjonane. - Funksjonen kan sjølvsagt òg utvidast til å gje ut tilleggsvariablar
som
dagar_til_oppf. Men her må ein tenka godt gjennom implementasjon for å sikra at alt vert handtert riktig, for eksempel dersom det finst fleire oppfølgingar innan normtid på same operasjon. - Utrekningane vert i prinsippet gjort på same måte som før. Einaste
forskjellen er at me no reknar først ut kriterievariablane for kvar
oppfølging pasienten har hatt. Ein operasjon har oppfølging innan
normtid dersom det finst minst éi slik oppfølging innnan
normtid (
any(ki_krit_teller)). - Me har brukt
!!!syms(names(d_oper))som grupperingsvariablar for å sikra at me får med alle kolonnane i operasjonsskjemaet, i tilfelle me seinare vil stratifisera på desse. - Det er òg viktig å avgruppera før me returnerer objektet, slik at seinare aggregering (som standard) vert gjort ugruppert.
Funksjonen brukar me på tilsvarande måte som før:
# Rekn ut kriteriedatasett
d_oppf1 = ki_oppf_innan_normtid(d_oper, d_oppf, dato_datasett = Sys.Date(), oppf_aar = 1)
#> `summarise()` has regrouped the output.
#> ℹ Summaries were computed grouped by pas_id, kjonn, alder, sjukehus,
#> dato_operasjon, and ki_krit_nevner.
#> ℹ Output is grouped by pas_id, kjonn, alder, sjukehus, and dato_operasjon.
#> ℹ Use `summarise(.groups = "drop_last")` to silence this message.
#> ℹ Use `summarise(.by = c(pas_id, kjonn, alder, sjukehus, dato_operasjon,
#> ki_krit_nevner))` for per-operation grouping (`?dplyr::dplyr_by`) instead.
d_oppf1
#> # A tibble: 5 × 7
#> pas_id kjonn alder sjukehus dato_operasjon ki_krit_nevner ki_krit_teller
#> <int> <fct> <dbl> <fct> <date> <lgl> <lgl>
#> 1 11 mann 18 A 2024-11-18 TRUE TRUE
#> 2 14 mann 25 B 2025-01-28 TRUE FALSE
#> 3 27 kvinne 29 A 2025-05-14 TRUE TRUE
#> 4 28 mann 78 B 2024-04-12 TRUE TRUE
#> 5 29 mann 45 B 2025-01-03 TRUE FALSE
# Rekn ut sjølve kvalitetsindikatoren,
# både totalt og stratifisert på kjønn
d_oppf1 |>
aggreger_ki_prop()
#> # A tibble: 1 × 5
#> est ki_teller ki_nevner konfint_nedre konfint_ovre
#> <dbl> <int> <int> <dbl> <dbl>
#> 1 0.6 3 5 0.231 0.882
d_oppf1 |>
group_by(kjonn) |>
aggreger_ki_prop()
#> # A tibble: 2 × 6
#> kjonn est ki_teller ki_nevner konfint_nedre konfint_ovre
#> <fct> <dbl> <int> <int> <dbl> <dbl>
#> 1 kvinne 1 1 1 0.207 1
#> 2 mann 0.5 2 4 0.150 0.850Ting å merka seg:
- Funksjonen treng ikkje gje ut eit datasett med like mange rader som i nokon av inndatasetta. Her er det 6 operasjonar og 7 oppfølgingar, men KI-funksjonen gjev ut 5 rader. Det viktige er at han gjev ut data på individnivå (her: person-/operasjonsnivå).
Kontinuerlege variablar
Til no har me sett på binomiske data. Men det kan lagast kvalitetsindikatorar for mange andre typar data. Her skal me sjå kontinuerlege data, meir spesifikt data på intervall- eller forholdstalsnivå. Dette er data der det gjev meining å snakka om gjennomsnittsverdiar, medianar og kvantilar.
Ein definerer KI-funksjonar som før, men no skal dei returnera datarammer med (iallfall) desse to variablane:
-
ki_x: Verdien til den kontinuerlege variabelen. -
ki_aktuell: Logisk variabel som seier om observasjonen oppfølgde kriteria for å inngå i kvalitetsindikatoren (svarar altså tilki_krit_nevnerfor binomiske data).
Verdiane til ki_aktuell må vera sann eller usann, og
ki_x må vera eit tal (ikkje NA) dersom
ki_aktuell er sann.
Eksempel: langtidsblodsukker (HbA1c)
Eit kvalitetsregister har HbA1c-nivå på pasientane som ein kvalitetsindikator. Dette målar langtidsblodsukker (gjennomsnittleg blodsukkernivå over tid). Viss behandlinga er god, bør dette vera i intervallet 20–42 mmol/mol. Høge verdiar er negativt, og verdiar frå 48 mmol/mol indikerer diabetes. Her er eksempeldata:
d_reg_kont
#> # A tibble: 6 × 5
#> pas_id kjonn alder sjukehus hb1ac
#> <int> <fct> <dbl> <fct> <dbl>
#> 1 11 mann 18 A 26.1
#> 2 13 kvinne 36 C NA
#> 3 14 mann 25 B 53.8
#> 4 27 kvinne 29 A NA
#> 5 28 mann 78 B 65.2
#> 6 29 mann 45 B 49.8I det tilfellet er indikatoren ferdigutrekna, så KI-funksjonen vert svært enkelt:
Aggergeringsfunksjonar for kontinurlege data
Det kan finnast fleire aggregeringsfunksjonar for kontinuerlege data.
Førebels støttar me éin, for utrekning av gjennomsnittsverdiar,
aggreger_ki_snitt():
# Rekn ut kvalitetsindikatoren
d_hba1c = d_reg_kont |>
ki_hb1ac()
# Snittverdiar for heile registeret
d_hba1c |>
aggreger_ki_snitt()
#> # A tibble: 1 × 4
#> est konfint_nedre konfint_ovre n_aktuell
#> <dbl> <dbl> <dbl> <int>
#> 1 48.7 22.6 74.9 4
# Snittverdiar fordelt på sjukehus
d_hba1c |>
group_by(sjukehus) |>
aggreger_ki_snitt()
#> # A tibble: 3 × 5
#> sjukehus est konfint_nedre konfint_ovre n_aktuell
#> <fct> <dbl> <dbl> <dbl> <int>
#> 1 A 26.1 NA NA 1
#> 2 B 56.3 36.4 76.1 3
#> 3 C NA NA NA 0Så det ser ut til at pasientane frå sjukehus B gjer det dårlegare enn dei frå sjukehusa A (men me har sjølvsagt altfor lite data til å konkludera!).
Ting å merka seg:
- Summen av
n_aktueller 4, sjølv om det var 6 pasientar i datasettet. Grunnen er at to av dei mangla HBA1C-verdi. - Konfidensintervallet vert rekna ut dersom det kan reknast ut. Viss det for eksempel er berre éin pasient i ei gruppe, vert det ikkje rekna ut. For sjukehus A får me gjennomsnitt men ikkje konfidensintervall, mens for sjukehus C får me ingen av delane.
Føresetnadane for å bruka funksjonen er dei same som for bruk av t-testar: Det må gje meining å rekna ut gjennomsnittsverdiar, og fordelinga til dei empiriske gjennomsnitta må kunna godt approksimerast av tilhøyrande t-fordeling (dette er vanlegvis oppfylt dersom ein har ikkje veldig få observasjonar per grupppe, eller viss verdiane innanfor kvar gruppe er normalfordelte).
(Me har òg langsiktige planar om å legga til støtte for bootstrap-baserte konfindensintervall for gjennomsnitta. Desse kan brukast der t-fordelinga ikkje er realistisk.)
Ratedata/Poisson-data
Ein annan type data er ratedata - data som følgjer ein Poissonfordeling.
KI-funksjonar defineres fortsatt på same måte, men denne gang skal dei returnera dataramme med (ialffall) desse tre variablane:
-
ki_antall: Antall hendingar som er blitt observert. -
ki_eksponering: Perioden hendingane er blitt observert i. -
ki_aktuell: Logisk variabel som seier om hendingane og eksponeringsperioden oppfølgde kriteria for å inngå i kvalitetsindikatoren (lignende somki_aktuellfor kontinuerlige data, ogki_krit_nevnerfor binomiske data).
Verdiane til ki_aktuell må vera sann eller usann, og
ki_antall og ki_eksponering må vera eit
heiltal (ikkje NA) dersom ki_aktuell er sann.
Vidare må ki_eksponering vera større enn 0 (og
ki_antall naturlegvis minst lik 0).
Eksempel: Infeksjonar per liggedøgn
Eit eksempel kan vera antall infeksjonar hos pasienter per liggedøgn.
d_reg_rate
#> # A tibble: 6 × 6
#> pas_id kjonn alder sjukehus infeksjonar liggedogn
#> <int> <fct> <dbl> <fct> <dbl> <dbl>
#> 1 11 mann 18 A 0 10
#> 2 13 kvinne 36 C NA NA
#> 3 14 mann 25 B 2 8
#> 4 27 kvinne 29 A NA 11
#> 5 28 mann 78 B 1 7
#> 6 29 mann 45 B 2 11I dette tilfellet er indikatoren ferdiutrekna, så KI-funksjonen vert svært enkelt:
Aggergeringsfunksjonar for ratedata/Poisson-data
Aggregering av ratedata kan gjøres ved å anvende
aggreger_ki_rate():
# Rekn ut kvalitetsindikatoren
d_infeksjonar = d_reg_rate |>
ki_infeksjonar()
# Rateverdiar for heile registeret
d_infeksjonar |>
aggreger_ki_rate()
#> # A tibble: 1 × 3
#> est konfint_nedre konfint_ovre
#> <dbl> <dbl> <dbl>
#> 1 0.139 0.0498 0.298
# Rateverdiar fordelt på sjukehus
d_infeksjonar |>
group_by(sjukehus) |>
aggreger_ki_rate()
#> # A tibble: 3 × 4
#> sjukehus est konfint_nedre konfint_ovre
#> <fct> <dbl> <dbl> <dbl>
#> 1 A 0 0 0.300
#> 2 B 0.192 0.0690 0.413
#> 3 C NA NA NA
# Rateverdiar per uke
d_infeksjonar |>
group_by(sjukehus) |>
aggreger_ki_rate(multiplikator = 7)
#> # A tibble: 3 × 4
#> sjukehus est konfint_nedre konfint_ovre
#> <fct> <dbl> <dbl> <dbl>
#> 1 A 0 0 2.10
#> 2 B 1.35 0.483 2.89
#> 3 C NA NA NALegg merke til at konfidensintervallet bereknast ved profil-likelihood-metoden. Dermed blir ikkje nødvendigvis konfidensintervallet symmetrisk om estimatet. Dette skal gi betre estimater når det er få hendingar som er blitt observert. Konfidensintervallet blir, som med dei andre aggregerings-funksjonane, kun berekna dersom det er mogleg. Ved ratedata er det ikkje mogleg kun dersom det ikkje er nokon datapunkt som er aktuelle, som ved sjukehus C i eksempelet.
Generelt og detaljar om rammeverket
(fixme: Etter alle eksempela ser me no på rammeverket generelt. Må forklara kva me meiner med nivå 1 og nivå 2-funksjonar (skal dei ha andre namn?), dokumentera kva eigenskapar funksjonar på ulike nivå må ha (inndata og utdata), og ha ei oversikt over dei ulike typane pakken støttar (p.t. berre funksjonar for brøkdata og for kontinuerlege funksjonar). Skriv om at me har planar om funksjonar for ratedata og (kanskje) levetidsdata. Kanskje òg ei liste med dei ulike aggregeringsfunksjonane. Litt om designprinsippa bak metodikken (eks. tidyverse-tankegang, robustheit, minimera risiko for feil, gjera jobben raskare og enklare, konsistente funksjonsnamn). Eit eksempel på korleis ein lagar sin eigen type. Kan bruka korrelasjon som eksempel (og brukaren kan utvida funksjonen til å støtta ulike korrelasjonstypar). Skriva om kor viktig det er at funksjonane er robuste (eksempel for t.test-funksjonen: med 0 observasjonar, med SD == 0, og med SD ~= 0). Detaljar/råd om namngjeving av funksjonar og objekt, og at funksjonar bør leggast i pakkar.)