Jakie dane są zbierane i przez kogo?


W Polsce dane zdrowotne Polaków zbiera wiele różnych instytucji. Poniżej - w dużym uproszczeniu - rozpiska kto co zbiera i jakie to dane.


  1. NFZ (Narodowy Fundusz Zdrowia)

Jakie dane?

  • informacje o świadczeniach refundowanych:

    • wizyty u lekarzy POZ i specjalistów (AOS)

    • hospitalizacje (szpital)

    • zabiegi

    • badania diagnostyczne

    • refundowane leki i wyroby medyczne

  • dane identyfikujące pacjenta (PESEL, wiek, płeć, adres – w zakresie potrzebnym do rozliczenia)

  • dane o rozpoznaniach (kody ICD-10 1), procedurach (ICD-9 2 , katalogi NFZ) itd.

Do czego?

  • rozliczanie świadczeń z placówkami (świadczeniodawcami)

  • statystyka i planowanie finansowania

  • kontrola nadużyć

  • analiza kosztów

NFZ ma ogromny obraz tego, co i za ile było za osobę refundowane, ale niekoniecznie wszystko, co pacjent robił prywatnie


  1. CeZ (Centrum e-Zdrowia – dawniej CSIOZ)

To instytucja odpowiedzialna za systemy e-zdrowia (P1, IKP itd.).

  1. System P1 (platforma e-zdrowia)

Jakie dane tam trafiają?

  • e-recepty – kto wystawił, komu (PESEL), jakie leki, dawki, realizacja w aptece

  • e-skierowania – na jakie świadczenia, przez kogo, dla kogo

  • e-zwolnienia (ZUS ZLA) – info o niezdolności do pracy (wraz z ZUS)

  • dokumenty elektroniczne EDM (gdzie są przechowywane, kto ma dostęp)

Same dokumenty EDM przechowywane są najczęściej w systemach szpitali/przychodni, ale P1 wie, że istnieją i gdzie.

  1. IKP – Internetowe Konto Pacjenta

To interfejs dla Ciebie, ale dane stoją za nim na P1/NFZ:

  • podgląd e-recept, e-skierowań, historii świadczeń refundowanych

  • często dane z programów profilaktycznych, szczepień itd.


  1. Podmioty lecznicze (szpitale, przychodnie, gabinety)

Każdy podmiot leczniczy prowadzi dokumentację medyczną pacjenta.

Jakie dane?

  • wywiad zdrowotny, rozpoznania, wyniki badań

  • opisy zabiegów, wypisy ze szpitala

  • historie chorób, pomiary (ciśnienie, waga), obrazowanie (RTG, TK, MRI)

  • zgody pacjenta, informacje o alergiach, lekach przyjmowanych itd.

Gdzie?

  • w systemach informatycznych danej placówki (HIS, gabinetowe, RIS/PACS) 3

  • częściowo w formie papierowej (archiwa)

Część z tych informacji może być udostępniana jako EDM i integrowana z P1, ale nie wszystko jest jeszcze w pełni zintegrowane i ujednolicone


  1. Sanepid (Państwowa Inspekcja Sanitarna) i inne instytucje nadzoru

Jakie dane?

  • zgłoszenia chorób zakaźnych i zakażeń (np. COVID, gruźlica, WZW, HIV itd.)

  • dane o szczepieniach ochronnych (zwłaszcza obowiązkowych)

  • dane epidemiologiczne z laboratoriów i placówek medycznych (np. ogniska epidemiczne, zatrucia)

Te dane są bardziej zbiorcze, ale przy zgłoszeniach bywają dane identyfikujące (PESEL, adres) – potrzebne do dochodzenia epidemiologicznego.


  1. GUS (Główny Urząd Statystyczny)

GUS “nie leczy”, ale zbiera statystyki zdrowotne:

  • dane o zgonach i przyczynach zgonu (na podstawie kart zgonu) Jak GUS prowadzi statystykę zgonów?

  • dane o hospitalizacjach, zachorowaniach na wybrane choroby

  • dane z badań statystycznych (np. ankietowe zdrowie populacji, styl życia)

GUS stara się anonimizować/zbiorczo prezentować dane – interesuje go bardziej „ile osób na 100 tys. zachorowało”, niż konkretny pacjent


  1. ZUS (Zakład Ubezpieczeń Społecznych)

Jakie dane zdrowotne ma ZUS?

  • informacje o niezdolności do pracy (e-ZLA)

    • okres niezdolności

    • kod jednostki chorobowej (często skrócony, np. F32)

  • dane z orzecznictwa – komisje ZUS do spraw niezdolności do pracy, renty

  • dane dot. wypadków przy pracy, chorób zawodowych (częściowo z PIP/pracodawcami)

Nie ma pełnej dokumentacji medycznej, ale ma sporo danych dotyczących zdolności do pracy i przyczyn.


  1. Uczelnie, instytuty badawcze, rejestry medyczne

Istnieją różne rejestry chorób i programów:

  • Krajowy Rejestr Nowotworów

  • rejestry kardiologiczne, diabetologiczne, transplantacyjne itd.

  • badania prowadzone przez NFZ, MZ, instytuty (np. NIZP-PZH, IMP itd.)

Zwykle:

  • dane są pseudonimizowane/anonymizowane, ale na etapie zbierania często są dane osobowe

  • celem jest monitorowanie jakości leczenia, przeżywalności, skuteczności terapii


  1. Pracodawcy i medycyna pracy

Gdzie i jakie dane?

  • jednostki medycyny pracy, z którymi pracodawca ma umowę, mają:

    • orzeczenia o zdolności/niezdolności do pracy

    • wyniki badań profilaktycznych (w zakresie potrzebnym do orzeczenia)

Pracodawca nie powinien widzieć pełnych danych medycznych – dostaje tylko orzeczenie (zdolny/niezdolny, ewentualne ograniczenia), a dokumentacja jest u lekarza medycyny pracy.


  1. Firmy prywatne – ubezpieczyciele, abonamenty medyczne, aplikacje

Coraz więcej danych zdrowotnych ląduje też u podmiotów niepublicznych:

  • prywatne przychodnie i sieci ( Luxmed, MediCover, Enel-Med itd.) – mają pełną dokumentację swoich pacjentów

  • prywatni ubezpieczyciele – dane z wniosków, ankiet medycznych, rozliczanych świadczeń

  • aplikacje mobilne / wearables:

    • kroki, sen, tętno, EKG, poziom stresu

    • dzienniczki glikemii, cyklu miesięcznego, diety, treningów

Tu zakres zależy od regulaminów, zgód marketingowych, RODO – często użytkownik akceptuje dość szerokie przetwarzanie.




Gdzie szukać?

Mapy Potrzeb Zdrowotnych

Dostępne dashboard’y / analizy na stronie:

Link do MPZ



Link do MPZ




Krajowy Rejestr Nowotworów


Link do KRN




Główny Urząd Statystyczny



Link do GUS


Use Cases





KRN



Na jakie nowotwory zapadają mieszkańcy “zapylonych”, dużych miast, raki związane z drogami oddechowymi??



Claude:







Wygenerujmy na stronie KRN podsumowanie zapadalności na podane przez model językowy rozpoznania ICD-10 (C34, C32, C30–C31, C11, C33), w okresie 2000-2010, dla obu płci łącznie, dla wszystkich grup wiekowych














GUS



Sporo się mówi, że Kraków jest położony w niecce, palą tam “kopciuchami”, a Katowice, to takie uprzemysłowione, zanieczyszczone miasto, z wiecznymi dymami z ogromnych kominów unoszących się nad dachami miasta. Zakładam zatem, że mieszkańcy właśnie tych miast żyją najkrócej!



Przeczesując odpowiednie zakładki na stronie oraz eksplorując szereg typów zbieranych statystyk, natrafiamy w koću na właściwą podstronę.

Warto zacząć od publikacji w .pdf, przybliżyć sobie kontekst, stosowane definicje i metody. Wówczas możemy znaleźć skojarzony z tematem plik .xlsx i analizować go na własną rękę.







Wyniki są co najmniej zastanawiające.

Dwa wyimki z raportu z roku 2023

“Trwanie życia było najdłuższe dla mężczyzn w Krakowie (77,1 roku)”

“Największą różnicę między trwaniem życia mężczyzn i kobiet odnotowano w Łodzi (7,5 roku). Na tle innych wielkich miast bardzo niekorzystnie wypadają podregion katowicki oraz Łódź, w których trwanie życie jest krótsze o więcej niż rok w stosunku do średniej krajowe”.

Jeden cytat z raportu z roku 2024

“W 2024 r. najdłuższe trwanie życia dla mężczyzn i kobiet odnotowano w Warszawie (odpowiednio 77,74 oraz 83,86 roku). Najkrótsze natomiast dla mężczyzn w Łodzi (73,51 roku), a dla kobiet w podregionie katowickim (80,25 roku).”



Jak możemy to potencjalnie wyjaśnić?





Występuje tu tzw. efekt “premii miejskiej”, który w skrócie sprowadza się do tego, że w mieście mamy:

  • Lepszą opiekę medyczną (Dostęp do specjalistów, szpitali klinicznych)

  • Wyższe wykształcenie, a co za tym idzie zdrowszy tryb życia, profilaktyka

  • Wyższe dochody, a to oznacza lepszą dietę, lepszej jakości produkty, aktywność fizyczną

  • Migracja selekcyjna, do miast przyjeżdżają zdrowsi, młodsi ludzie, za pracą, na studia, etc.

  • Mniejsze palenie tytoniu? W miastach pali się mniej niż na wsi. Ale tu poczekałbym na efekty ‘wapowania’.











Policja



“Kiedyś to było bezpiecznie! Teraz strach dziecko na podwórko puścić, Panie Kochany…”

Zobaczmy



“Drogi puste, ale nie spokojniejsze”

Przykład analizy











Mapy Potrzeb Zdrowotnych



Na co umierali Polacy w trakcie pandemii Covid-19?





Nowotwory i ich profilaktyka w dobie pandemii Covid-19

Przykład analizy











Zdrowe Dane (NFZ)



Liczba depresji?



Depresja widziana z punktu widzenia płatnika świadczeń.

Przykład raportu












Przykładowa analiza w R


Physionet


Literatura:

Johnson A, Pollard T, Mark R. MIMIC-III Clinical Database Demo (version 1.4). physionet.org. 2019. RRID:SCR_007345. Available from: https://doi.org/10.13026/C2HM2Q

Dane


Link do strony


Po ściągnięciu pliku .zip na dysk można z poziomu skryptu wypakować wszystkie pliki:

# utils::unzip(zipfile = "dane/mimic-iii-clinical-database-demo-1.4.zip")

Lub wczytać pojedynczo ściągnięte pliki .csv:

# Pacjenci
PATIENTS <- read.csv(file = "dane/PATIENTS.csv")

# Hospitalizacje
ADMISSIONS <- read.csv(file = "dane/ADMISSIONS.csv")

Zrozumienie zbioru

dplyr::glimpse(PATIENTS)
## Rows: 100
## Columns: 8
## $ row_id      <int> 9467, 9472, 9474, 9478, 9479, 9486, 9487, 9489, 9491, 9492…
## $ subject_id  <int> 10006, 10011, 10013, 10017, 10019, 10026, 10027, 10029, 10…
## $ gender      <chr> "F", "F", "F", "F", "M", "F", "F", "M", "M", "F", "M", "F"…
## $ dob         <chr> "2094-03-05 00:00:00", "2090-06-05 00:00:00", "2038-09-03 …
## $ dod         <chr> "2165-08-12 00:00:00", "2126-08-28 00:00:00", "2125-10-07 …
## $ dod_hosp    <chr> "2165-08-12 00:00:00", "2126-08-28 00:00:00", "2125-10-07 …
## $ dod_ssn     <chr> "2165-08-12 00:00:00", "", "2125-10-07 00:00:00", "2152-09…
## $ expire_flag <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
dplyr::glimpse(ADMISSIONS)
## Rows: 129
## Columns: 19
## $ row_id               <int> 12258, 12263, 12265, 12269, 12270, 12277, 12278, …
## $ subject_id           <int> 10006, 10011, 10013, 10017, 10019, 10026, 10027, …
## $ hadm_id              <int> 142345, 105331, 165520, 199207, 177759, 103770, 1…
## $ admittime            <chr> "2164-10-23 21:09:00", "2126-08-14 22:32:00", "21…
## $ dischtime            <chr> "2164-11-01 17:15:00", "2126-08-28 18:59:00", "21…
## $ deathtime            <chr> "", "2126-08-28 18:59:00", "2125-10-07 15:13:00",…
## $ admission_type       <chr> "EMERGENCY", "EMERGENCY", "EMERGENCY", "EMERGENCY…
## $ admission_location   <chr> "EMERGENCY ROOM ADMIT", "TRANSFER FROM HOSP/EXTRA…
## $ discharge_location   <chr> "HOME HEALTH CARE", "DEAD/EXPIRED", "DEAD/EXPIRED…
## $ insurance            <chr> "Medicare", "Private", "Medicare", "Medicare", "M…
## $ language             <chr> "", "", "", "", "", "", "", "", "", "POLI", "", "…
## $ religion             <chr> "CATHOLIC", "CATHOLIC", "CATHOLIC", "CATHOLIC", "…
## $ marital_status       <chr> "SEPARATED", "SINGLE", "", "DIVORCED", "DIVORCED"…
## $ ethnicity            <chr> "BLACK/AFRICAN AMERICAN", "UNKNOWN/NOT SPECIFIED"…
## $ edregtime            <chr> "2164-10-23 16:43:00", "", "", "2149-05-26 12:08:…
## $ edouttime            <chr> "2164-10-23 23:00:00", "", "", "2149-05-26 19:45:…
## $ diagnosis            <chr> "SEPSIS", "HEPATITIS B", "SEPSIS", "HUMERAL FRACT…
## $ hospital_expire_flag <int> 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0…
## $ has_chartevents_data <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…


Zazwyczaj dostępna jest jakaś dokumentacja zbioru na stronie, bądź “zaszyta” w pakiecie, z którego zbiór pozyskujemy.


Opis zmiennych ze zbiorów.

PATIENTS

  • row_id – techniczny klucz główny (ID wiersza), w analizach najczęściej niepotrzebny.

  • subject_id – identyfikator pacjenta, łączy wszystkie tabele dot. tej osoby (klucz do PATIENTS, ADMISSIONS itd.).

  • gender – płeć (‘M’, ‘F’).

  • dobdate of birth – data urodzenia (przesunięta w czasie dla anonimizacji).

  • doddate of death – data zgonu (jeśli znana, z różnych źródeł; NA jeśli pacjent przeżył ≥90 dni od wypisu).

  • dod_hosp – data zgonu wg dokumentacji szpitalnej (jeśli zmarł w szpitalu).

  • dod_ssn – data zgonu wg Social Security (rejestr zgonów USA).

  • expire_flag – flaga 0/1 – czy pacjent zmarł (1 = tak).


ADMISSIONS

  • row_id – techniczny klucz wiersza.

  • subject_id – pacjent (łączy z PATIENTS).

  • hadm_id – hospital admission ID – konkretne przyjęcie do szpitala (klucz tej tabeli).

  • admittime – data/godzina przyjęcia do szpitala.

  • dischtime – data/godzina wypisu ze szpitala.

  • deathtime – data/godzina zgonu w szpitalu (jeśli pacjent zmarł podczas tego pobytu).

  • admission_type – typ przyjęcia (np. EMERGENCY, ELECTIVE, URGENT).

  • admission_location – skąd pacjent trafił (np. z SOR, z innego oddziału, z innego szpitala).

  • discharge_location – dokąd został wypisany (do domu, na inny oddział, do hospicjum itd.).

  • insurance – typ ubezpieczenia (np. MEDICARE, PRIVATE).

  • language – język pacjenta (np. ENGL).

  • religion – religia (np. CATHOLIC).

  • marital_status – stan cywilny (MARRIED, SINGLE…).

  • ethnicity – grupa etniczna (np. WHITE, BLACK/AFRICAN AMERICAN).

  • edregtime – czas rejestracji na SOR.

  • edouttime – czas wypisu z SOR.

  • diagnosis – tekstowy opis rozpoznania przy przyjęciu (nie kod ICD; kody są w DIAGNOSES_ICD).

  • hospital_expire_flag – flaga 0/1 – czy pacjent zmarł w tym pobycie w szpitalu.

  • has_chartevents_data – 0/1 – czy dla tej hospitalizacji są dane w tabeli CHARTEVENTS (ciągłe pomiary z OIOM).


Opis zbiorów


Łączenie zbiorów

# Łączenie na poziomie hospitalizacji 
# (każdy wiersz = jeden pobyt z cechami pacjenta)
dane <- ADMISSIONS %>%
  left_join(PATIENTS, 
            by = "subject_id")



Analiza


Struktura demograficzna pacjentów.

rozklad_plci <- PATIENTS %>%
  count(gender) %>%
  mutate(Procent = n / sum(n) * 100)

# tabela
kableExtra::kable(rozklad_plci, 
                  caption = "Rozkład płci", 
                  col.names = c("Płeć", "Liczba obserwacji", "%")
                  )
Rozkład płci
Płeć Liczba obserwacji %
F 55 55
M 45 45


# alternatywnie, inny sposób liczenia i inna funkcja print'ująca tabelę
DT::datatable(
  janitor::tabyl(PATIENTS$gender) # <-
  ,colnames = c("Płeć", "Liczba pacjentów", "%")
  )


# etykiety z procentami
labels <- paste0(rozklad_plci$gender, ": ", round(rozklad_plci$Procent, 1), "%")

pie(x = rozklad_plci$Procent,
    labels = labels,
    col = c("darkblue",
            "lightblue"),
    main = "Rozkład płci wśród pacjentów")


library(ggplot2)

rozklad_plci <- rozklad_plci %>%
  mutate(
    label = paste0(gender, ": ", round(Procent, 1), "%") # etykiety z procentami
    )

ggplot(rozklad_plci, 
       aes(x = "", 
           y = Procent, 
           fill = gender)) +
  geom_col(width = 1) +
  coord_polar(theta = "y") +
  geom_text(aes(label = label),
            position = position_stack(vjust = 0.5)) +
  scale_fill_manual(values = c("darkblue",
                               "lightblue")) +
  labs(
    x = NULL,
    y = NULL,
    fill = "Płeć",
    title = "Rozkład płci wśród pacjentów"
  ) +
  theme_void()



Wiek przy pierwszej hospitalizacji (zbiór ma przesunięte daty z uwagi na procedury pseudonimizacyjne, ale relacje czasowe są zachowane).

library(lubridate) # pakiet / narzędzie służące do łatwej i wygodnej pracy z datami i czasem

admissions_min <- ADMISSIONS %>%
  group_by(subject_id) %>%
  summarise(first_admit = min(as_datetime(admittime)))

demo_age <- admissions_min %>%
  left_join(PATIENTS, by = "subject_id") %>%
  mutate(
    dob = as_datetime(dob),
    age_first_admit = as.numeric(difftime(first_admit, dob, units = "days")) / 365.25
  )

kableExtra::kable(
  t(as.matrix(
    summary(demo_age$age_first_admit) # <- 
    )
    )
  )
Min. 1st Qu. Median Mean 3rd Qu. Max.
17.1916 64.93596 76.90854 88.45447 85.17951 299.9969


hist(demo_age$age_first_admit,
     main = "Histogram wieku przy pierwszej hospitalizacji",
     xlab = "Wiek przy pierwszej hospitalizacji",
     col = "lightblue",
     border = "black",
     breaks = 30
     # ,xlim = c(0, 105) # <- jeśli nie chcemy wyrzucać ze zbioru możemy wyskalować oś ox
     )


hist(demo_age$age_first_admit,
     main = "Histogram wieku przy pierwszej hospitalizacji",
     xlab = "Wiek przy pierwszej hospitalizacji",
     col = "lightblue",
     border = "black",
     breaks = 30
     ,xlim = c(0, 105) # <- jeśli nie chcemy wyrzucać ze zbioru możemy wyskalować oś ox
     )


ggplot2::ggplot(demo_age, 
                aes(x = age_first_admit)) +
  geom_histogram(binwidth = 5,
                 fill = "skyblue2",
                 color = "black") +
  labs(title = "Histogram wieku przy pierwszej hospitalizacji",
       x = "Wiek przy pierwszej hospitalizacji",
       y = "Liczba") +
  theme_minimal()


ggplot2::ggplot(demo_age, 
                aes(x = age_first_admit)) +
  geom_histogram(binwidth = 5,
                 fill = "skyblue3",
                 color = "black") +
  labs(title = "Histogram wieku przy pierwszej hospitalizacji",
       x = "Wiek przy pierwszej hospitalizacji",
       y = "Liczba") +
  scale_x_continuous(limits = c(1,100)) +
  theme_minimal()


Liczba hospitalizacji na pacjenta

admissions_per_patient <- ADMISSIONS %>%
  count(subject_id, name = "n_admissions")

kableExtra::kable(
  t(as.matrix(
    summary(admissions_per_patient$n_admissions) # <- 
    )
    )
  )
Min. 1st Qu. Median Mean 3rd Qu. Max.
1 1 1 1.29 1 15
kableExtra::kable(
  janitor::tabyl(admissions_per_patient$n_admissions) # <-
  ,caption = "Liczba hositalizacji na pacjenta", 
  col.names = c("Liczba pobytów", "Liczba pacjentów", "%")
  )
Liczba hositalizacji na pacjenta
Liczba pobytów Liczba pacjentów %
1 86 0.86
2 11 0.11
3 2 0.02
15 1 0.01


# tabela: ile osób ma 1, 2, 3+ pobytów?
kableExtra::kable(x = 
                    admissions_per_patient %>%
                    mutate(n_cat = ifelse(test = n_admissions >= 3,
                                          yes = "3+",
                                          no =  as.character(n_admissions))) %>%
                    count(n_cat)
                  , col.names = c("Liczba pobytów", "Liczba pacjentów")
                  , caption = "Liczba pojedynczych i ponownych hospitalizacji"
                  )
Liczba pojedynczych i ponownych hospitalizacji
Liczba pobytów Liczba pacjentów
1 86
2 11
3+ 3


Długość pobytu (LOSlength of stay)


admissions_los <- ADMISSIONS %>%
  mutate(
    admittime = as.POSIXct(admittime, tz = "UTC"), # data/godzina przyjęcia do szpitala
    dischtime = as.POSIXct(dischtime, tz = "UTC"), # data/godzina wypisu ze szpitala
    los_days = as.numeric(difftime(dischtime, admittime, units = "days"))
  )


kableExtra::kable(
  t(as.matrix(
    summary(round(admissions_los$los_days, 1)) # <-
    )
    )
  )
Min. 1st Qu. Median Mean 3rd Qu. Max.
0 3.3 6.6 9.332558 10.6 124


ggplot(admissions_los, 
       aes(x = los_days)) +
  geom_density(
    fill = "#3399FF",         # Niebieski kolor wypełnienia
    color = "#0066CC",        # Ciemniejsza obwódka
    alpha = 0.7               # Przezroczystość wypełnienia
  ) +
  labs(
    title = "Rozkład Gęstości Czasu Pobytu (los_days)",
    x = "Liczba Dni Pobytu (los_days)",
    y = "Gęstość"
  ) +
  theme_minimal()


ggplot(admissions_los %>% filter(los_days < 40), 
       aes(x = los_days)) +
  geom_density(
    fill = "#3399FF",         # Niebieski kolor wypełnienia
    color = "#0066CC",        # Ciemniejsza obwódka
    alpha = 0.7               # Przezroczystość wypełnienia
  ) +
  labs(
    title = "Rozkład Gęstości Czasu Pobytu (los_days)",
    x = "Liczba Dni Pobytu (los_days)",
    y = "Gęstość"
  ) +
  theme_minimal()


ggplot(admissions_los, 
       aes(x = los_days)) +
  geom_histogram(
    bins = 30,                # Liczba słupków (przedziałów)
    fill = "#3399FF",         # kolor wypełnienia
    color = "lightslateblue"  # obramowanie słupków
  ) +
  labs(
    title = "Histogram Czasu Pobytu (los_days)",
    x = "Liczba Dni Pobytu (los_days)",
    y = "Częstotliwość"
  ) +
  theme_minimal()


ggplot(admissions_los %>% filter(los_days<40), 
       aes(x = los_days)) +
  geom_histogram(
    bins = 50,                # Liczba słupków (przedziałów)
    fill =  "lightslateblue", # kolor wypełnienia
    color =  "#3399FF"        # obramowanie słupków
  ) +
  labs(
    title = "Histogram Czasu Pobytu (los_days)",
    x = "Liczba Dni Pobytu (los_days)",
    y = "Częstotliwość"
  ) +
  theme_minimal()


ggplot(admissions_los, 
       aes(x = admission_type, 
           y = los_days)) +
  geom_boxplot() +
  coord_flip() +
  labs(x = "Typ przyjęcia", 
       y = "Długość pobytu [dni]") +
  theme_minimal()


ggplot(admissions_los, 
       aes(x = admission_type, 
           y = los_days)) +
  geom_boxplot() +
  coord_flip() +
  labs(x = "Typ przyjęcia", 
       y = "Długość pobytu [dni]") +
  scale_y_continuous(limits = c(1,40)) +
  theme_minimal()


Po spolszczeniu kategorii, żeby były bardziej intuicyjne.

admissions_los <- admissions_los %>%
  mutate(
    admission_type_pl = case_when(
      admission_type == "ELECTIVE"  ~ "planowy",
      admission_type == "EMERGENCY" ~ "nagły",
      admission_type == "URGENT"    ~ "pilny",
      admission_type == "NEWBORN"   ~ "noworodek",
      TRUE                          ~ "inny"
    )
  )

ggplot(admissions_los, 
       aes(x = admission_type_pl, 
           y = los_days,
           fill = admission_type_pl)) +   # kolor wg typu
  geom_boxplot() +
  coord_flip() +
  scale_fill_manual(
    values = c(
      "planowy"   = "#4C78A8",
      "pilny"     = "#F58518",
      "nagły"     = "#E45756",
      "noworodek" = "#72B7B2",
      "inny"      = "#B279A2"
    )
  ) +
  labs(
    x = "Typ przyjęcia", 
    y = "Długość pobytu [dni]",
    fill = "Typ przyjęcia"
  ) +
  scale_y_continuous(limits = c(1,40)) +
  theme_minimal() +
  theme(
    legend.position = "none"  # jeśli nie chcemy legendy (wynika z osi oy)
  )



Śmiertelność w szpitalu.

ADMISSIONS %>%
  summarise(
    n = n(),
    deaths = sum(hospital_expire_flag),
    mortality_pct = round(100 * deaths / n, 2)) %>%
  kableExtra::kable(
    col.names = c("Liczba pacjentów", "Liczba zgonów", "Udział (%)"), 
    caption = "Zgony szpitalne")
Zgony szpitalne
Liczba pacjentów Liczba zgonów Udział (%)
129 40 31.01



Śmiertelność wg typu przyjęcia

ADMISSIONS %>%
  group_by(admission_type) %>%
  summarise(
    n = n(),
    deaths = sum(hospital_expire_flag),
    mortality_pct = 100 * deaths / n
  ) %>%
  arrange(desc(mortality_pct)) %>%
  mutate(admission_type = case_when(
    admission_type == "ELECTIVE"  ~ "planowy",
    admission_type == "EMERGENCY" ~ "nagły",
    admission_type == "URGENT"    ~ "pilny",
    TRUE                          ~ "inny")
    ) %>%
  kableExtra::kable(
    col.names = c("Tryb przyjęcia", "Liczba pacjentów", "Liczba zgonów", "Udział (%)"), 
    caption = "Zgony szpitalne wg typu przyjęcia")
Zgony szpitalne wg typu przyjęcia
Tryb przyjęcia Liczba pacjentów Liczba zgonów Udział (%)
pilny 2 1 50.00000
nagły 119 39 32.77311
planowy 8 0 0.00000


Krzywa przeżycia w szpitalu


Bazując na ramce danych ADMISSIONS można używając estymatora Kaplana-Meyera zrobić uproszczoną analizę przeżycia w szpitalu: czas od przyjęcia do dischtime/deathtime, event = hospital_expire_flag.

library(survival)
library(survminer)  # ładne wykresy

surv_df <- admissions_los %>%
  filter(los_days < 50) %>% # !
  mutate(
    time_days = los_days,
    event = hospital_expire_flag
  )

fit <- survfit(
  Surv(time_days, event) ~ 1, 
  data = surv_df
  )

ggsurvplot(fit, 
           xlab = "Dni od przyjęcia", 
           ylab = "Prawdopodobieństwo przeżycia")


df_full <- admissions_los %>%
  left_join(demo_age %>% 
              select(subject_id, age_first_admit) %>% 
              filter(age_first_admit < 100), # !
            by = "subject_id")

ggplot(df_full, 
       aes(x = age_first_admit, 
           y = hospital_expire_flag)) +
  geom_smooth(method = "loess") +
  labs(x = "Wiek przy pierwszej hospitalizacji",
       y = "Szacowane prawdopodobieństwo zgonu w szpitalu") +
  theme_minimal()

# komentarz:
# geom_smooth():
# - bierze sobie wszystkie punkty (age_first_admit, hospital_expire_flag)
# - dopasowuje gładką krzywą (tu: LOESS)
# - i rysuje wartość oczekiwaną Y w funkcji X, czyli E[Y | X = wiek].


df_full <- df_full %>%
  filter(age_first_admit < 100) %>% # !
  mutate(age_group = cut(age_first_admit,
                         breaks = c(0, 40, 60, 80, 120),
                         labels = c("<40", "40–59", "60–79", "80+")))

smiert_po_grupach <- df_full %>%
  group_by(age_group) %>%
  summarise(
    n = n(),
    deaths = sum(hospital_expire_flag),
    mortality_pct = round(100 * deaths / n, 1)
  )

kableExtra::kable(smiert_po_grupach, 
                  col.names = c("Grupa wiekowa", "Liczba pacjentów", "Liczba zgonów", "%"),
                  caption = "Śmiertelność szpitalna po grupach wiekowych")
Śmiertelność szpitalna po grupach wiekowych
Grupa wiekowa Liczba pacjentów Liczba zgonów %
<40 6 4 66.7
40–59 21 6 28.6
60–79 56 10 17.9
80+ 37 16 43.2


ggplot(smiert_po_grupach, 
       aes(x = age_group, 
           y = mortality_pct)) +
  geom_col(fill = "steelblue") +
  labs(
    x = "Grupa wieku",
    y = "Śmiertelność w szpitalu [%]",
    title = "Śmiertelność szpitalna według grup wieku"
  ) +
  theme_minimal()


ggplot(smiert_po_grupach, 
       aes(x = age_group, 
           y = mortality_pct,
           fill = age_group)) +  # Przypisanie koloru do zmiennej
  geom_col() +
  # Użycie skali manualnej do ręcznego ustawienia kolorów
  scale_fill_manual(
    values = c("<40" = "#ADD8E6",
               "40–59" = "#ADD8E6", 
               "60–79" = "#4682B4", 
               "80+" = "#191970") # Przykładowe kolory dla każdej kategorii
  ) +
  labs(
    x = "Grupa wieku",
    y = "Śmiertelność w szpitalu [%]",
    title = "Śmiertelność szpitalna według grup wieku",
    fill = "Grupa Wieku" # Zmiana tytułu legendy
  ) +
  theme_minimal()




{mlbench}


{mlbench} to pakiet z przykładowymi zbiorami danych do ćwiczenia metod uczenia maszynowego (machine learning benchmark).


Dane

Zawiera różne dobrze znane zestawy, m.in.:

  • PimaIndiansDiabetes
  • BreastCancer
  • i inne
# install.packages("mlbench")
library(mlbench)

PimaIndiansDiabetes to data.frame z danymi medycznymi kobiet z plemienia Pima (rdzenni mieszkańcy Ameryki), używanymi do przewidywania, czy ktoś ma cukrzycę (diabetes).

data("PimaIndiansDiabetes")
str(PimaIndiansDiabetes)
## 'data.frame':    768 obs. of  9 variables:
##  $ pregnant: num  6 1 8 1 0 5 3 10 2 8 ...
##  $ glucose : num  148 85 183 89 137 116 78 115 197 125 ...
##  $ pressure: num  72 66 64 66 40 74 50 0 70 96 ...
##  $ triceps : num  35 29 0 23 35 0 32 0 45 0 ...
##  $ insulin : num  0 0 0 94 168 0 88 0 543 0 ...
##  $ mass    : num  33.6 26.6 23.3 28.1 43.1 25.6 31 35.3 30.5 0 ...
##  $ pedigree: num  0.627 0.351 0.672 0.167 2.288 ...
##  $ age     : num  50 31 32 21 33 30 26 29 53 54 ...
##  $ diabetes: Factor w/ 2 levels "neg","pos": 2 1 2 1 2 1 2 1 2 2 ...


Dokumentacja


Co oznaczają kolumny?

  • pregnant – liczba ciąż

  • glucose – stężenie glukozy w osoczu po 2h w teście doustnym (oral glucose tolerance test)

  • pressure – ciśnienie rozkurczowe (diastolic blood pressure, mm Hg)

  • triceps – grubość fałdu skórnego tricepsa (mm)

  • insulin – stężenie insuliny w surowicy (2 godziny, μU/ml)

  • mass – BMI (body mass index), czyli masa / wzrost

  • pedigree – wskaźnik obciążenia genetycznego cukrzycą (diabetes pedigree function)

  • age – wiek (w latach)

  • diabetes – zmienna wynikowa (target):

    • neg” – brak cukrzycy

    • pos” – cukrzyca obecna


Eksploracja zbioru


Zazwyczaj zapoznajemy się ze zbiorem eksplorując go, oglądając zmienne, rysując sobie rozkłady, tworząc tabele częstości, krzyżując jakieś zmienne ze sobą.


# Cukrzycy i zdrowi
table(PimaIndiansDiabetes$diabetes)
## 
## neg pos 
## 500 268


# Wiek a BMI
plot(y = PimaIndiansDiabetes$age,
     x = PimaIndiansDiabetes$mass)


# Glukowaza a BMI
plot(PimaIndiansDiabetes$glucose, 
     PimaIndiansDiabetes$mass,
     xlab = "Glukoza",
     ylab = "BMI")


# Cukrzyca a BMI
plot(PimaIndiansDiabetes$diabetes, 
     PimaIndiansDiabetes$mass,
     xlab = "Cukrzyca",
     ylab = "BMI")


Gdy zatrzymamy się na czymś ciekawym, zazwyczaj sięgamy po literaturę, dopytujemy ekspertów, etc., żeby zrozumieć naturę jakiegoś zjawiska, a następnie pogłębiamy analizę.


Interesujące zagadnienie


“Wysokie BMI matką wszystkich chorób” - autor nieznany


Co ogólnie wiemy o BMI?


Jak wygląda nasza populacja pod kątem wagi?

hist(PimaIndiansDiabetes$mass)


Model

Sprawdźmy zatem, jak BMI wpływa na prawdopodobieństwo wystąpienia cukrzycy.


Zbudujmy model.


model_bmi <- glm(diabetes ~ mass,
                 family = binomial, 
                 data = PimaIndiansDiabetes
                 )

Powyższy kod ma za zadanie:

  • zbudować model regresji logistycznej (glm(..., family = binomial)),

  • w którym zmienną objaśnianą jest diabetes (czy osoba ma cukrzycę – tak/nie),

  • a zmienną objaśniającą jest mass (BMI – wskaźnik masy ciała),

  • na danych z ramki PimaIndiansDiabetes




Model matematycznie ma postać:

\[ \text{logit}\big(P(\text{diabetes} = "pos")\big) = \beta_0 + \beta_1 \cdot \text{mass} \]

równoważne z:

\[ \log\left(\frac{p}{1-p}\right) = \beta_0 + \beta_1 \cdot \text{BMI} \]

gdzie:

  • \(p = P(\text{cukrzyca} = "pos")\),
  • \(\text{mass}\) to BMI,
  • \(\beta_0\) – wyraz wolny (przesunięcie),
  • \(\beta_1\) – wpływ BMI na log-odds cukrzycy.


Po przekształceniu:

\[ p = \frac{1}{1 + e^{-(\beta_0 + \beta_1 \cdot \text{BMI})}} \]



Podsumowanie modelu

summary(model_bmi)
## 
## Call:
## glm(formula = diabetes ~ mass, family = binomial, data = PimaIndiansDiabetes)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -3.68641    0.40896  -9.014  < 2e-16 ***
## mass         0.09353    0.01205   7.761 8.45e-15 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 993.48  on 767  degrees of freedom
## Residual deviance: 920.71  on 766  degrees of freedom
## AIC: 924.71
## 
## Number of Fisher Scoring iterations: 4

Krótki, uproszczony opis powyższego podsumowania.

  1. Współczynniki
  • Intercept (−3.686) – to jest „punkt startowy” modelu, logit prawdopodobieństwa cukrzycy, gdy mass = 0 (czysto techniczny, sam w sobie mało interpretowalny, bo BMI = 0 nie ma sensu).

  • mass: 0.09353 – to główny wynik:

    • dodatni współczynnik → im wyższe BMI, tym wyższe prawdopodobieństwo cukrzycy

    • w skali logitów to 0.0935 na każdy 1 punkt BMI

    • w skali ilorazu szans (odds ratio): exp(0.09353)≈1.10 czyli wzrost BMI o 1 jednostkę zwiększa szanse cukrzycy o ok. 10%.

  • p-value dla mass: 8.45e-15 i ***

    → efekt BMI jest bardzo istotny statystycznie (praktycznie zerowe prawdopodobieństwo, że to przypadek).

  1. Dopasowanie modelu
  • Null deviance: 993.48 – błąd modelu bez zmiennych (tylko stała).

  • Residual deviance: 920.71 – błąd modelu z BMI.

  • Spadek deviance (993.48 → 920.71) oznacza, że mass poprawia dopasowanie modelu – model z BMI lepiej przewiduje cukrzycę niż model pusty - „bez niczego”.

  • AIC: 924.71 – ogólny wskaźnik jakości dopasowania (im niżej, tym lepiej; sens ma przy porównywaniu kilku modeli).


Model pokazuje, że BMI ma silny, dodatni i istotny statystycznie związek z występowaniem cukrzycy w tych danych: im większa masa ciała (wyższe BMI), tym większe szanse, że osoba ma cukrzycę. Model z samym BMI już coś sensownego wyjaśnia, choć oczywiście nie opisuje całego zjawiska choroby.




Czym są szanse (odds) i iloraz szans (odds ratio)? (Przypomnienie)


  1. Co to są szanse (odds)?

Dla zdarzenia (np. „ma cukrzycę”) mamy:

  • \(p\) = prawdopodobieństwo zdarzenia
  • \(1 - p\) = prawdopodobieństwo braku zdarzenia

Szanse (odds) definiuje się jako:

\[ \text{odds} = \frac{p}{1-p} \]

Przykład:

  • \(p = 0.2\) (20% prawdopodobieństwa)
  • \(\text{odds} = 0.2 / 0.8 = 0.25\)

Interpretacja: „szanse 1 do 4” (1:4).


  1. Co to jest iloraz szans (odds ratio, OR)?

Iloraz szans porównuje szanse między dwiema grupami:

\[ \text{OR} = \frac{\text{odds w grupie A}}{\text{odds w grupie B}} \]

  • OR = 1 → brak różnicy między grupami
  • OR > 1 → w grupie A szanse zdarzenia są większe niż w B
  • OR < 1 → w grupie A szanse zdarzenia są mniejsze

Przykład z modelu:

  • mass ma OR ≈ 1.098
  • Interpretacja: jeśli BMI wzrośnie o 1 jednostkę, szanse cukrzycy rosną ok. 1.098 raza, czyli o ok. 9.8%.


Dlaczego stosuje się OR w regresji logistycznej?

W regresji logistycznej modelujemy logarytm szans (log-odds):

\[ \log\left(\frac{p}{1-p}\right) = \beta_0 + \beta_1 \cdot \text{mass} \]

  • \(\beta_1\) to zmiana log-szans przy wzroście BMI o 1.
  • \(\exp(\beta_1)\) daje iloraz szans (OR), który jest łatwiejszy do interpretacji:
    „przy wzroście BMI o 1 jednostkę, szanse cukrzycy rosną o X%”.

Czyli:

  • Model liczy w tle log-szanse, bo prosta zależność jest liniowa.
  • Funkcja exp(coef(model)) przekształca to w iloraz szans, który można sensownie interpretować dla praktyki klinicznej lub prezentacji wyników.



Jak wyciągnąć powyższe z modelu?

exp( # podnosi e do potęgi każdego współczynnika
  coef(model_bmi) # wyciąga współczynniki z modelu logistycznego
  )
## (Intercept)        mass 
##  0.02506174  1.09804408


  • mass = 1.098...

    • To jest iloraz szans (odds ratio) dla BMI.

    • Znaczy to, że wzrost BMI o 1 punkt zwiększa szanse wystąpienia cukrzycy o ok. 9.8% (bo 1.098 ≈ 1 + 0.098).

  • (Intercept) = 0.02506

    • To są szanse cukrzycy przy BMI = 0 (czysto technicznie wynik modelu, biologicznie bez sensu, bo BMI=0 nie istnieje).

    • Rzadko interpretuje się to dosłownie, ważniejszy jest współczynnik przy mass.



Przewidywanie

Jak praktycznie możemy wykorzystać modele regresji?

# Generujemy sekwencję bmi, min() i max() z danych, i "sensowny krok"
bmi_seq <- seq(
  from = min(PimaIndiansDiabetes$mass, na.rm = TRUE),
  to   = max(PimaIndiansDiabetes$mass, na.rm = TRUE),
  by   = 0.1
)

# robimy ramkę danych do predykcji używając wygenerowanej sekwencji
new_data <- data.frame(mass = bmi_seq)

# liczymy przewidywane prawdopodobieństwo
pred_prob <- predict(object = model_bmi, 
                     newdata = new_data, 
                     type = "response")

# rysujemy na osiach policzone przed chwilą elementy
plot(new_data$mass, 
     pred_prob,
     type = "l",              # l = line
     xlab = "BMI (mass)",
     ylab = "P(cukrzyca = 'pos')",
     main = "Prawdopodobieństwo cukrzycy w funkcji BMI")

plot(new_data$mass,
     pred_prob,
     type = "l",              # l = line
     xlab = "BMI (mass)",
     ylab = "P(cukrzyca = 'pos')",
     main = "Prawdopodobieństwo cukrzycy w funkcji BMI")

# dodanie danych z ramki
points(PimaIndiansDiabetes$mass,
       as.numeric(PimaIndiansDiabetes$diabetes == "pos"),
       pch = 16, cex = 0.5)



Wyjaśnienia pojęć


Zgony - proces zbierania informacji przez GUS


Krok 1 – Lekarz wystawia kartę zgonu Na karcie zgonu lekarz stwierdzający zgon ma obowiązek wpisać trzy przyczyny zgonu w formie opisu słownego: bezpośrednią (ostateczna przyczyna zgonu), wtórną (stan dający jej początek) oraz wyjściową (pierwotną) – chorobę lub okoliczności, które zapoczątkowały łańcuch zdarzeń prowadzących do śmierci. Lekarz wystawiający kartę zgonu nie jest uprawniony do kodowania przyczyn, ma obowiązek jedynie opisać stany chorobowe słownie.

Krok 2 – Dane trafiają do USC i dalej do GUS GUS otrzymuje dane o osobach zmarłych z dwóch źródeł: z urzędów stanu cywilnego – zarówno w postaci elektronicznej (zbiór danych przesyłany bezpośrednio na serwer GUS), jak i w postaci papierowej kserokopii z opisem słownym przyczyn zgonu, wysyłanej do Urzędu Statystycznego w Olsztynie. Dodatkowo, w celach kontrolnych, z Ministerstwa Cyfryzacji trafiają zbiorcze zestawienia aktów zgonów sporządzonych przez USC za każdy miesiąc.

Krok 3 – Kodowanie przez lekarzy-koderów To jest serce całego procesu i jego największa słabość. Kopie kart zgonu w formie papierowej wysyłane są ze wszystkich USC w Polsce do Urzędu Statystycznego w Olsztynie raz w miesiącu. Tam lekarz-koder – na podstawie opisów słownych, a w razie wątpliwości dokumentacji medycznej lub konsultacji z lekarzem orzekającym – rozstrzyga wyjściową przyczynę zgonu i nadaje jej kod ICD-10. Kod ten wprowadzany jest do zbioru statystycznego, a nie na samą kartę zgonu. Funkcję kodera wykonuje zaledwie 15 lekarzy, zaprzysiężonych co do zachowania tajemnicy statystycznej.

Krok 4 – Kontrola i walidacja Wskazane kody przyczyn zgonów poddawane są kilkupoziomowej kontroli: automatycznej walidacji w powiązaniu z płcią i wiekiem zmarłego; ręcznej weryfikacji przez naukowe instytuty medyczne (Instytut Onkologii, Instytut Matki i Dziecka, Instytut Kardiologii, NIZP-PZH, CSIOZ); oraz kontroli oprogramowaniem EDIT opracowanym przez Eurostat. Przypadki wyłonione w toku kontroli trafiają do ponownego sprawdzenia przez lekarzy-koderów.

Krok 5 – Publikacja danych Dane o liczbie zgonów ogółem (bez przyczyn) publikowane są co miesiąc w Biuletynie Statystycznym. Natomiast szczegółowe dane skorelowane z przyczynami zgonów dostępne są dopiero w styczniu roku następnego po roku następnym – czyli z ok. dwuletnim opóźnieniem.

Kluczowe ograniczenia systemu Cały proces kodowania jedynie wyjściowej (a nie wszystkich trzech) przyczyny zgonu wynika m.in. z braku elektronicznej karty zgonu w Polsce. Jej wprowadzenie umożliwiłoby automatyczne kodowanie i uwzględnienie wszystkich przyczyn, co wiązałoby się jednak z koniecznością zmian prawnych. Polska i tak przekazuje do Eurostatu i WHO wyłącznie wyjściową przyczynę zgonu, bo żadna z tych organizacji nie wymaga pełnych trzech przyczyn.
Krótko mówiąc – to system mocno zależny od ręcznej pracy kilkunastu lekarzy, papierowych dokumentów i dużego opóźnienia czasowego.


  1. ICD-10 Międzynarodowa Statystyczna Klasyfikacja Chorób i Problemów Zdrowotnych, 10. rewizja (ang. International Classification of Diseases) – opracowany przez WHO system kodowania chorób, zaburzeń, urazów i innych stanów zdrowotnych. Każda jednostka chorobowa ma przypisany unikalny alfanumeryczny kod (np. J45 = astma, I10 = nadciśnienie; Litera + cyfry). Służy do diagnozowania, sprawozdawczości, statystyk zdrowotnych i rozliczeń z NFZ. Czasem kod jest bardziej szczegółowy, np. J45 – astma (kategoria ogólna), J45.0 – astma przeważnie alergiczna, J45.1 – astma niealergiczna, J45.8 – astma mieszana. Część po kropce może określać m.in.: postać/typ choroby (np. alergiczna vs. niealergiczna), stopień nasilenia (łagodna, umiarkowana, ciężka), lokalizację (np. przy złamaniach – która kość, która strona), fazę (np. ostra vs. przewlekła) itd.
    Etiologia (czyli przyczyna choroby) jest w ICD-10 obsługiwana przez podwójne kodowanie, czyli użycie dwóch kodów jednocześnie: kod † (krzyżyk) – oznacza chorobę podstawową / przyczynę kod * (gwiazdka) – oznacza manifestację / powikłanie w konkretnym narządzie Na przykład cukrzycowa retinopatia = E14.3† + H36.0*
    ↩︎

  2. ICD-9 Poprzednia, 9. rewizja tej samej klasyfikacji – w Polsce stosowana głównie do kodowania procedur medycznych (zabiegów, operacji, badań diagnostycznych), nie chorób. W kontekście polskiej dokumentacji medycznej i NFZ funkcjonuje jako ICD-9 CM (Clinical Modification) i odpowiada na pytanie „co zrobiono pacjentowi?“, podczas gdy ICD-10 odpowiada na pytanie „co mu dolega?” (np. 88.71 – USG serca; Cyfry (+ podkody)).
    ↩︎

  3. Systemy informatyczne danej placówki medycznej to specjalistyczne rozwiązania IT, które wspomagają zarządzanie procesami medycznymi i administracyjnymi. HIS (Hospital Information System) to zintegrowany szpitalny system informacyjny, który obejmuje centralne moduły jak rejestracja pacjentów, zlecenia, elektroniczną dokumentację medyczną oraz moduły peryferyjne obsługujące laboratorium, radiologię czy farmację. Systemy gabinetowe wspierają pracę mniejszych jednostek medycznych, umożliwiając prowadzenie dokumentacji i zarządzanie pacjentami.
    RIS (Radiology Information System) to system informatyczny dedykowany oddziałom radiologii, który pozwala na zarządzanie zleceniami, optymalizację wykorzystania zasobów oraz tworzenie elektronicznej dokumentacji badań obrazowych. PACS (Picture Archiving and Communication System) to system do archiwizacji, przesyłania i udostępniania obrazów diagnostycznych, który integruje się z RIS i HIS, usprawniając wymianę informacji i przyspieszając diagnostykę medyczną.
    Te systemy współdziałają, by podnieść efektywność pracy placówki, zmniejszyć ryzyko błędów oraz poprawić jakość opieki nad pacjentem dzięki elektronicznej dokumentacji i szybkiej wymianie danych między urządzeniami diagnostycznymi a personelem medycznym. Za: https://ucyfrowienie.pl/systemy-pacsris/↩︎