Setup + import
Pokaż kod
pacman:: p_load (
rio,
here,
tidyverse, #zawiera dplyr, forcats, ggplot2, readr, tibble, purr
lubridate,
openxlsx,
skimr, # get overview of data
gtsummary, # summary statistics and tests
rstatix, # summary statistics and statistical tests
janitor, # adding totals and percents to tables
scales, # easily convert proportions to percents
flextable, # converting tables to pretty images
hrbrthemes,
viridis,
gt,
DT
)
# here("NEWSTAT.Rproj")
data_cleaned <- readRDS (here:: here ("data" , "data_cleaned.rds" ))
data_alerty <- data_cleaned %>%
filter (outcome != "HOT NEWS reocena" )
Słownik, definicje i założenia
Interwencje - wszystkie wpisy, w tym reoceny HOT NEWS (przedłużenie/ zakończenie nadzoru).
Alerty - wszystkie interwencje bez uwzględnienia reocen HOT NEWS, czyli interwencje zakończone:
Wzmożony nadzór HOT NEWS zakończyć się może:
Wskaźniki jakości opieki ZWR - pozytywne :
uniknięcie przyjęcia do OIT = HOT NEWS zakończony poprawą (bez przyjęcia do OIT i NZK w ciągu 24h),
zapobieganie nieplanowemu przyjęciu do OIT :
alert zakończony przyjęciem do OIT,
HOT NEWS zakończony przyjęciem do OIT,
ograniczenie terapii daremnej,
Wskaźniki jakości opieki ZWR - negatywne :
NZK w ciągu 24 h od alertu (nie dotyczy alertów zakończonych przyjęciem do OIT).
Okres
04.08.2025 - 30.09.2025
343 interwencje, w tym 134 w sierpniu i 209 we wrześniu.
13 oddziałów.
lista_cleaned <- lapply(data_cleaned, identity)
Tabela
Pokaż kod
library (DT)
library (dplyr)
library (lubridate)
df_tbl <- data_cleaned %>%
select (- c (patient, score_cat, brak_lekarza, hotnews_details)) %>%
mutate (
time = format (ymd_hms (time), "%d.%m.%Y %H:%M" ) # 05.08.2025 11:55
)
Warning: There was 1 warning in `mutate()`.
ℹ In argument: `time = format(ymd_hms(time), "%d.%m.%Y %H:%M")`.
Caused by warning:
! 4 failed to parse.
Pokaż kod
# indeksy kolumn (0-based)
u_idx <- match ("uwagi" , names (df_tbl)) - 1
t_idx <- match ("time" , names (df_tbl)) - 1
datatable (
df_tbl,
rownames = FALSE ,
escape = which (names (df_tbl) != "uwagi" ),
options = list (
pageLength = 5 ,
autoWidth = TRUE ,
columnDefs = list (
list (width = "160px" , targets = t_idx), # szersza kolumna 'time'
list (width = "300px" , targets = u_idx), # szersza kolumna 'uwagi'
list (
targets = u_idx,
render = JS ("
function(data, type, row, meta) {
if (data == null) return data;
function escHTML(x){return x.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');}
function escAttr(x){return x.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/ \" /g,'"');}
if (type !== 'display') return data;
var full = String(data);
var short = full.length > 50 ? full.substr(0, 50) + '…' : full;
return '<span title= \" ' + escAttr(full) + ' \" >' + escHTML(short) + '</span>';
}"
)
)
)
)
) %>%
formatStyle (
columns = names (df_tbl),
fontSize = "12px"
)
Liczba interwencji dziennie
Pokaż kod
# 1) Tabela z liczbą alertów dziennie
interwencje_daily <- data_cleaned %>%
mutate (data = as.Date (time)) %>%
count (data, name = "interwencje" ) %>%
arrange (data)
# 2) Wykres
ggplot (interwencje_daily, aes (x = data, y = interwencje)) +
geom_col (
fill = "#69b3a2" ,
color = "#e9ecef" ,
alpha = 0.9 ,
width = 0.9
) +
ggtitle ("Liczba interwencji dziennie" ) +
scale_y_continuous (
breaks = seq (0 , max (interwencje_daily$ interwencje, na.rm = TRUE ), by = 2 ) # co 2
) +
scale_x_date (
date_labels = "%d.%m" ,
date_breaks = "3 day"
) +
theme_ipsum () +
theme (
plot.title = element_text (size = 15 ),
axis.text.x = element_text (angle = 45 , hjust = 1 ),
panel.grid.major.x = element_blank (),
panel.grid.minor = element_blank (),
panel.grid.major.y = element_line (color = "gray80" , linewidth = 0.3 ) # poprawione
)
Pokaż kod
hist_data <- interwencje_daily %>%
count (interwencje, name = "dni" ) %>%
complete (interwencje = 0 : max (interwencje, na.rm = TRUE ),
fill = list (dni = 0 )) %>%
arrange (interwencje)
max_interwencje <- max (hist_data$ interwencje, na.rm = TRUE )
max_count <- max (hist_data$ dni, na.rm = TRUE )
ggplot (hist_data, aes (x = interwencje, y = dni)) +
geom_col (fill = "#69b3a2" , color = "#e9ecef" , alpha = 0.9 , width = 0.9 ) +
ggtitle ("Rozkład liczby interwencji dziennie" ) +
scale_x_continuous (breaks = 0 : max_interwencje,
expand = expansion (mult = c (0.01 , 0.01 ))) +
scale_y_continuous (breaks = 0 : max_count,
expand = expansion (mult = c (0 , 0.05 ))) +
theme_ipsum () +
labs (x = "Interwencje" , y = "Liczba dni" ) +
theme (plot.title = element_text (size = 15 ),
panel.grid.major.x = element_blank (),
panel.grid.minor = element_blank (),
panel.grid.major.y = element_line (color = "gray80" , linewidth = 0.3 ))
Alerty
Ilość i rodzaj alertów NEWS wykonanych przez SWIT.
Pokaż kod
# Przygotowanie danych
outcome_counts <- data_alerty %>%
count (outcome) %>%
mutate (
pct = n / sum (n),
label = paste0 (n, " (" , percent (pct, accuracy = 0.1 ), ")" ),
outcome = fct_relevel (outcome, "HOT NEWS" , "SOFT NEWS" , "DNR" , "Błąd pomiaru" , "Przyjęcie do OIT" )
)
# Wykres
ggplot (outcome_counts, aes (x = outcome, y = n, fill = outcome)) +
geom_col () +
geom_text (aes (label = label), vjust = - 0.5 , size = 4 ) +
scale_fill_hue (c = 40 ) +
theme_minimal () +
theme (legend.position = "none" ) +
labs (
title = "Liczba alertów według wyniku" ,
x = NULL ,
y = "Liczba alertów"
)
Pokaż kod
data_alerty %>%
count (outcome) %>%
mutate (
outcome = fct_relevel (outcome, "HOT NEWS" , "SOFT NEWS" , "Przyjęcie do OIT" , "DNR" , "Błąd pomiaru" ),
pct = scales:: percent (n / sum (n), accuracy = 0.1 )) %>%
arrange (outcome) %>% # posortuj zgodnie z kolejnością faktora
bind_rows (
summarise (.,
outcome = "Razem" ,
n = sum (n),
pct = scales:: percent (1 , accuracy = 0.1 )
)
) %>%
gt:: gt () %>%
gt:: cols_label (
outcome = "Alert" ,
n = "Ilość" ,
pct = "Odsetek"
) %>%
gt:: tab_style (
style = gt:: cell_text (weight = "bold" ), # pogrubienie
locations = gt:: cells_column_labels ( # wybierz etykiety kolumn
columns = everything () # wszystkie kolumny
)
)
HOT NEWS
81
16.8%
SOFT NEWS
288
59.6%
Przyjęcie do OIT
34
7.0%
DNR
56
11.6%
Błąd pomiaru
24
5.0%
Razem
483
100.0%
Oddziały
Ilość alertów w podziale na oddziały (bez uwzględnienia reocen HOT NEWS).
Pokaż kod
data_alerty %>%
count (oddzial) %>%
arrange (n) %>%
mutate (oddzial = factor (oddzial, levels = oddzial)) %>%
ggplot (aes (x = oddzial, y = n)) +
geom_segment (aes (xend = oddzial, y = 0 , yend = n), color = "gray70" ) +
geom_point (size = 4 , color = "orange" ) +
coord_flip () +
theme_bw () +
labs (
title = "Liczba alertów SWIT wg oddziału" ,
x = NULL ,
y = "Liczba alertów"
)
Pokaż kod
data_alerty %>%
count (oddzial) %>%
arrange (desc (n)) %>% # opcjonalnie sortowanie
bind_rows (
summarise (.,
oddzial = "Razem" ,
n = sum (n)
)
) %>%
gt () %>%
gt:: cols_label (
oddzial = "Oddział" ,
n = "Ilość alertów"
) %>%
gt:: tab_style (
style = gt:: cell_text (weight = "bold" ), # pogrubienie
locations = gt:: cells_column_labels ( # wszystkie nagłówki
columns = everything ()
)
)
Chirurgia Czerwona
123
Chirurgia Biała
43
Interna I0
43
Ortopedia
39
Urologia
33
Diabetologia
30
Nefrologia
28
Neurochirurgia
26
Pulmonologia
25
Geriatria I5
19
Endokrynologia
18
Kardiologia 1 G0
15
Neurologia Udary
10
Kardiologia 2 G1
8
OK Neurologii
6
OK Przeszczepu Szpiku Kostnego
4
OK Chorób Zakaźnych
3
Reumatologia
2
Stacja Dializ
2
OK Angiologii
1
OK Chirurgii Naczyń
1
OK Gastroenterologii i Hepatologii
1
OK Hematologii
1
Sala Nadzoru Pooperacyjnego
1
Toksykologia
1
Razem
483
Ratownik
Ilość wpisanych interwencji w zależności od ratownika.
Pokaż kod
data_cleaned %>%
count (ratownik) %>%
arrange (ratownik) %>%
mutate (ratownik = forcats:: fct_inorder (ratownik) |> forcats:: fct_rev ()) %>%
ggplot (aes (x = n, y = ratownik)) +
geom_segment (aes (xend = 0 , yend = ratownik), color = "gray70" ) +
geom_point (size = 4 , color = "darkred" ) +
theme_bw () +
labs (
title = "Ilość wpisanych interwencji" ,
x = NULL ,
y = "Ratownik"
)
Pokaż kod
data_cleaned %>%
count (ratownik) %>%
gt:: gt () %>%
gt:: cols_label (
ratownik = "Ratownik" ,
n = "Ilość interwencji"
) %>%
gt:: tab_style (
style = gt:: cell_text (weight = "bold" ), # pogrubienie
locations = gt:: cells_column_labels ( # wybierz etykiety kolumn
columns = everything () # wszystkie kolumny
)
)
Andrzej
46
Grzesiek
38
Hubert
69
Jurek
42
Justyna
71
Kuba
48
Maciek
90
Marcin
77
Marek
26
Mateusz
34
Michał
4
Piotrek
94
Sebastian
80
Tomek
19
HOT NEWS
Efekty działań po kwalifikacji chorego do HOT NEWS.
Pokaż kod
hotnews_efekt <- data_cleaned %>%
filter (hotnews_details %in% c ("Poprawa" , "OIT" , "DNR" )) %>%
count (hotnews_details) %>%
mutate (
pct = n/ sum (n),
label = paste0 (n, " (" , percent (pct, accuracy = 0.1 ), ")" ),
hotnews_details = fct_relevel (hotnews_details, "Poprawa" , "OIT" , "DNR" )
)
# Wykres
ggplot (hotnews_efekt, aes (x = hotnews_details, y = n, fill = hotnews_details)) +
geom_col () +
geom_text (aes (label = label), vjust = - 0.5 , size = 4 ) +
scale_fill_hue (c = 40 ) +
theme_minimal () +
theme (legend.position = "none" ) +
labs (
title = "Efekty działań u chorych HOT NEWS" ,
x = NULL ,
y = "Liczba"
)
Pokaż kod
data_cleaned %>%
filter (hotnews_details %in% c ("Poprawa" , "OIT" , "DNR" )) %>%
count (hotnews_details) %>%
gt:: gt () %>%
gt:: cols_label (
hotnews_details = "Efekt końcowy" ,
n = "Ilość"
) %>%
gt:: tab_style (
style = gt:: cell_text (weight = "bold" ), # pogrubienie
locations = gt:: cells_column_labels ( # wybierz etykiety kolumn
columns = everything () # wszystkie kolumny
)
) %>%
gt:: tab_style (
style = gt:: cell_text (weight = "bold" ), # pogrubienie
locations = gt:: cells_column_labels ( # wybierz etykiety kolumn
columns = everything () # wszystkie kolumny
)
)
Wynik NEWS
Boxplot przedstawia medianę (pogrubiona linia), kwartyle Q1 i Q3 (granice boxplot) oraz minimalne i maksymalne wartości w próbie (wąsy). Nałożone punkty pokazują indywidualne wyniki pacjentów.
Pokaż kod
# 1) Przygotuj dane: tylko HOT/ SOFT, bez braków score, ustaw kolejność słupków
df_box <- data_cleaned %>%
filter (outcome %in% c ("HOT NEWS" , "SOFT NEWS" ),
! is.na (score)) %>%
mutate (outcome = fct_relevel (outcome, "HOT NEWS" , "SOFT NEWS" ))
df_box %>%
ggplot (aes (x = outcome, y = score, fill = outcome)) +
geom_boxplot (width = 0.6 , outlier.shape = NA ) +
geom_jitter (color = "black" , size = 0.6 , alpha = 0.9 , width = 0.15 ) +
geom_hline (yintercept = 7 , color = "red" , alpha = 0.4 , linewidth = 0.6 ) + # linia na 7
scale_fill_viridis (discrete = TRUE , alpha = 0.6 ) +
scale_y_continuous (
breaks = c (seq (0 , max (df_box$ score, na.rm = TRUE ), by = 2 ), 7 ) |> sort () # <--- dodane 7
) +
theme_ipsum () +
theme (
legend.position = "none" ,
plot.title = element_text (size = 11 )
) +
ggtitle ("Rozkład punktacji HOT NEWS vs SOFT NEWS" ) +
xlab ("" ) +
ylab ("Score" )
Pokaż kod
# policz statystyki opisowe dla HOT NEWS i SOFT NEWS
summary_stats <- data_cleaned %>%
filter (outcome %in% c ("HOT NEWS" , "SOFT NEWS" )) %>%
group_by (outcome) %>%
summarise (
n = n (),
Min = min (score, na.rm = TRUE ),
Q1 = quantile (score, 0.25 , na.rm = TRUE ),
Median = median (score, na.rm = TRUE ),
Mean = mean (score, na.rm = TRUE ),
Q3 = quantile (score, 0.75 , na.rm = TRUE ),
Max = max (score, na.rm = TRUE ),
SD = sd (score, na.rm = TRUE ),
.groups = "drop"
)
# tabela w gt
summary_stats %>%
gt () %>%
cols_label (
outcome = "Alert" ,
n = "N" ,
Min = "Min" ,
Q1 = "Q1" ,
Median = "Median" ,
Mean = "Mean" ,
Q3 = "Q3" ,
Max = "Max" ,
SD = "SD"
) %>%
fmt_number (
columns = c (Min, Q1, Median, Mean, Q3, Max, SD), # tylko numeryczne
decimals = 1
) %>%
tab_style (
style = cell_text (weight = "bold" ),
locations = cells_column_labels (columns = everything ())
)
SOFT NEWS
288
0.0
3.0
5.0
4.9
7.0
13.0
2.5
HOT NEWS
81
0.0
5.0
6.0
6.4
8.0
13.0
2.6
Sposób zgłoszenia alertu
Pokaż kod
zgloszenie_cat <- data_cleaned %>%
filter (zgloszenie %in% c ("Obchód" , "Telefoniczny" , "Lekarz SWIT" )) %>%
count (zgloszenie) %>%
mutate (
pct = n/ sum (n),
label = paste0 (n, " (" , percent (pct, accuracy = 0.1 ), ")" )
)
# Wykres
ggplot (zgloszenie_cat, aes (x = zgloszenie, y = n, fill = zgloszenie)) +
geom_col (width = 0.4 ) + # <-- domyślnie 0.9, mniejsza wartość = węższe słupki
geom_text (aes (label = label), vjust = - 0.5 , size = 4 ) +
scale_fill_hue (c = 40 ) +
theme_minimal () +
theme (legend.position = "none" ) +
labs (
title = "Sposób zgłoszenia" ,
x = NULL ,
y = "Liczba"
)
Pokaż kod
data_cleaned %>%
filter (zgloszenie %in% c ("Obchód" , "Telefoniczny" , "Lekarz SWIT" )) %>%
count (zgloszenie) %>%
mutate (
pct = scales:: percent (n/ sum (n), accuracy = 0.1 )) %>%
gt:: gt () %>%
gt:: cols_label (
zgloszenie = "Sposób zgłoszenia" ,
n = "Ilość" ,
pct = "Odsetek"
) %>%
gt:: tab_style (
style = gt:: cell_text (weight = "bold" ), # pogrubienie
locations = gt:: cells_column_labels ( # wybierz etykiety kolumn
columns = everything () # wszystkie kolumny
)
)
Lekarz SWIT
32
6.6%
Obchód
285
59.0%
Telefoniczny
166
34.4%