As medidas de referência foram obtidas utilizando dispositivos eletrônicos comerciais destinados ao monitoramento domiciliar de parâmetros fisiológicos, metabólicos e antropométricos. A pressão arterial sistólica (PAS), pressão arterial diastólica (PAD) e frequência cardíaca (FC) foram medidas por meio de um monitor automático de braço Omron HEM-7346T (Omron Healthcare Co., Kyoto, Japão), equipado com tecnologia Bluetooth para armazenamento e transferência dos dados.
A massa corporal total (MCT) e a composição corporal foram obtidas utilizando uma balança de bioimpedância Omron HBF-222T (Omron Healthcare Co., Kyoto, Japão). A temperatura corporal foi medida com um termômetro digital G-Tech TH-186 (Accumed-Glicomed, Duque de Caxias, RJ, Brasil). A saturação periférica de oxigênio (SpO₂) e a frequência cardíaca também foram avaliadas por um oxímetro de pulso de dedo Multilaser Saúde HC261 (Multilaser Industrial S.A., Extrema, MG, Brasil).
A glicose intersticial foi monitorada continuamente por meio do sistema FreeStyle Libre (Abbott Diabetes Care Inc., Alameda, CA, EUA), um sensor de monitorização contínua da glicose (CGM) aplicado na região posterior do braço, capaz de registrar automaticamente as concentrações de glicose ao longo do dia sem necessidade de punções digitais rotineiras.
As medidas obtidas pelos dispositivos de referência foram comparadas às fornecidas por dispositivos vestíveis da Samsung Electronics Co. (Suwon, Coreia do Sul), incluindo smartwatch Galaxy Watch e pulseira Galaxy Fit. As variáveis avaliadas compreenderam frequência cardíaca, saturação periférica de oxigênio, massa corporal total, percentual de gordura corporal e demais parâmetros fisiológicos disponibilizados pelos dispositivos.
Todos os equipamentos foram utilizados de acordo com as instruções dos fabricantes. As medições foram realizadas em ambiente domiciliar durante o período de acompanhamento longitudinal do participante. Os dispositivos Omron, G-Tech, Multilaser e FreeStyle Libre foram considerados métodos de referência para fins de comparação e avaliação de equivalência metrológica dos dispositivos vestíveis.
A equivalência entre os métodos foi investigada por meio da abordagem estrutural implementada no pacote eirasAgree, baseada na avaliação sequencial de viés estrutural, precisão estrutural e concordância estrutural.
A Monitorização Ambulatorial da Pressão Arterial (MAPA) consiste no registro automático e repetido da pressão arterial ao longo de 24 horas durante as atividades habituais do indivíduo. Diferentemente da medida isolada obtida em consultório, a MAPA permite avaliar a variabilidade circadiana da pressão arterial, identificar padrões de vigília e sono e reduzir a influência do efeito do avental branco.
Em geral, o equipamento realiza medições em intervalos regulares, tipicamente a cada 15 ou 20 minutos durante o período diurno e a cada 20 a 30 minutos durante o período noturno. A partir dessas medidas podem ser calculadas estatísticas descritivas, médias diurnas e noturnas, cargas pressóricas e indicadores derivados da pressão arterial.
A pressão arterial média (PAM) é uma medida frequentemente utilizada para representar a perfusão média dos tecidos ao longo do ciclo cardíaco. Em condições fisiológicas, pode ser aproximada por
\[ PAM \approx PAD + \frac{PAS-PAD}{3} \]
em que:
Neste estudo foram comparadas três medidas relacionadas à pressão arterial média:
A simples correlação entre essas medidas não é suficiente para
afirmar equivalência. Duas técnicas podem apresentar elevada correlação
e, ainda assim, diferirem sistematicamente em viés, precisão ou
concordância individual. Por esse motivo, a comparação entre PAM, PAM13
e MAPA foi realizada utilizando os testes estruturais de acurácia,
precisão e concordância implementados no pacote eirasagree,
conforme metodologia proposta por Silveira, Vieira e Siqueira
(2024).
A análise de equivalência foi conduzida por meio de três comparações pareadas:
A equivalência completa somente pode ser assumida quando não há evidências de diferenças de acurácia, precisão e concordância entre os métodos avaliados.
Analisar as variáveis digitais:
Temperatura, SpO2_WTC,
SpO2_ML, PI_ML, FC_ML,
FC_WTC, FC_Gfit, FR_Gfit,
Glicemia, MCT_ML,
GorduraCorp_WTC.
A análise usa apenas a janela temporal em que há pelo menos uma variável digital observada.
As comparações de equivalência de métodos são feitas diretamente com
eirasagree::AllStructuralTests(), sem reimplementar o
método:
Pulso(bpm) como referência para FC_ML,
FC_WTC e FC_Gfit;Peso(kg) como referência para MCT_ML;Gordura corporal(%) como referência para
GorduraCorp_WTC;MAPA, PAM e PAM13 comparadas
entre si na planilha MAPA.As demais variáveis digitais são analisadas por descrição, gráficos temporais e associação com FC, MCT e gordura corporal da balança.
load_pkgs <- function(pkgs) {
for (p in pkgs) {
if (!requireNamespace(p, quietly = TRUE)) {
stop("Pacote ausente: ", p, ". Instale com install.packages('", p, "') ou conforme documentação do pacote.")
}
suppressPackageStartupMessages(library(p, character.only = TRUE))
}
invisible(TRUE)
}
safe_locale_ptbr <- function() {
suppressWarnings(try(Sys.setlocale("LC_ALL", "pt_BR.UTF-8"), silent = TRUE))
invisible(TRUE)
}
as_num <- function(x) {
suppressWarnings(as.numeric(x))
}
stop_if_missing <- function(df, vars) {
miss <- setdiff(vars, names(df))
if (length(miss) > 0) stop("Colunas ausentes: ", paste(miss, collapse = ", "))
invisible(TRUE)
}
date_range_str <- function(d) {
d <- as.Date(d)
d <- d[is.finite(d)]
if (length(d) == 0) return(NA_character_)
paste0("Início: ", format(min(d), "%Y-%m-%d"), " Fim: ", format(max(d), "%Y-%m-%d"))
}
axis_month_year <- function(dates, by = "1 month") {
dates <- as.Date(dates)
dates <- dates[is.finite(dates)]
if (length(dates) == 0) return(invisible(NULL))
at <- seq(from = as.Date(cut(min(dates), "month")),
to = as.Date(cut(max(dates), "month")),
by = by)
axis(1, at = at, labels = format(at, "%m/%y"), las = 2, cex.axis = 0.8)
invisible(at)
}
plot_calendar_var <- function(df, date_col, y_col, main = NULL, ylab = NULL,
by = "1 month", hlines = NULL, show_smooth = TRUE) {
stop_if_missing(df, c(date_col, y_col))
d <- as.Date(df[[date_col]])
y <- as_num(df[[y_col]])
ok <- is.finite(d) & is.finite(y)
d <- d[ok]
y <- y[ok]
if (length(y) < 1) {
plot.new()
title(main = paste0(y_col, ": sem dados"))
return(invisible(NULL))
}
o <- order(d)
d <- d[o]
y <- y[o]
if (is.null(main)) main <- paste0("Paciente: JOS | ", y_col)
if (is.null(ylab)) ylab <- y_col
yl <- range(y, na.rm = TRUE)
if (diff(yl) == 0) yl <- yl + c(-1, 1)
plot(d, y, type = "p", cex = 0.55, xaxt = "n", xlim = range(d), ylim = yl,
main = main, xlab = "Mês/Ano", ylab = ylab, col = "black")
lines(d, y, col = "gray")
axis_month_year(d, by = by)
if (!is.null(hlines)) {
for (h in hlines) abline(h = h, lty = 2)
}
if (show_smooth && length(y) >= 10) {
x <- seq_along(y)
bw <- suppressWarnings(KernSmooth::dpill(x, y, gridsize = length(y)))
if (!is.finite(bw) || bw <= 0) bw <- max(1, length(y) / 20)
lp <- KernSmooth::locpoly(x, y, bandwidth = bw, gridsize = length(y))
sm <- stats::approx(lp$x, lp$y, xout = x, rule = 2)$y
lines(d, sm, lwd = 1.5)
}
invisible(data.frame(Data = d, y = y))
}
round_numeric <- function(df, digits = 4) {
out <- as.data.frame(df)
num <- vapply(out, is.numeric, logical(1))
out[num] <- lapply(out[num], round, digits = digits)
out
}
summary_numeric <- function(df, vars) {
out <- data.frame(
variavel = character(0),
n = integer(0),
media = numeric(0),
dp = numeric(0),
mediana = numeric(0),
q1 = numeric(0),
q3 = numeric(0),
minimo = numeric(0),
maximo = numeric(0)
)
for (v in vars) {
y <- as_num(df[[v]])
y <- y[is.finite(y)]
if (length(y) == 0) {
linha <- data.frame(variavel = v, n = 0, media = NA, dp = NA, mediana = NA,
q1 = NA, q3 = NA, minimo = NA, maximo = NA)
} else {
linha <- data.frame(
variavel = v,
n = length(y),
media = mean(y),
dp = stats::sd(y),
mediana = stats::median(y),
q1 = as.numeric(stats::quantile(y, 0.25, na.rm = TRUE)),
q3 = as.numeric(stats::quantile(y, 0.75, na.rm = TRUE)),
minimo = min(y),
maximo = max(y)
)
}
out <- rbind(out, linha)
}
out
}
cor_pair_table <- function(df, y_vars, x_vars) {
out <- data.frame(
y = character(0),
x = character(0),
n = integer(0),
pearson = numeric(0),
spearman = numeric(0),
p_pearson = numeric(0),
p_spearman = numeric(0)
)
for (yv in y_vars) {
for (xv in x_vars) {
y <- as_num(df[[yv]])
x <- as_num(df[[xv]])
ok <- is.finite(x) & is.finite(y)
n <- sum(ok)
if (n >= 4) {
ct1 <- suppressWarnings(stats::cor.test(x[ok], y[ok], method = "pearson"))
ct2 <- suppressWarnings(stats::cor.test(x[ok], y[ok], method = "spearman", exact = FALSE))
linha <- data.frame(y = yv, x = xv, n = n,
pearson = unname(ct1$estimate),
spearman = unname(ct2$estimate),
p_pearson = ct1$p.value,
p_spearman = ct2$p.value)
} else {
linha <- data.frame(y = yv, x = xv, n = n,
pearson = NA, spearman = NA,
p_pearson = NA, p_spearman = NA)
}
out <- rbind(out, linha)
}
}
out
}
scatter_ref <- function(df, y_col, x_col, main = NULL) {
y <- as_num(df[[y_col]])
x <- as_num(df[[x_col]])
ok <- is.finite(x) & is.finite(y)
if (sum(ok) < 3) {
plot.new()
title(main = paste0(y_col, " vs ", x_col, ": dados insuficientes"))
return(invisible(NULL))
}
if (is.null(main)) main <- paste0(y_col, " vs ", x_col)
plot(x[ok], y[ok], pch = 1, cex = 0.65, col = "black",
xlab = x_col, ylab = y_col, main = main)
abline(stats::lm(y[ok] ~ x[ok]), lty = 1)
invisible(TRUE)
}
make_eiras_data <- function(df, reference_col, newmethod_col) {
stop_if_missing(df, c(reference_col, newmethod_col))
x <- as_num(df[[reference_col]])
y <- as_num(df[[newmethod_col]])
ok <- is.finite(x) & is.finite(y)
out <- data.frame(Referencia = x[ok], NovoMetodo = y[ok])
## Nomes simples evitam erro de arquivos gerados pelo eirasagree
## com caracteres especiais, como %, parênteses e acentos.
out
}
safe_file_label <- function(x) {
x <- iconv(x, from = "", to = "ASCII//TRANSLIT")
x <- gsub("[^A-Za-z0-9]+", "_", x)
x <- gsub("^_+|_+$", "", x)
x
}
run_eirasagree_pair <- function(df, reference_col, newmethod_col,
alpha = 0.05, out.format = "html") {
dat <- make_eiras_data(df, reference_col, newmethod_col)
cat("\n\nComparação estrutural: ", newmethod_col, " vs ", reference_col, " \n", sep = "")
cat("n pares completos: ", nrow(dat), " \n", sep = "")
if (nrow(dat) < 5) {
cat("Dados insuficientes para comparação estrutural.\n")
return(invisible(NULL))
}
subdir <- paste0(safe_file_label(newmethod_col), "_vs_", safe_file_label(reference_col))
outdir <- file.path("eirasagree_outputs", subdir)
dir.create(outdir, recursive = TRUE, showWarnings = FALSE)
oldwd <- getwd()
setwd(outdir)
zz <- file("console_eirasagree.txt", open = "wt")
sink(zz)
sink(zz, type = "message")
out <- NULL
erro <- NULL
tryCatch(
{
out <- eirasagree::AllStructuralTests(
dat,
reference.cols = 1,
newmethod.cols = 2,
alpha = alpha,
out.format = out.format
)
},
error = function(e) {
erro <<- e
}
)
sink(type = "message")
sink()
close(zz)
setwd(oldwd)
if (!is.null(erro)) {
stop("Erro em eirasagree::AllStructuralTests para ",
newmethod_col, " vs ", reference_col, ": ", conditionMessage(erro))
}
html_file <- file.path(outdir, "Referencia_and_NovoMetodo.html")
console_file <- file.path(outdir, "console_eirasagree.txt")
if (file.exists(html_file)) {
cat("Relatório HTML: [abrir resultado](", html_file, ") \n", sep = "")
} else {
cat("Relatório HTML não localizado. Ver console: [console_eirasagree.txt](", console_file, ") \n", sep = "")
}
invisible(out)
}
pkgs <- c("readxl", "dplyr", "knitr", "KernSmooth", "GGally", "eirasagree")
load_pkgs(pkgs)
safe_locale_ptbr()
df0 <- readxl::read_excel(params$xlsx_path, sheet = params$sheet_pressoes)
vars_digitais <- c(
"Temperatura",
"SpO2_WTC",
"SpO2_ML",
"PI_ML",
"FC_ML",
"FC_WTC",
"FC_Gfit",
"FR_Gfit",
"Glicemia",
"MCT_ML",
"GorduraCorp_WTC"
)
vars_referencia <- c(
"Pulso(bpm)",
"Peso(kg)",
"Gordura corporal(%)"
)
stop_if_missing(df0, c("Data da medição", vars_digitais, vars_referencia))
df <- as.data.frame(df0)
df[["Data da medição"]] <- as.POSIXct(df[["Data da medição"]], tz = "America/Sao_Paulo")
for (v in c(vars_digitais, vars_referencia)) {
df[[v]] <- as_num(df[[v]])
}
linhas_digitais <- rowSums(!is.na(df[, vars_digitais, drop = FALSE])) > 0
df_digital <- df[linhas_digitais, , drop = FALSE]
df_digital <- df_digital[order(df_digital[["Data da medição"]]), , drop = FALSE]
cat("Janela digital | ", date_range_str(df_digital[["Data da medição"]]), "\n")
## Janela digital | Início: 2023-10-22 Fim: 2024-02-01
cat("n linhas na janela digital:", nrow(df_digital), "\n")
## n linhas na janela digital: 98
resumo <- summary_numeric(df_digital, c(vars_digitais, vars_referencia))
knitr::kable(round_numeric(resumo, 4), caption = "Resumo descritivo na janela temporal digital")
| variavel | n | media | dp | mediana | q1 | q3 | minimo | maximo |
|---|---|---|---|---|---|---|---|---|
| Temperatura | 98 | 35.6143 | 0.2577 | 35.60 | 35.400 | 35.800 | 34.9 | 36.2 |
| SpO2_WTC | 86 | 93.7674 | 2.5607 | 94.00 | 92.000 | 95.750 | 90.0 | 100.0 |
| SpO2_ML | 90 | 96.4778 | 1.2382 | 96.00 | 96.000 | 98.000 | 94.0 | 99.0 |
| PI_ML | 90 | 1009.1678 | 6690.4728 | 6.35 | 4.600 | 7.900 | 1.3 | 45142.0 |
| FC_ML | 90 | 78.3111 | 5.9806 | 78.00 | 74.000 | 82.750 | 66.0 | 94.0 |
| FC_WTC | 89 | 83.0449 | 6.8853 | 82.00 | 79.000 | 87.000 | 70.0 | 101.0 |
| FC_Gfit | 85 | 79.8118 | 6.8112 | 80.00 | 75.000 | 85.000 | 64.0 | 99.0 |
| FR_Gfit | 86 | 16.1977 | 1.6579 | 16.00 | 15.000 | 17.000 | 13.0 | 20.0 |
| Glicemia | 28 | 99.5000 | 5.7252 | 99.50 | 94.750 | 102.750 | 89.0 | 113.0 |
| MCT_ML | 81 | 85.7370 | 0.6972 | 85.70 | 85.300 | 86.200 | 83.6 | 87.3 |
| GorduraCorp_WTC | 79 | 29.0114 | 0.8419 | 29.10 | 28.700 | 29.600 | 26.1 | 30.5 |
| Pulso(bpm) | 98 | 80.5612 | 7.4930 | 81.00 | 74.000 | 85.000 | 68.0 | 105.0 |
| Peso(kg) | 98 | 85.2429 | 0.7355 | 85.20 | 84.800 | 85.675 | 83.3 | 87.1 |
| Gordura corporal(%) | 98 | 28.8194 | 0.3366 | 28.90 | 28.625 | 29.075 | 28.0 | 29.6 |
Cada gráfico usa apenas as datas em que a própria variável tem valor observado. Isso evita eixo temporal artificialmente longo.
for (v in vars_digitais) {
plot_calendar_var(
df = df_digital,
date_col = "Data da medição",
y_col = v,
main = paste0("Paciente: JOS | ", v),
ylab = v,
by = "1 month"
)
}
cor_tab <- cor_pair_table(
df = df_digital,
y_vars = vars_digitais,
x_vars = vars_referencia
)
knitr::kable(round_numeric(cor_tab, 4), caption = "Correlação das variáveis digitais com FC, MCT e gordura corporal da balança")
| y | x | n | pearson | spearman | p_pearson | p_spearman |
|---|---|---|---|---|---|---|
| Temperatura | Pulso(bpm) | 98 | 0.3317 | 0.2898 | 0.0008 | 0.0038 |
| Temperatura | Peso(kg) | 98 | -0.2366 | -0.2288 | 0.0190 | 0.0234 |
| Temperatura | Gordura corporal(%) | 98 | -0.2017 | -0.2035 | 0.0464 | 0.0445 |
| SpO2_WTC | Pulso(bpm) | 86 | 0.0192 | 0.0257 | 0.8609 | 0.8140 |
| SpO2_WTC | Peso(kg) | 86 | 0.1835 | 0.2357 | 0.0908 | 0.0289 |
| SpO2_WTC | Gordura corporal(%) | 86 | 0.0694 | 0.1532 | 0.5255 | 0.1591 |
| SpO2_ML | Pulso(bpm) | 90 | -0.3842 | -0.3740 | 0.0002 | 0.0003 |
| SpO2_ML | Peso(kg) | 90 | 0.2809 | 0.2762 | 0.0073 | 0.0084 |
| SpO2_ML | Gordura corporal(%) | 90 | 0.2688 | 0.2774 | 0.0104 | 0.0081 |
| PI_ML | Pulso(bpm) | 90 | -0.0564 | 0.3502 | 0.5975 | 0.0007 |
| PI_ML | Peso(kg) | 90 | 0.1827 | -0.0084 | 0.0847 | 0.9377 |
| PI_ML | Gordura corporal(%) | 90 | 0.1558 | -0.0168 | 0.1424 | 0.8753 |
| FC_ML | Pulso(bpm) | 90 | 0.7416 | 0.6960 | 0.0000 | 0.0000 |
| FC_ML | Peso(kg) | 90 | -0.2202 | -0.1862 | 0.0371 | 0.0789 |
| FC_ML | Gordura corporal(%) | 90 | -0.2006 | -0.1862 | 0.0580 | 0.0789 |
| FC_WTC | Pulso(bpm) | 89 | 0.5301 | 0.5389 | 0.0000 | 0.0000 |
| FC_WTC | Peso(kg) | 89 | -0.0315 | -0.0782 | 0.7696 | 0.4663 |
| FC_WTC | Gordura corporal(%) | 89 | -0.1016 | -0.1226 | 0.3434 | 0.2525 |
| FC_Gfit | Pulso(bpm) | 85 | 0.6742 | 0.6079 | 0.0000 | 0.0000 |
| FC_Gfit | Peso(kg) | 85 | -0.2624 | -0.2525 | 0.0152 | 0.0198 |
| FC_Gfit | Gordura corporal(%) | 85 | -0.2621 | -0.2581 | 0.0154 | 0.0171 |
| FR_Gfit | Pulso(bpm) | 86 | 0.0891 | 0.0540 | 0.4144 | 0.6217 |
| FR_Gfit | Peso(kg) | 86 | -0.4480 | -0.4280 | 0.0000 | 0.0000 |
| FR_Gfit | Gordura corporal(%) | 86 | -0.4230 | -0.4448 | 0.0000 | 0.0000 |
| Glicemia | Pulso(bpm) | 28 | -0.0121 | -0.0629 | 0.9514 | 0.7506 |
| Glicemia | Peso(kg) | 28 | 0.1015 | 0.0967 | 0.6072 | 0.6245 |
| Glicemia | Gordura corporal(%) | 28 | 0.1128 | 0.1147 | 0.5675 | 0.5610 |
| MCT_ML | Pulso(bpm) | 81 | -0.1836 | -0.1485 | 0.1009 | 0.1858 |
| MCT_ML | Peso(kg) | 81 | 0.8744 | 0.8827 | 0.0000 | 0.0000 |
| MCT_ML | Gordura corporal(%) | 81 | 0.7882 | 0.7994 | 0.0000 | 0.0000 |
| GorduraCorp_WTC | Pulso(bpm) | 79 | -0.2953 | -0.3261 | 0.0082 | 0.0034 |
| GorduraCorp_WTC | Peso(kg) | 79 | -0.0516 | -0.0428 | 0.6514 | 0.7080 |
| GorduraCorp_WTC | Gordura corporal(%) | 79 | -0.0408 | -0.0005 | 0.7214 | 0.9964 |
vars_pair <- c(vars_digitais, vars_referencia)
vars_pair <- vars_pair[vars_pair %in% names(df_digital)]
ok_n <- sapply(vars_pair, function(v) sum(is.finite(as_num(df_digital[[v]]))))
vars_pair <- vars_pair[ok_n >= 3]
if (length(vars_pair) >= 2) {
print(GGally::ggpairs(
df_digital[, vars_pair, drop = FALSE],
title = "Variáveis digitais e referências da balança",
diag = list(continuous = "densityDiag")
))
}
for (v in vars_digitais) {
scatter_ref(df_digital, y_col = v, x_col = "Pulso(bpm)")
scatter_ref(df_digital, y_col = v, x_col = "Peso(kg)")
scatter_ref(df_digital, y_col = v, x_col = "Gordura corporal(%)")
}
Método aplicado diretamente por
eirasagree::AllStructuralTests().
Apenas pares que medem o mesmo construto são submetidos à comparação estrutural:
Comparação estrutural: FC_ML vs Pulso(bpm)
n pares completos: 90
Relatório HTML não localizado. Ver console: console_eirasagree.txt
Comparação estrutural: FC_WTC vs Pulso(bpm)
n pares completos: 89
Relatório HTML não localizado. Ver console: console_eirasagree.txt
Comparação estrutural: FC_Gfit vs Pulso(bpm)
n pares completos: 85
Relatório HTML não localizado. Ver console: console_eirasagree.txt
Comparação estrutural: SpO2_ML vs SpO2_WTC
n pares completos: 86
Relatório HTML não localizado. Ver console: console_eirasagree.txt
Comparação estrutural: MCT_ML vs Peso(kg)
n pares completos: 81
Relatório HTML não localizado. Ver console: console_eirasagree.txt
Comparação estrutural: GorduraCorp_WTC vs Gordura corporal(%)
n pares completos: 79
Relatório HTML não localizado. Ver console: console_eirasagree.txt
Nesta seção, a análise fica restrita à janela temporal da própria
planilha MAPA. A comparação estrutural é feita com
eirasagree::AllStructuralTests(), sem uso de Bland-Altman
clássico como critério decisório.
df_mapa0 <- readxl::read_excel(params$xlsx_path, sheet = params$sheet_mapa)
stop_if_missing(df_mapa0, c("HORA", "PAS", "PAD", "PAM", "MAPA", "PAM13", "FC"))
df_mapa <- as.data.frame(df_mapa0)
df_mapa[["HORA"]] <- as.POSIXct(df_mapa[["HORA"]], tz = "America/Sao_Paulo")
for (v in c("PAS", "PAD", "PAM", "MAPA", "PAM13", "FC")) {
df_mapa[[v]] <- as_num(df_mapa[[v]])
}
df_mapa <- df_mapa[is.finite(df_mapa[["HORA"]]), , drop = FALSE]
df_mapa <- df_mapa[order(df_mapa[["HORA"]]), , drop = FALSE]
df_mapa <- df_mapa |>
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"
)
cat("Janela MAPA | ", date_range_str(df_mapa[["HORA"]]), "\n")
## Janela MAPA | Início: 2023-01-20 Fim: 2023-01-21
cat("n tempos observados:", nrow(df_mapa), "\n")
## n tempos observados: 77
resumo_mapa <- summary_numeric(df_mapa, c("PAS", "PAD", "PAM", "MAPA", "PAM13", "FC"))
knitr::kable(round_numeric(resumo_mapa, 4), caption = "Resumo descritivo da planilha MAPA")
| variavel | n | media | dp | mediana | q1 | q3 | minimo | maximo |
|---|---|---|---|---|---|---|---|---|
| PAS | 77 | 131.3377 | 11.6930 | 130.0000 | 125.0000 | 140.0000 | 98.0000 | 166.0000 |
| PAD | 77 | 96.2468 | 10.3035 | 96.0000 | 89.0000 | 101.0000 | 75.0000 | 125.0000 |
| PAM | 77 | 109.4652 | 10.2288 | 109.3098 | 102.0984 | 115.3171 | 88.7916 | 137.9888 |
| MAPA | 77 | 112.3377 | 10.0218 | 112.0000 | 105.0000 | 118.0000 | 89.0000 | 141.0000 |
| PAM13 | 77 | 107.9437 | 9.9504 | 107.6667 | 101.3333 | 113.3333 | 87.3333 | 135.3333 |
| FC | 77 | 81.7662 | 14.2172 | 80.0000 | 71.0000 | 89.0000 | 62.0000 | 133.0000 |
plot_calendar_var(df_mapa, "HORA", "PAM", main = "MAPA | PAM", ylab = "PAM", by = "1 day", hlines = c(70, 90, 100))
plot_calendar_var(df_mapa, "HORA", "MAPA", main = "MAPA | MAPA", ylab = "MAPA", by = "1 day", hlines = c(70, 90, 100))
plot_calendar_var(df_mapa, "HORA", "PAM13", main = "MAPA | PAM13", ylab = "PAM13", by = "1 day", hlines = c(70, 90, 100))
o <- order(df_mapa[["HORA"]])
x <- seq_along(o)
plot(x, df_mapa[["PAM"]][o], type = "p", cex = 0.55, pch = 1,
main = "MAPA | Comparação PAM, MAPA e PAM13",
xlab = "Tempo ordenado", ylab = "mmHg", col = "black")
lines(x, df_mapa[["PAM"]][o], col = "gray")
points(x, df_mapa[["MAPA"]][o], pch = 2, cex = 0.55, col = "black")
lines(x, df_mapa[["MAPA"]][o], col = "gray")
points(x, df_mapa[["PAM13"]][o], pch = 3, cex = 0.55, 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, 2, 3), bty = "n")
X_mapa <- df_mapa[, c("PAM", "MAPA", "PAM13"), drop = FALSE]
knitr::kable(round(stats::cor(X_mapa, use = "pairwise.complete.obs"), 4),
caption = "Correlação entre PAM, MAPA e PAM13")
| PAM | MAPA | PAM13 | |
|---|---|---|---|
| PAM | 1.0000 | 0.9912 | 0.9942 |
| MAPA | 0.9912 | 1.0000 | 0.9931 |
| PAM13 | 0.9942 | 0.9931 | 1.0000 |
Comparação estrutural: PAM vs MAPA
n pares completos: 77
Relatório HTML não localizado. Ver console: console_eirasagree.txt
Comparação estrutural: PAM13 vs MAPA
n pares completos: 77
Relatório HTML não localizado. Ver console: console_eirasagree.txt
Comparação estrutural: PAM13 vs PAM
n pares completos: 77
Relatório HTML não localizado. Ver console: console_eirasagree.txt
O procedimento clássico de Bland-Altman não foi usado como critério
decisório. A equivalência entre métodos foi avaliada pelo procedimento
estrutural do pacote eirasagree, com decomposição em
acurácia, precisão e concordância com a bissetriz.
Para variáveis que não medem o mesmo construto da balança, como
Temperatura, PI_ML, FR_Gfit e
Glicemia, não há comparação de método contra FC, MCT ou
gordura corporal. SpO2_WTC e SpO2_ML medem o
mesmo construto e, por isso, foram comparadas por
eirasagree::AllStructuralTests(). Na planilha
MAPA, as três estimativas de pressão arterial média
(PAM, PAM13 e MAPA) foram
comparadas par a par: PAM versus MAPA,
PAM13 versus MAPA e PAM13 versus
PAM.
A equivalência entre os dispositivos vestíveis e os respectivos métodos de referência foi avaliada utilizando a abordagem estrutural implementada no pacote eirasAgree. A análise foi conduzida em três níveis complementares: viés estrutural (acurácia), precisão estrutural e concordância estrutural.
A frequência cardíaca medida pelo Galaxy Fit apresentou ausência de viés estrutural, manutenção da precisão estrutural e concordância estrutural compatível com a reta identidade. Esse foi o único dispositivo que satisfez simultaneamente todos os critérios de equivalência.
A frequência cardíaca medida pelo oxímetro Multilaser apresentou viés estrutural negativo e perda de precisão estrutural em relação ao monitor Omron, embora a hipótese de concordância estrutural não tenha sido rejeitada.
A frequência cardíaca medida pelo Galaxy Watch apresentou viés estrutural positivo em relação ao monitor Omron. Apesar da manutenção da precisão estrutural e da concordância estrutural, a presença de viés impediu a demonstração de equivalência.
A estimativa de gordura corporal fornecida pelo Galaxy Watch apresentou viés estrutural positivo e perda de precisão estrutural em relação à balança Omron HBF-222T.
A massa corporal total medida pela balança Multilaser apresentou viés estrutural positivo em relação à balança Omron HBF-222T, embora a precisão estrutural tenha sido preservada.
A saturação periférica de oxigênio medida pelo Galaxy Watch apresentou viés estrutural positivo e perda de precisão estrutural em relação ao oxímetro Multilaser HC261.
Os resultados demonstraram que a frequência cardíaca obtida pelo Galaxy Fit foi a única variável que apresentou evidência simultânea de ausência de viés estrutural, manutenção da precisão estrutural e concordância estrutural com o método de referência.
Os demais dispositivos apresentaram evidência de viés estrutural, perda de precisão estrutural ou ambos. Embora a hipótese de concordância estrutural não tenha sido rejeitada em nenhuma das comparações, a presença de viés e/ou perda de precisão impede a caracterização de equivalência metrológica.
Esses resultados evidenciam que a concordância estrutural, isoladamente, não é suficiente para demonstrar equivalência entre métodos de medida. A equivalência requer simultaneamente ausência de viés estrutural, manutenção da precisão estrutural e compatibilidade com a reta identidade.
| Variável | Método novo | Método de referência | Diferença média (Novo − Referência) | IC95% do viés | Viés estrutural | Precisão estrutural | Concordância estrutural |
|---|---|---|---|---|---|---|---|
| Frequência cardíaca (bpm) | Galaxy Fit | Omron HEM-7346T | -0,46 | -1,77 a 0,77 | Não rejeitado | Não rejeitada | Não rejeitada |
| Frequência cardíaca (bpm) | Oxímetro Multilaser HC261 | Omron HEM-7346T | -2,00 | -2,92 a -1,08 | Rejeitado | Rejeitada | Não rejeitada |
| Frequência cardíaca (bpm) | Galaxy Watch | Omron HEM-7346T | +2,75 | 1,36 a 4,11 | Rejeitado | Não rejeitada | Não rejeitada |
| Gordura corporal (%) | Galaxy Watch | Omron HBF-222T | +0,26 | 0,03 a 0,46 | Rejeitado | Rejeitada | Não rejeitada |
| Massa corporal total (kg) | Balança Multilaser | Omron HBF-222T | +0,60 | 0,52 a 0,68 | Rejeitado | Não rejeitada | Não rejeitada |
| Saturação periférica de oxigênio (%) | Galaxy Watch | Oxímetro Multilaser HC261 | +2,74 | 2,43 a 3,05 | Rejeitado | Rejeitada | Não rejeitada |
Dos seis testes realizados, apenas um apresentou ausência de viés estrutural. Três comparações apresentaram simultaneamente viés estrutural e perda de precisão estrutural. Em todas as comparações a hipótese de concordância estrutural foi mantida. Entretanto, somente a frequência cardíaca medida pelo Galaxy Fit apresentou evidência compatível com equivalência metrológica completa em relação ao método de referência.
A análise foi conduzida utilizando os testes estruturais do pacote
eirasagree, que avaliam três propriedades hierárquicas:
Uma correlação elevada não implica equivalência. Dois métodos podem apresentar correlação próxima de 1 e ainda assim apresentar viés sistemático ou diferenças de precisão.
A correlação foi extremamente elevada (\(r=0,991\)), indicando forte associação linear entre as medidas. Entretanto, observou-se viés médio de aproximadamente −2,87 mmHg, com IC95% entre −3,22 e −2,55 mmHg. O teste de acurácia rejeitou a hipótese de ausência de viés.
O teste de precisão não rejeitou a hipótese de linha horizontal, indicando que a diferença entre os métodos permanece aproximadamente constante ao longo da faixa de pressão observada.
O teste de concordância pela regressão de Deming e a região elíptica não rejeitaram a hipótese de equivalência estrutural após a correção do viés.
Interpretação: PAM e MAPA diferem por um viés constante de aproximadamente −2,9 mmHg, mas apresentam boa precisão e concordância estrutural.
A correlação também foi extremamente elevada (\(r=0,993\)). Contudo, o viés médio foi maior, aproximadamente −4,39 mmHg, com IC95% entre −4,70 e −4,11 mmHg. O teste de acurácia rejeitou a hipótese de ausência de viés.
O teste de precisão não foi rejeitado, indicando ausência de erro proporcional relevante ao longo da faixa de medidas.
A regressão de Deming e a análise elíptica indicaram concordância estrutural após a correção do viés médio.
Interpretação: PAM13 produz valores sistematicamente inferiores aos do MAPA em aproximadamente 4,4 mmHg, porém mantém boa precisão e concordância estrutural.
A correlação permaneceu extremamente elevada (\(r=0,994\)). O viés médio foi de aproximadamente −1,52 mmHg, com IC95% entre −1,80 e −1,26 mmHg. O teste de acurácia rejeitou a hipótese de ausência de viés.
Diferentemente das comparações anteriores, o teste de precisão foi rejeitado. Além disso, o teste de Shukla rejeitou \(\lambda=1\), indicando diferenças entre as variâncias dos erros dos métodos.
Apesar disso, a regressão de Deming e a região elíptica não rejeitaram a concordância estrutural.
Interpretação: PAM13 e PAM apresentam pequeno viés médio, mas também diferenças de precisão, sugerindo que não devem ser considerados intercambiáveis sem correção.
| Comparação | Correlação | Viés médio (mmHg) | Acurácia | Precisão | Concordância |
|---|---|---|---|---|---|
| PAM × MAPA | 0,991 | −2,87 | Reprovada | Aprovada | Aprovada |
| PAM13 × MAPA | 0,993 | −4,39 | Reprovada | Aprovada | Aprovada |
| PAM13 × PAM | 0,994 | −1,52 | Reprovada | Reprovada | Aprovada |
Os três métodos apresentam correlações extremamente elevadas (\(r>0,99\)), porém todos exibem viés sistemático estatisticamente significativo. A menor discrepância média foi observada entre PAM13 e PAM (−1,52 mmHg), enquanto a maior ocorreu entre PAM13 e MAPA (−4,39 mmHg). PAM e MAPA apresentaram a melhor combinação de propriedades métricas, com viés moderado, precisão preservada e concordância estrutural. Já PAM13 e PAM mostraram diferenças tanto de acurácia quanto de precisão, indicando que não são estritamente equivalentes apesar da elevada correlação.