1 Introducción

Effectiveness of mRNA vaccine boosters against infection with the SARS-CoV-2 omicron (B.1.1.529) variant in Spain: a nationwide cohort study

1.1 Background

The omicron (B.1.1.529) variant of SARS-CoV-2 has increased capacity to elude immunity and cause breakthrough infections. The aim of this study was to estimate the effectiveness of mRNA-based vaccine boosters (third dose) against infection with the omicron variant by age, sex, time since complete vaccination, type of primary vaccine, and type of booster.

1.2 Methods

In this population cohort study, we linked data from three population registries in Navarra (Vaccination Registry, Laboratory Results Registry, and BARDENA-Population registry) to select

  • community-dwelling individuals aged 40 years or older,
  • who completed their primary vaccine schedule at least 3 months before the start of follow-up, and
  • had not tested positive for SARS-CoV-2 since the start of the pandemic.

On each day between Jan 3, and Feb 6, 2022, we matched:

  • individuals who received a booster mRNA vaccine and controls of the same
    • sex,
    • age group,
    • health care zone (postal code),
    • type of vaccine (second one),
    • time since primary vaccination,
    • and number of previous tests (0,1,2,3+).
  • We estimated risk of laboratory-confirmed SARS-CoV-2 infection using the Kaplan-Meier method and compared groups using risk ratios (RR) and risk differences. Vaccine effectiveness was calculated as one minus RR.

1.2.1 Population data metadata

Etiqueta Variable
Código paciente (pac_unif_cod) id
genero gen
Fecha nacimiento (exclusion) fnac
grupo de edad, quinquenal salvo (0,40],(90,110] gedad
fecha defuncion fdef
migrante (pais de nacimiento no España) migran
area (grupo de paises) de nacimiento lapna
codigo aportacion farmaceutica (tsi) ctsi
vive en residencia true resn
unidad de convivencia, truncado (8 ó mas) ucn8
renta seccion censal categorizada -promedio 2011-2016- rentasc
codigo de zona basica -por centro de atencion- czbs
profesional sociosanitario true prfss
profesional educativo true educa
vulnerable vuln
dependiente true depe
demencia deme
diabetes diab
enf. autoinmune ainm
coronariopatia corp
insuficiencia renal cronica irc
enf pulmonar obstructiva cronica-EPOC epoc
hiperlipemia hlip
hipertension arterial (hta) hta
ictus ictu
obesidad (imc>30) obes
indice masa corporal (imc) imc
indice masa corporal, categorias cimc
recibe vacuna gripe campaña 2020 vgp20
recibe vacuna gripe campaña 2021 vgp21
tipo vacuna covid 1 tvc1
vacunacion incompleta true vcix
fecha vacuna covid 1 fvc1
tipo vacuna covid 2 tvc2
fecha vacuna covid 2 fvc2
tipo vacuna covid 3 tvc3
fecha vacuna covid 3 fvc3
infeccion covid si/no (ref junio 2022) cvsn
fecha infeccion covid 1 fcv1
fecha infeccion covid 2 fcv2
fecha primer ingreso hospitalario fih1
ingreso UCI si/no ucisn
fecha ingreso UCI fuci

1.3 Time series of exposure and outcome (T0= Jan 3, 2022)

2 Programación

2.1 f_tabla0: genera la tabla de análisis inicial

  • parametro: fecha 0 de arranque -primera cohorte-(por defecto 2022-01-14)
  • entrada:
      1. tabla de poblacion-vacunas-infecciones: pobana
      1. tabla de test previos
  • salida: tabla de analisis * a la que se agrega el numero de test negativos previos (0,1,2,3+) * filtra y excluye aquellos con infeccion previa/ # sociosanitario/ # educación/ # viven en residencia/ # dependientes/ # zbs desconocida * selecciona variables para el analisis, pasando a numericas las candidatas para el matching
f_tabla0<-function(fecha0="2022-01-14", file_Rdata_poblacion="Rdata/pobana.Rdata",file_Rdata_test="Rdata/test220613.Rdata") {
  fecha0=ymd(fecha0)
  print("Asegurate de que Knit Directory es Current working directory")
  if (!"pobana"%in% ls(envir = .GlobalEnv)){
    load(file_Rdata_poblacion,envir = .GlobalEnv)
  }
  if (!"test220613"%in% ls(envir = .GlobalEnv)){
  load(file_Rdata_test,envir = .GlobalEnv )
  }
  get("pobana", envir = .GlobalEnv)->pobana
  get("test220613", envir = .GlobalEnv)->test220613
  print(paste("n de partida :",nrow(pobana)))
  print("Excluyendo: socio y/o sanitarios...") 
  pobana %>% filter(is.na(prfss) | prfss!=TRUE)->pobana
    print(paste("n :",nrow(pobana)))
  print("Excluyendo: trabj educacion...") 
    pobana %>% filter(is.na(educa) | educa!=TRUE)->pobana 
    print(paste("n :",nrow(pobana)))  
  print("Excluyendo: viven en residencia...") 
  pobana %>% filter(is.na(resn) | resn!=TRUE)->pobana
    print(paste("n :",nrow(pobana)))
  print("Excluyendo:dependientes...")   
  pobana %>% filter(is.na(depe) | depe!=TRUE)->pobana
    print(paste("n :",nrow(pobana)))
  print("Excluyendo: zona basica desconocida...")   
  pobana %>% filter(!is.na(czbs))->pobana 
    print(paste("n :",nrow(pobana)))
  print("Excluyendo: tuvo infección previa...")
  pobana %>% filter(cvsn==0|fcv1>=ymd(fecha0))->pobana
    print(paste("n :",nrow(pobana)))
  print("Excluyendo: no finalizo vacunacion completa 90 dias antes...")
  pobana %>% filter(fecha0-fvc2>90)->tabla_0_pobana
     print(paste("n :",nrow(tabla_0_pobana))) 
  print("calculando tests negativos previos...")
  test220613%>% filter(id %in% tabla_0_pobana$id &tpos==FALSE) %>% group_by(id)%>% summarise(n_0=max(n)) %>% right_join(tabla_0_pobana, by="id") %>% mutate(n_0=ifelse(is.na(n_0),0,ifelse(n_0>3,3,n_0)), tv2=as.numeric(ymd(fecha0)-fvc2))->pobana_n_tabla0
  
 pobana_n_tabla0 <- pobana_n_tabla0 %>% mutate(
    genn=as.numeric(gen)-1,
    gedadn=as.numeric(gedad),
    czbs=as.numeric(czbs),
    migran=as.numeric(migran),
    vgp20=as.numeric(vgp20),
    vgp21=as.numeric(vgp21),
    tvc2n=as.numeric(as.factor(tvc2))) %>% dplyr::select(id,fvc2,fvc3,fdef,fcv1,fih1,genn,gedad,gedadn,czbs,n_0,tvc2n,tvc3,tv2,migran,vgp20,vgp21,ucn8) 
 
  return(pobana_n_tabla0)
}


# lo aplico empleando como inicio 2022-01-03": genera el flow_chart
 

 f_tabla0(fecha0="2022-01-03")->tab0220103
## [1] "Asegurate de que Knit Directory es Current working directory"
## [1] "n de partida : 645209"
## [1] "Excluyendo: socio y/o sanitarios..."
## [1] "n : 622490"
## [1] "Excluyendo: trabj educacion..."
## [1] "n : 490730"
## [1] "Excluyendo: viven en residencia..."
## [1] "n : 481242"
## [1] "Excluyendo:dependientes..."
## [1] "n : 474254"
## [1] "Excluyendo: zona basica desconocida..."
## [1] "n : 460039"
## [1] "Excluyendo: tuvo infección previa..."
## [1] "n : 373412"
## [1] "Excluyendo: no finalizo vacunacion completa 90 dias antes..."
## [1] "n : 304370"
## [1] "calculando tests negativos previos..."

2.2 f_exclusion: criterios de exclusión tiempo-dependientes

  • parametro: datos + fecha 0 de arranque -primera cohorte-

  • entrada: tabla filtrada por f_tabla0

  • salida: tabla de analisis * filtra y excluye aquellos con:

                   + con fecha de 3 dosis anterior al t0/
                   + fallecidos o /
                   + infectados previamente/
f_exclusion= function(data, fecha0) {
data= data %>% filter(is.na(fvc3) | fecha0 <= fvc3,
           is.na(fcv1) | fecha0 < fcv1,
           is.na(fdef) |  fecha0 < fdef)
return(data)           
}

2.3 f_exposicionseguimiento: criterios de exclusión tiempo-dependientes

  • parametro: datos + fecha 0 de arranque -primera cohorte- + duración del seguimiento

  • entrada: tabla filtrada por f_tabla0 y f_exclusion

  • salida: tabla de analisis:

          * genera variable `exposicion`/
          * recoge fecha inicio de la cohorte/
          * genera fecha fin seguimiento/censura del sujeto/
f_exposicionseguimiento= function(data, fecha0, n=60) {
  data= data %>% mutate(
    exposicion = if_else(fecha0 == fvc3 & !is.na(fvc3), 1, 0),
    date_start = fecha0,
    date_end = fecha0 + n,
    date_end = ifelse(!is.na(fdef) & fdef < date_end, fdef, date_end),
    date_end = ifelse(exposicion == 0 & !is.na(fvc3) & fvc3 < date_end,  fvc3, date_end),
    date_end = as.Date.numeric(date_end, origin ="1970-01-01" )
  )
 return(data) 
}

2.4 f_matching: creacion de parejas

Esta es la función que pretende ser más genérica

  • parametros:

    • datos
    • fecha 0 de arranque -primera cohorte-
    • vector de variables para el emparejamiento
    • variable de resultados (por defecto en este caso fecha primera infección)
    • f_exclusion
    • f_exposicionseguimiento
  • entrada: tabla filtrada por f_tabla0 y f_exclusion

  • salida: tabla de analisis:

         + genera variable `exposicion`/
         + recoge fecha inicio de la cohorte/
         + genera fecha fin seguimiento/censura del sujeto/

    los motivos de censura de una pareja son: + fallece + el miembro no expuesto se vacuna

f_matching <- function(fecha0, data, vector_match, out="fcv1", f_exclusion, f_exposicionseguimiento) {
  fecha0=ymd(fecha0)
  print(fecha0)
  print(c("out= ",out))
x= data  %>% f_exclusion(fecha0) %>% f_exposicionseguimiento(fecha0) 

try({
    greedymatch <- Matching::Match(Tr = x$exposicion, M = 1, X = x[vector_match], exact = TRUE, ties = FALSE)
    a <- x[unlist(greedymatch[c("index.treated", "index.control")]),]
    par <- rep(1:(nrow(a)/2), 2)
  
    df=bind_cols(par = par, a) %>%
      group_by(par) %>%
      mutate(date_end = min(date_end),
             # creo la variable de resultado)
             caso = ifelse(!!as.name(out) <= date_end & !is.na(!!as.name(out)), 1, 0),
             date_end = ifelse(caso==1, !!as.name(out), date_end),
             date_end = as.Date.numeric(date_end, origin ="1970-01-01")) %>%
      data.frame() 
    print(c("n= ",nrow(df)))
    print(c("casos= ",df %>% filter(caso==1) %>% nrow()))
  }
  )
}

2.4.1 Sobre el vector de variables de emparejamiento ej

Referido al dia 2022-01-14, y aplicando el vector de cruze:

  • edad (“gedadn”),
  • nº de test negativos previos (“n_0”): n=12894 individuos

al emparejar por:

  + tiempo desde la segunda vacuna(tv2)-> 11469, 
  + genero->10542, 
  + zbs-> 10542, 
  + migrante->9804, 
  + vacunas de grupe(vgp20 + vgp21)-> 8180, 
  + tipo de segunda vacuna(tvc2)->8020,
  + numero de convivientes (truncado en 8): ucn8=4364

2.5 construcción de las diversas cohortes

# se define el vector de emparejamiento
vector_match=c("czbs","genn" ,"gedadn", "n_0", "tvc2n", "tv2", "migran","vgp20","vgp21","ucn8")

# fecha de inicio y fin: secuencia de fechas
fecha_inicio=dmy("03/01/2022")
fecha_fin=dmy("05/01/2022") 
fechas <- seq.Date(from = fecha_inicio, to = fecha_fin, by = "days")

# se genera una lista con las diversas cohortes diarias 
listaej <- lapply(fechas,
                f_matching, 
                data=tab0220103,
                vector_match=vector_match,
                out="fcv1",
                f_exclusion=f_exclusion,
                f_exposicionseg=f_exposicionseg)
## [1] "2022-01-03"
## [1] "out= " "fcv1" 
## [1] "n= "  "1072"
## [1] "casos= " "83"     
## [1] "2022-01-04"
## [1] "out= " "fcv1" 
## [1] "n= " "474"
## [1] "casos= " "28"     
## [1] "2022-01-05"
## [1] "out= " "fcv1" 
## [1] "n= " "702"
## [1] "casos= " "36"

2.6 f_analisis:

  • parametros:
  • entrada: lista de cohortes con secuencia de t0-diaria
  • salida:
    + Kaplan-Meier -grafico + Cox- RR- efectividad: 1-RR
f_analisis <- function(lista) {
  base_analisis <- bind_rows(lista[sapply(lista, class) == "data.frame"])
 print(table(base_analisis$caso))
 
  base_analisis <- base_analisis %>% mutate(survtime=date_end-date_start)
fit <- survfit(Surv(survtime, caso) ~ exposicion, data=base_analisis)
print(ggsurvplot(fit, data = base_analisis, xlab="days of follow-up",
           ylab="Survival probability",
           main="Product-Limit Survival Estimates", ylim=c(0.85,1), risk.table = TRUE, conf.int = TRUE))

### cox
cpp=coxph(formula = Surv(survtime, caso) ~ exposicion, data = base_analisis)
print("# Estimated effectiveness was 43·6% (40·0–47·1) -toda España-")
   print(paste0("# Estimated effectiveness in Navarra was :",round((1-exp(coef(cpp)))*100,1), "% (", round((1-exp(confint((cpp)))[2])*100,1),"-", round((1-exp(confint((cpp)))[1])*100,1),")"))
}

f_analisis(lista)
## 
##     0     1 
## 44922  2504

## [1] "# Estimated effectiveness was 43·6% (40·0–47·1) -toda España-"
## [1] "# Estimated effectiveness in Navarra was :43.5% (38.7-47.9)"