Espirometria e teste broncodilatador de crianças portadoras de asma do HCB

Carmen Livia Martins, Julia Furtado< Jordana, Mariana e Marcus Jones

# classificacao das espirometrias BASAL [SBPT]

## 
##        Normal   Obstructive   Restrictive    Dysanapsis         Mixed Uncategorized 
##           259            49            30            25             9            96
## 
##        Normal   Obstructive   Restrictive    Dysanapsis         Mixed Uncategorized 
##           368            15            18            19             5            43

Demographic data: Age and height of the sample

The sample consisted of 468 asthmatic children, 277 male and 191 female. Age ranged from 3.62 to 12.99 years, with a median age of 9.1. The median height was 134, ranging from 97 e 187.

##                             
##                              Overall                
##   n                             468                 
##   Sex = Male (%)                277 (59.2)          
##   Age (years) (median [IQR])   9.07 [7.50, 10.85]   
##   Height (cm) (median [IQR]) 134.00 [125.00, 144.00]
##   Weight (Kg) (median [IQR])  31.00 [24.88, 40.00]  
##   BMI (median [IQR])          17.08 [15.36, 19.56]

Histograma idade

Histograma da Altura

Espirometria Basal (Pre BD)

##                               
##                                Overall          
##   n                             468             
##   fvc (median [IQR])           2.00 [1.60, 2.36]
##   fev1 (median [IQR])          1.68 [1.35, 1.97]
##   fev1/fvc (median [IQR])      0.86 [0.80, 0.90]
##   FEF25-75% (median [IQR])     1.79 [1.41, 2.35]
##   FEF25-75%/fvc (median [IQR]) 0.94 [0.73, 1.15]

Espirometria Pos BD

##                                   
##                                    Overall          
##   n                                 468             
##   fvc_pos (median [IQR])           2.03 [1.64, 2.42]
##   fev1_pos (median [IQR])          1.79 [1.46, 2.12]
##   fev1/fvc_pos (median [IQR])      0.89 [0.85, 0.92]
##   FEF25-75%_pos (median [IQR])     2.25 [1.86, 2.75]
##   FEF25-75%/fvc_pos (median [IQR]) 1.11 [0.93, 1.31]

Classificação SBPT pré-BD

## 
##        Normal   Obstructive   Restrictive    Dysanapsis         Mixed Uncategorized 
##           259            49            30            25             9            96

Classificação SBPT pós-BD

## 
##        Normal   Obstructive   Restrictive    Dysanapsis         Mixed Uncategorized 
##           368            15            18            19             5            43

GLI Race Neutral Baseline

## 
##        Normal   Obstructive   Restrictive         Mixed    Dysanapsis Uncategorized 
##           358            17            19             4            23            47

GLI Race Neutral Post-BD

## 
##        Normal   Obstructive   Restrictive         Mixed    Dysanapsis Uncategorized 
##           369             9            14             3            31            42

comparação classificação espirométrica Equação Brasileira x GLI Race-Neutral

Baseline, All subjects (n=468)
GLI Race-Neutral
Normal Obstructive Restrictive Mixed Dysanapsis Uncategorized Total
SBPT
Normal 259 0 0 0 0 0 259
Obstructive 11 16 0 0 1 21 49
Restrictive 5 0 16 0 0 9 30
Dysanapsis 9 0 0 0 16 0 25
Mixed 0 1 2 4 0 2 9
Uncategorized 74 0 1 0 6 15 96
Total 358 17 19 4 23 47 468
Post BD, All subjects (n=468)
GLI Race-Neutral
Normal Obstructive Restrictive Mixed Dysanapsis Uncategorized Total
SBPT
Normal 339 0 0 0 11 18 368
Obstructive 1 9 0 0 0 5 15
Restrictive 3 0 13 0 0 2 18
Dysanapsis 2 0 0 0 17 0 19
Mixed 0 0 1 3 0 1 5
Uncategorized 24 0 0 0 3 16 43
Total 369 9 14 3 31 42 468

NEW ANALYSIS

BASELINE SBPT

## 
##        Normal   Obstructive   Restrictive    Dysanapsis         Mixed Uncategorized 
##           259           121            30            25             9            24

POST BD SBPT

## 
##        Normal   Obstructive   Restrictive         Mixed    Dysanapsis Uncategorized 
##           368            38            18             5            19            18

BASELINE GLI RACE-NEUTRAL

## 
##        Normal   Obstructive   Restrictive    Dysanapsis         Mixed Uncategorized 
##           358            50            19            23             4            14

POST BD GLI RACE-NEUTRAL

# Definir limites
LLN <- -1.645
threshold_fvc <- 0.674  # Percentil 75 para disanapsis

# Função para classificar padrões de espirometria
classify_spirometry_pg <- function(gnfev1_z_pos, gnfvc_z_pos, gnfev1fvc_z_pos) {
  if (is.na(gnfev1_z_pos) | is.na(gnfvc_z_pos) | is.na(gnfev1fvc_z_pos)) {
    return(NA)  # Mantém os valores NA
  } else if (gnfvc_z_pos >= LLN & gnfev1_z_pos >= LLN & gnfev1fvc_z_pos >= LLN) {
    return("Normal")
  } else if (gnfvc_z_pos >= LLN & gnfvc_z_pos < threshold_fvc & gnfev1fvc_z_pos < LLN) {
    return("Obstructive")
  } else if (gnfvc_z_pos < LLN & gnfev1_z_pos < LLN & gnfev1fvc_z_pos >= LLN) {
    return("Restrictive")
  } else if (gnfvc_z_pos < LLN & gnfev1fvc_z_pos < LLN) {
    return("Mixed")
  } else if (gnfvc_z_pos >= threshold_fvc & gnfev1_z_pos > LLN & gnfev1fvc_z_pos < LLN) {
    return("Dysanapsis")
  } else {
    return("Uncategorized")
  }
}

# Vetorizar a função para permitir aplicação em colunas do dataframe
classify_spirometry_pg <- Vectorize(classify_spirometry_pg)

# Aplicar classificação ao dataframe
df$class_pg <- classify_spirometry_pg(df$gnfev1_z_pos, df$gnfvc_z_pos, df$gnfev1fvc_z_pos)

# Converter para fator ordenado
df$class_pg <- factor(df$class_pg, ordered = TRUE, levels = c("Normal", "Obstructive", "Restrictive", "Dysanapsis", "Mixed", "Uncategorized"))

# Exibir resumo da classificação
table(df$class_pg)
## 
##        Normal   Obstructive   Restrictive    Dysanapsis         Mixed Uncategorized 
##           418            15            15             8             2             8

NEW COMPARISONS

comparação classificação espirométrica Equação Brasileira x GLI Race-Neutral

Baseline, All subjects (n=468)
GLI Race-Neutral
Normal Obstructive Restrictive Dysanapsis Mixed Uncategorized Total
SBPT
Normal 259 0 0 0 0 0 259
Obstructive 64 49 0 7 0 1 121
Restrictive 5 0 16 0 0 9 30
Dysanapsis 9 0 0 16 0 0 25
Mixed 0 1 2 0 4 2 9
Uncategorized 21 0 1 0 0 2 24
Total 358 50 19 23 4 14 468
Post BD, All subjects (n=468)
GLI Race-Neutral
Normal Obstructive Restrictive Dysanapsis Mixed Uncategorized Total
SBPT
Normal 368 0 0 0 0 0 368
Obstructive 18 15 0 2 0 3 38
Restrictive 3 0 13 0 0 2 18
Mixed 0 0 2 0 2 1 5
Dysanapsis 13 0 0 6 0 0 19
Uncategorized 16 0 0 0 0 2 18
Total 418 15 15 8 2 8 466
# Carregar as bibliotecas necessárias
library(ggplot2)
library(ggalluvial)
library(dplyr)

# Verificar se as colunas existem no dataframe
if (!all(c("class_bs", "class_bg") %in% names(df))) {
  stop("As colunas 'class_bs' e 'class_bg' não existem no dataframe.")
}

# Criar um dataframe para o gráfico Alluvial
df_alluvial <- df %>%
  count(class_bs, class_bg) %>%  # Contar as transições entre classificações
  rename(Equation_1 = class_bs, Equation_2 = class_bg)

# Criar o gráfico Alluvial (Sankey)
alluvial2 <- ggplot(df_alluvial,
       aes(axis1 = Equation_1, axis2 = Equation_2, y = n)) +
  geom_alluvium(aes(fill = Equation_1)) +
  geom_stratum() +
  geom_text(stat = "stratum", aes(label = after_stat(stratum))) +
  theme_minimal() +
  labs(title = "Comparison of Spirometry Classifications Between Equations",
       x = "Equation SBPT", y = "Number of Patients",
       caption = "Alluvial plot showing classification transitions") +
  scale_fill_brewer(palette = "Set1")  # Paleta de cores agradável

alluvial2
## Warning in to_lodes_form(data = data, axes = axis_ind, discern = params$discern): Some strata appear at multiple axes.
## Warning in to_lodes_form(data = data, axes = axis_ind, discern = params$discern): Some strata appear at multiple axes.
## Warning in to_lodes_form(data = data, axes = axis_ind, discern = params$discern): Some strata appear at multiple axes.

library(easyalluvial)
p <- alluvial_wide(
  data = df %>% select(class_bs, class_bg),  # Selecionar apenas as colunas de classificação
  fill_by = "all_flows"  # Define a cor baseada na primeira variável (class_bs)
)

# Adicionar título usando ggtitle()
p + ggtitle("Comparison of Spirometry Classifications Between Equations") +
  theme_minimal()

# Carregar pacotes necessários
library(ggplot2)
library(dplyr)
library(tidyr)

# Criar um exemplo de dataframe com seis categorias
dfx <- data.frame(df$class_bs, df$class_bg)

# Contar o número de indivíduos em cada categoria para ambas as variáveis
df_counts_bs <- dfx %>%
  count(df.class_bs) %>%
  rename(Categoria = df.class_bs, Contagem = n) %>%
  mutate(Variável = "class_bs")

df_counts_bg <- dfx %>%
  count(df.class_bg) %>%
  rename(Categoria = df.class_bg, Contagem = n) %>%
  mutate(Variável = "class_bg")

# Garantir que a coluna Categoria seja do mesmo tipo (character) antes de unir os dados
df_counts_bs$Categoria <- as.character(df_counts_bs$Categoria)
df_counts_bg$Categoria <- as.character(df_counts_bg$Categoria)

# Unir os dois conjuntos de dados
df_counts <- bind_rows(df_counts_bs, df_counts_bg)

df_counts$Categoria <- factor(df_counts$Categoria, ordered = TRUE, levels = c("Normal", "Obstructive", "Restrictive", "Dysanapsis","Mixed",  "Uncategorized"))


# Criar o gráfico de barras lado a lado
bar1 <- ggplot(df_counts, aes(x = Categoria, y = Contagem, fill = Variável, alpha = 0.1)) +
  geom_bar(stat = "identity", position = position_dodge(width = 0.6)) +  scale_fill_manual(values = p2)+ geom_text(aes(label = Contagem), 
            position = position_dodge(width = 0.8), 
            vjust = -0.5, size = 5) +
  labs(x = "Classification", y = "Number of Subjects", fill = "Variável") +
  theme_minimal() + theme(legend.position = "none")+
  theme(text = element_text(size = 14))
bar1

# Carregar pacotes
library(ggplot2)
library(ggalluvial)
library(dplyr)
library(scales)  # Para ajustar transparência das cores
## 
## Attaching package: 'scales'
## The following object is masked from 'package:purrr':
## 
##     discard
## The following object is masked from 'package:readr':
## 
##     col_factor
# Criando um conjunto de dados com 6 categorias em ambos os lados
data <- data.frame(
    Etapa = c("Normal", "Normal", "Normal", "Restritivo", "Restritivo", "Restritivo", 
              "Obstrutivo", "Obstrutivo", "Obstrutivo", "Misto", "Misto", "Misto",
              "Disanaptico", "Disanaptico", "Disanaptico", "Indeterminado", "Indeterminado", "Indeterminado"),
    Status = c("Normal", "Restritivo", "Obstrutivo", "Normal", "Restritivo", "Obstrutivo", 
               "Normal", "Restritivo", "Obstrutivo", "Misto", "Disanaptico", "Indeterminado",
               "Misto", "Disanaptico", "Indeterminado", "Misto", "Disanaptico", "Indeterminado"),
    Freq = c(10, 5, 7, 8, 4, 6, 12, 6, 9, 7, 3, 5, 6, 5, 4, 3, 2, 1)  # Frequência dos fluxos
)

# Criando o gráfico Alluvial com as colunas em cor mais clara
ggplot(data, aes(axis1 = Etapa, axis2 = Status, y = Freq)) +
    geom_alluvium(aes(fill = Status), width = 0.2, alpha = 0.8) +  # Linhas de fluxo
    geom_stratum(aes(fill = after_stat(stratum)), color = "black", size = 0.5, alpha = 0.4) +  # Colunas mais claras
    geom_text(stat = "stratum", aes(label = after_stat(stratum)), size = 5) + # Rótulos
    scale_fill_manual(values = c(
        "Normal" = "#1f77b4", 
        "Restritivo" = "#ff7f0e", 
        "Obstrutivo" = "#2ca02c", 
        "Misto" = "#d62728", 
        "Disanaptico" = "#9467bd", 
        "Indeterminado" = "#8c564b"
    )) +  # Define as cores de cada categoria
    scale_x_discrete(limits = c("Etapa", "Status")) +
    theme_minimal() +
    labs(
        title = "Distribuição das Categorias Respiratórias",
        x = "Categoria Inicial",
        y = "Frequência"
    ) +
    theme(axis.text.y = element_blank(), axis.ticks.y = element_blank())
## Warning in to_lodes_form(data = data, axes = axis_ind, discern = params$discern): Some strata appear at multiple axes.
## Warning in to_lodes_form(data = data, axes = axis_ind, discern = params$discern): Some strata appear at multiple axes.
## Warning in to_lodes_form(data = data, axes = axis_ind, discern = params$discern): Some strata appear at multiple axes.

# Garantindo que as classes estejam ordenadas corretamente
ordered_classes <- factor(
    c("Normal", "Obstructive", "Restrictive", "Dysanapsis", "Mixed", "Uncategorized"),
    levels = c("Normal", "Obstructive", "Restrictive", "Dysanapsis", "Mixed", "Uncategorized"),
    ordered = TRUE
)

# Criando um dataframe baseado nos dados do usuário
data <- df %>%
    group_by(class_bs, class_bg) %>%
    summarise(Freq = n(), .groups = "drop") %>%
    mutate(
        class_bs = factor(class_bs, levels = levels(ordered_classes), ordered = TRUE),
        class_bg = factor(class_bg, levels = levels(ordered_classes), ordered = TRUE)
    )

# Criando o gráfico Alluvial
ggplot(data, aes(axis1 = class_bs, axis2 = class_bg, y = Freq)) +
    geom_alluvium(aes(fill = class_bg), width = 0.2, alpha = 0.8) +  # Linhas de fluxo
    geom_stratum(aes(fill = after_stat(stratum)), color = "black", size = 0.5, alpha = 0.4) +  # Blocos mais claros
    geom_text(stat = "stratum", aes(label = after_stat(stratum)), size = 5) + # Rótulos
    scale_fill_manual(values = c(
        "Normal" = "#1f77b4", 
        "Obstructive" = "#ff7f0e", 
        "Restrictive" = "#2ca02c", 
        "Dysanapsis" = "#9467bd", 
        "Mixed" = "#d62728", 
        "Uncategorized" = "#8c564b"
    )) +  # Define cores das categorias
    scale_x_discrete(limits = c("SBPT2020", "GLI2022")) +  # Renomeando os eixos
    theme_minimal() +
    labs(
        title = "Comparação de Classificações: SBPT2020 vs GLI2022",
        x = "Equação de Classificação",
        y = "Frequência"
    ) +
    theme(axis.text.y = element_blank(), axis.ticks.y = element_blank())
## Warning in to_lodes_form(data = data, axes = axis_ind, discern = params$discern): Some strata appear at multiple axes.
## Warning in to_lodes_form(data = data, axes = axis_ind, discern = params$discern): Some strata appear at multiple axes.
## Warning in to_lodes_form(data = data, axes = axis_ind, discern = params$discern): Some strata appear at multiple axes.

# Criando o gráfico Alluvial
ggplot(data, aes(axis1 = class_bs, axis2 = class_bg, y = Freq)) +
geom_alluvium(aes(fill = class_bs), width = 0.2, alpha = 0.8) +  # Linhas de fluxo
geom_stratum(aes(fill = after_stat(stratum)), color = "black", size = 0.8, alpha = 0.5) +  # Blocos mais claros
geom_text(stat = "stratum", aes(label = after_stat(stratum)), size = 5) + # Rótulos
scale_fill_manual(values = c(
"Normal" = "#1f77b4AA",
"Obstructive" = "#ff7f0eAA",
"Restrictive" = "#2ca02cAA",
"Dysanapsis" = "#9467bdAA",
"Mixed" = "#d62728AA",
"Uncategorized" = "#8c564bAA"
)) +  # Define cores das categorias
scale_x_discrete(limits = c("SBPT2020", "GLI2022")) +  # Renomeando os eixos
theme_minimal() +
labs(
title = "Comparação de Classificações: SBPT2020 vs GLI2022",
x = "Equação de Classificação",
y = "Frequência"
) +
theme(axis.text.y = element_blank(), axis.ticks.y = element_blank(), legend.position = "none")
## Warning in to_lodes_form(data = data, axes = axis_ind, discern = params$discern): Some strata appear at multiple axes.
## Warning in to_lodes_form(data = data, axes = axis_ind, discern = params$discern): Some strata appear at multiple axes.
## Warning in to_lodes_form(data = data, axes = axis_ind, discern = params$discern): Some strata appear at multiple axes.

# Criando o gráfico Alluvial
alluvial <- ggplot(data, aes(axis1 = class_bs, axis2 = class_bg, y = Freq)) +
geom_alluvium(aes(fill = class_bs), width = 0.2, alpha = 0.7) +  # Linhas de fluxo
geom_stratum(aes(fill = after_stat(stratum)), color = "black", size = 0.8, alpha = 1) +  # Blocos mais claros
geom_text(stat = "stratum", aes(label = after_stat(stratum)), size = 5) + # Rótulos
scale_fill_manual(values = c(
"Normal" = "#1f77b4AA",
"Obstructive" = "#ff7f0eAA",
"Restrictive" = "#2ca02c",
"Dysanapsis" = "#9467bd",
"Mixed" = "#d62728",
"Uncategorized" = "#8c564b"
)) +  # Define cores das categorias
scale_x_discrete(limits = c("SBPT2020", "GLI2022")) +  # Renomeando os eixos
theme_minimal() +
labs(
title = "Comparação de Classificações: SBPT2020 vs GLI2022",
x = "Equação de Classificação",
y = "Frequência"
) +
theme(axis.text.y = element_blank(), axis.ticks.y = element_blank(), legend.position = "none")
alluvial
## Warning in to_lodes_form(data = data, axes = axis_ind, discern = params$discern): Some strata appear at multiple axes.
## Warning in to_lodes_form(data = data, axes = axis_ind, discern = params$discern): Some strata appear at multiple axes.
## Warning in to_lodes_form(data = data, axes = axis_ind, discern = params$discern): Some strata appear at multiple axes.

FEV1FVC_Z & FVC_Z ALL BASELINE by SBPT2020

# Definir limites para os quadrados (ajustar manualmente se necessário)
bounds <- df %>%
    group_by(class_bs) %>%
    summarise(
        x_min = min(fev1fvc_z),
        x_max = max(fev1fvc_z),
        y_min = min(fvc_z),
        y_max = max(fvc_z),
        .groups = "drop"
    )

# Criar scatter plot com quadrados para cada classificação
ggplot(df, aes(x = fev1fvc_z, y = fvc_z, colour = class_bs, shape = class_bs)) +
    geom_point(size = 3, alpha = 0.8) +  # Ajuste do tamanho e transparência dos pontos
    geom_rect(data = bounds, aes(xmin = x_min, xmax = x_max, ymin = y_min, ymax = y_max, fill = class_bs),
              alpha = 0.2, inherit.aes = FALSE) +  # Adiciona caixas ao redor dos grupos
    labs(
        title = "Relação entre FEV1/FVC e FVC com Quadrados",
        x = "FEV1/FVC (Z-score)",
        y = "FVC (Z-score)",
        colour = "Classificação SBPT2020",
        shape = "Classificação SBPT2020"
    ) +
    scale_color_manual(values = c(
        "Normal" = "#1f77b4", 
        "Obstructive" = "#ff7f0e", 
        "Restrictive" = "#2ca02c", 
        "Dysanapsis" = "#9467bd", 
        "Mixed" = "#d62728", 
        "Uncategorized" = "#8c564b"
    )) +
    theme_minimal() +
    theme(
        legend.position = "right",
        legend.title = element_text(size = 12),
        legend.text = element_text(size = 10)
    )
## Warning: Using shapes for an ordinal variable is not advised

FEV1FVC_Z & FVC_Z ALL BASELINE by GLI2022 RACE-NEUTRAL

# Definir limites para os quadrados (ajustar manualmente se necessário)
bounds <- df %>%
    group_by(class_bg) %>%
    summarise(
        x_min = min(gnfev1fvc_z),
        x_max = max(gnfev1fvc_z),
        y_min = min(gnfvc_z),
        y_max = max(gnfvc_z),
        .groups = "drop"
    )

# Criar scatter plot com quadrados para cada classificação
ggplot(df, aes(x = gnfev1fvc_z, y = gnfvc_z, colour = class_bg, shape = class_bg)) +
    geom_point(size = 3, alpha = 0.8) +  # Ajuste do tamanho e transparência dos pontos
    geom_rect(data = bounds, aes(xmin = x_min, xmax = x_max, ymin = y_min, ymax = y_max, fill = class_bg),
              alpha = 0.2, inherit.aes = FALSE) +  # Adiciona caixas ao redor dos grupos
    labs(
        title = "Relação entre FEV1/FVC e FVC com Quadrados",
        x = "FEV1/FVC (GLI2022 Z-score)",
        y = "FVC (GLI2022 Z-score)",
        colour = "Classificação GLI2022 ",
        shape = "Classificação GLI2022 "
    ) +
    scale_color_manual(values = c(
        "Normal" = "#1f77b4", 
        "Obstructive" = "#ff7f0e", 
        "Restrictive" = "#2ca02c", 
        "Dysanapsis" = "#9467bd", 
        "Mixed" = "#d62728", 
        "Uncategorized" = "#8c564b"
    )) +
    theme_minimal() +
    theme(
        legend.position = "right",
        legend.title = element_text(size = 12),
        legend.text = element_text(size = 10)
    )
## Warning: Using shapes for an ordinal variable is not advised