1 Sinais vitais como indicadores de saúde

A compreensão dos sinais vitais inicia-se pela definição dos conceitos de saúde e doença. Segundo a Organização Mundial da Saúde (OMS), saúde é “um estado de completo bem-estar físico, mental e social, e não apenas a ausência de doença ou enfermidade” (OMS, 1946). Embora essa definição tenha representado um avanço conceitual importante, diversos autores destacam suas limitações práticas. Segre e Ferraz (1997), por exemplo, argumentam que a ideia de “completo bem-estar” é idealizada e dificilmente alcançável, tornando a definição pouco operacional para a prática clínica.

Independentemente das discussões conceituais, a medicina moderna baseia-se na premissa de que a saúde está associada à capacidade do organismo de manter a estabilidade de seu ambiente interno. Essa condição é denominada homeostase. A homeostase corresponde ao conjunto de mecanismos fisiológicos que regulam continuamente variáveis biológicas essenciais para a vida, como temperatura corporal, pressão arterial, frequência cardíaca, frequência respiratória, glicemia, equilíbrio hidroeletrolítico, pH sanguíneo e concentração de oxigênio nos tecidos.

Os organismos vivos são sistemas dinâmicos sujeitos a constantes perturbações internas e externas. Para garantir a sobrevivência, múltiplos mecanismos de controle atuam de forma integrada para manter essas variáveis dentro de limites fisiológicos compatíveis com o funcionamento adequado dos órgãos e sistemas. Quando esses mecanismos são eficazes, o organismo permanece em equilíbrio funcional; quando falham ou são sobrecarregados, surgem alterações fisiológicas que podem evoluir para estados patológicos.

Sob essa perspectiva, a doença pode ser entendida como uma disfunção dos mecanismos homeostáticos. Seja por agentes infecciosos, alterações genéticas, deficiências nutricionais, distúrbios metabólicos ou processos degenerativos, a doença manifesta-se frequentemente por alterações mensuráveis das funções vitais do organismo. Essas alterações podem produzir sintomas percebidos pelo paciente e sinais observáveis pelos profissionais de saúde.

Esses três indicadores representam dimensões complementares da homeostase:

Alterações na massa corporal podem influenciar tanto a frequência cardíaca quanto a pressão arterial média. O aumento da adiposidade, por exemplo, está frequentemente associado à ativação simpática, maior débito cardíaco e elevação da pressão arterial, enquanto a redução da massa corporal tende a diminuir a sobrecarga cardiovascular e melhorar a eficiência hemodinâmica. Dessa forma, a análise conjunta de FC, PAM e MCT fornece uma visão integrada do estado fisiológico e cardiovascular do indivíduo.

Os sinais vitais constituem, portanto, indicadores objetivos do estado funcional do organismo. Tradicionalmente incluem a pressão arterial, a frequência cardíaca, a frequência respiratória e a temperatura corporal. Atualmente, outros parâmetros são frequentemente incorporados à monitorização clínica, como a saturação periférica de oxigênio (SpO₂), a glicemia capilar e a pressão arterial média (PAM). Esses indicadores fornecem informações quantitativas sobre a capacidade do organismo de manter a homeostase e permitem identificar precocemente desvios fisiológicos associados a doenças agudas ou crônicas.

A monitorização sistemática dos sinais vitais desempenha papel central na avaliação clínica, no acompanhamento terapêutico e na prevenção de complicações. Em indivíduos saudáveis, esses parâmetros tendem a permanecer dentro de faixas fisiológicas relativamente estáveis. Em contrapartida, alterações persistentes ou abruptas podem indicar comprometimento dos mecanismos regulatórios do organismo, exigindo investigação diagnóstica e intervenção médica.

Dessa forma, o estudo dos sinais vitais representa uma aplicação prática do conceito de homeostase. A análise longitudinal dessas variáveis permite avaliar a dinâmica do equilíbrio fisiológico, identificar tendências, detectar mudanças estruturais ao longo do tempo e compreender como diferentes fatores biológicos, comportamentais e ambientais influenciam o estado de saúde dos indivíduos.

## =========================================================
## EXECUÇÃO: PrESsaO (sheet="PrESsaO")
## =========================================================

pkgs <- c(
  "readxl","dplyr","ggplot2","forecast","KernSmooth","GGally","car",
  "changepoint","strucchange","aTSA","trend","lmtest"
)
load_pkgs(pkgs)
safe_locale_ptbr()

df <- read_pressoes(params$xlsx_path, sheet = params$sheet_pressoes)
S  <- make_series_pressoes(df)

cat("Paciente: JOS | ", date_range_str(S$dates), "\n")
## Paciente: JOS |  Início: 2023-03-05  Fim: 2026-06-14
series4 <- cbind(S$SYS, S$DIA, S$FC, S$MCT)
colnames(series4) <- c("SYS","DIA","FC","MCT")

pairs_quick(series4)
##       SYS   DIA    FC   MCT
## SYS  1.00  0.74 -0.17  0.14
## DIA  0.74  1.00 -0.05  0.17
## FC  -0.17 -0.05  1.00 -0.09
## MCT  0.14  0.17 -0.09  1.00

scatter_matrix(as.data.frame(series4))

plot_ts_multi(series4)

plot_ts_single(series4[,1:2])

PAM <- pam_your_formula(SYS = S$SYS, DIA = S$DIA, FC = S$FC)

pairs_quick(cbind(PAM = PAM, MCT = S$MCT))
##      PAM  MCT
## PAM 1.00 0.16
## MCT 0.16 1.00

## PAM (diagnóstico + plot calendário)
run_full_diagnostics(PAM)
## Created Using changepoint version 2.3 
## Changepoint type      : Change in mean 
## Method of analysis    : PELT 
## Test Statistic  : Normal 
## Type of penalty       : MBIC with value, 21.15037 
## Minimum Segment Length : 1 
## Maximum no. of cpts   : Inf 
## Number of changepoints: 409 
## NULL

## Created Using changepoint version 2.3 
## Changepoint type      : Change in variance 
## Method of analysis    : PELT 
## Test Statistic  : Normal 
## Type of penalty       : MBIC with value, 21.15037 
## Minimum Segment Length : 2 
## Maximum no. of cpts   : Inf 
## Changepoint Locations :  
## NULL

## 
##   Optimal 5-segment partition: 
## 
## Call:
## breakpoints.formula(formula = y ~ 1)
## 
## Breakpoints at observation number:
## 195 384 556 953 
## 
## Corresponding to breakdates:
## 0.169124 0.3330442 0.4822203 0.8265395

## 
##  Approximate Cox-Stuart trend test
## 
## data:  y
## D+ = 316, p-value = 0.009815
## alternative hypothesis: data have a increasing trend
## 
## 
##  Cox and Stuart Trend test
## 
## data:  y
## z = 0.18703, n = 1153, p-value = 0.8516
## alternative hypothesis: monotonic trend
## 
## 
##  Mann-Kendall trend test
## 
## data:  y
## z = -0.96486, n = 1153, p-value = 0.3346
## alternative hypothesis: true S is not equal to 0
## sample estimates:
##             S          varS           tau 
## -1.260100e+04  1.705332e+08 -1.897528e-02 
## 
## 
##  Sen's slope
## 
## data:  y
## z = -0.96486, n = 1153, p-value = 0.3346
## alternative hypothesis: true z is not equal to 0
## 95 percent confidence interval:
##  -0.0015429128  0.0005302837
## sample estimates:
##   Sen's slope 
## -0.0004996896
plot_series_with_changes_calendar(
  dates = S$dates,
  y = PAM,
  main = paste0("Paciente: JOS | ", date_range_str(S$dates)),
  ylab = "PAM",
  hlines = c(70, 90, 100),
  by = "3 months"
)

## MCT
run_full_diagnostics(S$MCT)
## Created Using changepoint version 2.3 
## Changepoint type      : Change in mean 
## Method of analysis    : PELT 
## Test Statistic  : Normal 
## Type of penalty       : MBIC with value, 21.15037 
## Minimum Segment Length : 1 
## Maximum no. of cpts   : Inf 
## Changepoint Locations : 12 50 133 188 286 529 640 772 918 966 1014 
## NULL

## Created Using changepoint version 2.3 
## Changepoint type      : Change in variance 
## Method of analysis    : PELT 
## Test Statistic  : Normal 
## Type of penalty       : MBIC with value, 21.15037 
## Minimum Segment Length : 2 
## Maximum no. of cpts   : Inf 
## Changepoint Locations : 133 200 289 673 
## NULL

## 
##   Optimal 5-segment partition: 
## 
## Call:
## breakpoints.formula(formula = y ~ 1)
## 
## Breakpoints at observation number:
## 172 344 710 918 
## 
## Corresponding to breakdates:
## 0.1491761 0.2983521 0.6157849 0.7961839

## 
##  Approximate Cox-Stuart trend test
## 
## data:  y
## D+ = 303, p-value = 0.02872
## alternative hypothesis: data have a increasing trend
## 
## 
##  Cox and Stuart Trend test
## 
## data:  y
## z = 10.899, n = 1153, p-value < 2.2e-16
## alternative hypothesis: monotonic trend
## 
## 
##  Mann-Kendall trend test
## 
## data:  y
## z = -13.482, n = 1153, p-value < 2.2e-16
## alternative hypothesis: true S is not equal to 0
## sample estimates:
##             S          varS           tau 
## -1.760020e+05  1.704220e+08 -2.678129e-01 
## 
## 
##  Sen's slope
## 
## data:  y
## z = -13.482, n = 1153, p-value < 2.2e-16
## alternative hypothesis: true z is not equal to 0
## 95 percent confidence interval:
##  -0.002913907 -0.002028986
## sample estimates:
##  Sen's slope 
## -0.002459016
plot_series_with_changes_calendar(
  dates = S$dates,
  y = S$MCT,
  main = paste0("Paciente: JOS | ", date_range_str(S$dates)),
  ylab = "MCT",
  hlines = c(85),
  by = "3 months"
)

## FC
run_full_diagnostics(S$FC)
## Created Using changepoint version 2.3 
## Changepoint type      : Change in mean 
## Method of analysis    : PELT 
## Test Statistic  : Normal 
## Type of penalty       : MBIC with value, 21.15037 
## Minimum Segment Length : 1 
## Maximum no. of cpts   : Inf 
## Number of changepoints: 524 
## NULL

## Created Using changepoint version 2.3 
## Changepoint type      : Change in variance 
## Method of analysis    : PELT 
## Test Statistic  : Normal 
## Type of penalty       : MBIC with value, 21.15037 
## Minimum Segment Length : 2 
## Maximum no. of cpts   : Inf 
## Changepoint Locations : 507 600 956 999 
## NULL

## 
##   Optimal 3-segment partition: 
## 
## Call:
## breakpoints.formula(formula = y ~ 1)
## 
## Breakpoints at observation number:
## 173 598 
## 
## Corresponding to breakdates:
## 0.1500434 0.518647

## $index
## [1]  596 1037
## 
## $values
## [1] 118 107
## 
## $raw
## $raw$index
## [1]  596 1037
## 
## $raw$replacements
## [1] 85.5 83.0
## 
## 
## 
##  Approximate Cox-Stuart trend test
## 
## data:  y
## D+ = 364, p-value = 1.041e-13
## alternative hypothesis: data have a increasing trend
## 
## 
##  Cox and Stuart Trend test
## 
## data:  y
## z = 4.9819, n = 1153, p-value = 6.297e-07
## alternative hypothesis: monotonic trend
## 
## 
##  Mann-Kendall trend test
## 
## data:  y
## z = -7.4004, n = 1153, p-value = 1.357e-13
## alternative hypothesis: true S is not equal to 0
## sample estimates:
##             S          varS           tau 
## -9.654600e+04  1.701941e+08 -1.483238e-01 
## 
## 
##  Sen's slope
## 
## data:  y
## z = -7.4004, n = 1153, p-value = 1.357e-13
## alternative hypothesis: true z is not equal to 0
## 95 percent confidence interval:
##  -0.005836576 -0.003325942
## sample estimates:
##  Sen's slope 
## -0.004580153
plot_series_with_changes_calendar(
  dates = S$dates,
  y = S$FC,
  main = paste0("Paciente: JOS | ", date_range_str(S$dates)),
  ylab = "FC",
  hlines = c(60, 80, 100),
  by = "3 months"
)

## ARIMAX (exemplo)
cat("Normalidade (Shapiro):\n")
## Normalidade (Shapiro):
print(shapiro.test(as.numeric(PAM)))
## 
##  Shapiro-Wilk normality test
## 
## data:  as.numeric(PAM)
## W = 0.99767, p-value = 0.1009
print(shapiro.test(as.numeric(S$MCT)))
## 
##  Shapiro-Wilk normality test
## 
## data:  as.numeric(S$MCT)
## W = 0.80457, p-value < 2.2e-16
res_arimax <- fit_arimax(
  y = as.numeric(PAM),
  xreg = cbind(MCT = as.numeric(S$MCT)),
  lambda = 0
)
## 
##  Fitting models using approximations to speed things up...
## 
##  Regression with ARIMA(2,0,2) errors : Inf
##  Regression with ARIMA(0,0,0) errors : -3007.646
##  Regression with ARIMA(1,0,0) errors : -3084.085
##  Regression with ARIMA(0,0,1) errors : -3064.123
##  Regression with ARIMA(0,0,0) errors : -1399.47
##  Regression with ARIMA(2,0,0) errors : -3114.524
##  Regression with ARIMA(3,0,0) errors : -3117.966
##  Regression with ARIMA(4,0,0) errors : -3128.164
##  Regression with ARIMA(5,0,0) errors : -3144.104
##  Regression with ARIMA(5,0,1) errors : -3153.823
##  Regression with ARIMA(4,0,1) errors : -3154.566
##  Regression with ARIMA(3,0,1) errors : -3152.357
##  Regression with ARIMA(4,0,2) errors : Inf
##  Regression with ARIMA(3,0,2) errors : -3151.294
##  Regression with ARIMA(5,0,2) errors : -3151.822
##  Regression with ARIMA(4,0,1) errors : -2961.357
## 
##  Now re-fitting the best model(s) without approximations...
## 
##  Regression with ARIMA(4,0,1) errors : -3157.552
## 
##  Best model: Regression with ARIMA(4,0,1) errors
print(res_arimax$summary)
## Series: y 
## Regression with ARIMA(4,0,1) errors 
## Box Cox transformation: lambda= 0 
## 
## Coefficients:
##          ar1      ar2      ar3     ar4      ma1  intercept     MCT
##       0.8993  -0.0102  -0.0597  0.0780  -0.7330     4.0030  0.0054
## s.e.  0.0730   0.0414   0.0405  0.0346   0.0682     0.1553  0.0018
## 
## sigma^2 = 0.003755:  log likelihood = 1586.84
## AIC=-3157.68   AICc=-3157.55   BIC=-3117.28
## 
## Training set error measures:
##                     ME     RMSE      MAE        MPE     MAPE      MASE
## Training set 0.1577777 5.293355 4.169381 -0.1910768 4.820878 0.7664638
##                     ACF1
## Training set -0.00689067
print(res_arimax$coeftest)
## 
## z test of coefficients:
## 
##             Estimate Std. Error  z value  Pr(>|z|)    
## ar1        0.8993123  0.0729949  12.3202 < 2.2e-16 ***
## ar2       -0.0101882  0.0413821  -0.2462  0.805528    
## ar3       -0.0596912  0.0404691  -1.4750  0.140217    
## ar4        0.0779701  0.0345530   2.2565  0.024037 *  
## ma1       -0.7329597  0.0682470 -10.7398 < 2.2e-16 ***
## intercept  4.0029575  0.1552597  25.7823 < 2.2e-16 ***
## MCT        0.0053669  0.0018138   2.9590  0.003087 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
print(res_arimax$box_p)
## [1] 0.9307619
## =========================================================
## EXECUÇÃO: MAPA (sheet="MAPA") com eixo hora (15 em 15 min)
## =========================================================

load_pkgs(c("readxl","dplyr","GGally","car","forecast","KernSmooth"))

df_mapa <- readxl::read_excel(params$xlsx_path, sheet = params$sheet_mapa)
stop_if_missing(df_mapa, c("HORA","PAS","PAD","PAM","MAPA","PAM13","FC"))

df_mapa <- df_mapa |>
  dplyr::mutate(
    HORA  = as.POSIXct(HORA, tz = "America/Sao_Paulo"),
    PAS   = as.numeric(PAS),
    PAD   = as.numeric(PAD),
    PAM   = as.numeric(PAM),
    MAPA  = as.numeric(MAPA),
    PAM13 = as.numeric(PAM13),
    FC    = as.numeric(FC)
  ) |>
  dplyr::filter(is.finite(HORA)) |>
  dplyr::arrange(HORA) |>
  dplyr::group_by(HORA) |>
  dplyr::summarise(
    PAS   = mean(PAS,   na.rm = TRUE),
    PAD   = mean(PAD,   na.rm = TRUE),
    PAM   = mean(PAM,   na.rm = TRUE),
    MAPA  = mean(MAPA,  na.rm = TRUE),
    PAM13 = mean(PAM13, na.rm = TRUE),
    FC    = mean(FC,    na.rm = TRUE),
    .groups = "drop"
  )

## índice 15 em 15 min (eixo que você quer)
t15 <- seq_len(nrow(df_mapa))

## ---- exploratório: correlação + pares
X1 <- df_mapa[, c("PAS","PAD","FC")]
X2 <- df_mapa[, c("PAM","MAPA","PAM13")]

print(round(cor(X1, use = "pairwise.complete.obs"), 2))
##       PAS  PAD    FC
## PAS  1.00 0.68 -0.02
## PAD  0.68 1.00  0.10
## FC  -0.02 0.10  1.00
print(round(cor(X2, use = "pairwise.complete.obs"), 2))
##        PAM MAPA PAM13
## PAM   1.00 0.99  0.99
## MAPA  0.99 1.00  0.99
## PAM13 0.99 0.99  1.00
print(GGally::ggpairs(X1, title = "", diag = list(continuous = "densityDiag")))

print(GGally::ggpairs(X2, title = "", diag = list(continuous = "densityDiag")))

car::scatterplotMatrix(as.data.frame(df_mapa[, c("PAS","PAD","FC","PAM","MAPA","PAM13")]),
                       smooth = FALSE,
                       regLine = TRUE,
                       lower.panel = NULL,
                       ellipse = list(levels = c(0.68), robust = TRUE, fill = FALSE),
                       col = "gray45",
                       cex = 0.2,
                       pch = 1,
                       main = "")

## ---- utilitários de plot robusto em eixo t15
break_on_gap_time <- function(dates, y, gap_mins = 60) {
  o <- order(dates)
  dates <- dates[o]
  y <- as.numeric(y)[o]
  
  dx <- c(0, diff(as.numeric(dates)))     # segundos
  y_line <- y
  y_line[dx > gap_mins * 60] <- NA_real_  # quebra linhas em gaps grandes
  
  list(dates = dates, y = y, y_line = y_line, o = o)
}

locpoly_smooth_x <- function(x, y) {
  x <- as.numeric(x)
  y <- as.numeric(y)
  
  ok <- is.finite(x) & is.finite(y)
  x <- x[ok]; y <- y[ok]
  if (length(y) < 10) return(rep(NA_real_, length(ok)))
  
  o <- order(x)
  x <- x[o]; y <- y[o]
  n <- length(y)
  
  bw <- suppressWarnings(KernSmooth::dpill(x, y, gridsize = n))
  if (!is.finite(bw) || bw <= 0) {
    rng <- diff(range(x))
    bw <- if (is.finite(rng) && rng > 0) rng/20 else 1
  }
  
  lp <- KernSmooth::locpoly(x, y, bandwidth = bw, gridsize = n)
  stats::approx(lp$x, lp$y, xout = x, rule = 2)$y
}

plot_15min <- function(dates, y, main, ylab,
                       hlines = NULL,
                       vlines = NULL,
                       gap_mins = 60,
                       show_smooth = TRUE) {
  
  b <- break_on_gap_time(dates, y, gap_mins = gap_mins)
  
  ## x = índice 15-min após ordenação
  x <- seq_along(b$dates)
  
  plot(x, b$y, type = "p", cex = 0.5,
       main = main, xlab = "Hora (15 em 15 min)", ylab = ylab,
       col = "black")
  lines(x, b$y_line, col = "gray")
  
  if (!is.null(hlines)) for (h in hlines) abline(h = h, lty = 2)
  if (!is.null(vlines)) for (v in vlines) abline(v = v, lty = 3)
  
  if (show_smooth) {
    sm <- locpoly_smooth_x(x, b$y)
    if (any(is.finite(sm))) lines(x, sm, lwd = 1.5)
  }
  
  invisible(TRUE)
}

## ---- gráficos pedidos: PAM e FC em 15/15
plot_15min(df_mapa$HORA, df_mapa$PAM,
           main = "MAPA do Paciente: JOS\nData: 20/01/2023",
           ylab = "PAM",
           hlines = c(70, 90, 100),
           vlines = c(24, 36, 68),
           gap_mins = 60)

plot_15min(df_mapa$HORA, df_mapa$FC,
           main = "MAPA do Paciente: JOS\nData: 20/01/2023",
           ylab = "FC",
           hlines = c(60, 100),
           vlines = c(24, 36, 68),
           gap_mins = 60)

## ---- comparação no mesmo painel: PAM vs MAPA vs PAM13
o <- order(df_mapa$HORA)
x <- seq_along(o)

plot(x, df_mapa$PAM[o], type = "p", cex = 0.5,
     main = "MAPA do Paciente: JOS\nComparação: PAM vs MAPA vs PAM13",
     xlab = "Hora (15 em 15 min)", ylab = "mmHg", col = "black")
lines(x, df_mapa$PAM[o], col = "gray")

points(x, df_mapa$MAPA[o], pch = 1, cex = 0.5, col = "black")
lines(x, df_mapa$MAPA[o], col = "gray")

points(x, df_mapa$PAM13[o], pch = 2, cex = 0.5, col = "black")
lines(x, df_mapa$PAM13[o], col = "gray")

abline(h = c(70, 90, 100), lty = 2)
legend("topright",
       legend = c("PAM","MAPA","PAM13"),
       pch = c(1,1,2),
       bty = "n")