This tutorial is dedicated to the cleaning and transformation of registry data. The goal is to create a homogeneous dataset that can be exploited by registry users or data analysis professionals collaborating in future studies. The initial dataset, on which the code contained in this page is executed, is the result of the joining forms document included in another tutorial.
The tutorial is structured according to the form to which the created variable would belong. The code is included in a drop-down format, and an example of the dataset obtained with each execution is included for the variables of interest. All the code is based on functions belonging to Rstats and the Tidyverse metalibrary. The values used as cut-off points in the design of categorical variables from numerical variables are based on the clinical interest and/or distribution of the data.
Set up
- Install and load the libraries you will need.
- Load the dataset containing the unified file with the lung resections of your department. You will have to adapt the path, inside the
read_xlsx
function in the last line of the following code snippet, to the directory on your computer where you have your xlsx data file.
Code
list.of.packages <- c("tidyverse", "readxl", "lubridate", "kableExtra", "gt")
new.packages <- list.of.packages[!(list.of.packages %in%
installed.packages()[,"Package"])]
if(length(new.packages)) install.packages(new.packages)
lapply(list.of.packages, require, character.only = TRUE)
data <- readRDS(file = "../data/datosClinicos_rpa.RDS")
Preoperative Period
Age
- Creation of
edad_del_paciente_a_la_fecha_de_intervencion_cat
as a way of categorising patient age according to the distribution of data.
Code
data$edad_del_paciente_a_la_fecha_de_intervencion_cat <-
cut(as.numeric(data$edad_del_paciente_a_la_fecha_de_intervencion),
breaks = c(0,40,50,60,65,70,75,80,85,100),
labels = c("< 40","[40 - 50)",
"[50 - 60)",
"[60 - 65)",
"[65-70)",
"[70 - 75)",
"[75 - 80)",
"[80 - 85)",
">= 85"),
right = FALSE)
set.seed(1)
data %>%
select(edad_del_paciente_a_la_fecha_de_intervencion,
edad_del_paciente_a_la_fecha_de_intervencion_cat) %>%
slice_sample(n = 5) %>%
gt() %>%
opt_row_striping(row_striping = TRUE)
edad_del_paciente_a_la_fecha_de_intervencion | edad_del_paciente_a_la_fecha_de_intervencion_cat |
---|---|
65 | [65-70) |
43 | [40 - 50) |
NA | NA |
69 | [65-70) |
66 | [65-70) |
Bode Mass Index
- Categorisation of body mass index according to ranges of clinical interest through the creation of
indice_de_masa_corporal_cat
Code
data$indice_de_masa_corporal_cat <-
cut(as.numeric(data$indice_de_masa_corporal),
breaks = c(0,18.5,25,30,35,40, 60),
labels = c("<18.5: Bajo",
"[18.5 - 25): Normal",
"[25 - 30): Sobrepeso",
"[30 - 35): Obesidad I",
"[35 - 40): Obesidad II",
">= 40: Obesidad III"),
right = FALSE)
set.seed(1)
data %>%
select(indice_de_masa_corporal, indice_de_masa_corporal_cat) %>%
slice_sample(n = 5) %>%
gt() %>%
opt_row_striping(row_striping = TRUE)
indice_de_masa_corporal | indice_de_masa_corporal_cat |
---|---|
NA | NA |
18.42 | <18.5: Bajo |
27.28 | [25 - 30): Sobrepeso |
26.33 | [25 - 30): Sobrepeso |
20.83 | [18.5 - 25): Normal |
Functional Status
- Categorisation of preoperative FEV1, FVC and DLCO values as percentages. The variables created are
fev1_percent_cat
,cvf_percent_cat
anddlco_percent_cat
.
Code
data$fev1_percent_cat <-
cut(as.numeric(data$fev1_percent),
breaks = c(0,50,60,70,80,90,100,200),
labels = c("< 50",
"[50 - 60)",
"[60 - 70)",
"[70 - 80)",
" [80 - 90)",
"[90 - 100)",
">= 100"),
right = FALSE)
data$cvf_percent_cat <-
cut(as.numeric(data$cvf_percent),
breaks = c(0,50,60,70,80,90,100,200),
labels = c("< 50",
"[50 - 60)",
"[60 - 70)",
"[70 - 80)",
"[80 - 90)", "[90 - 100)", ">= 100"),
right = FALSE)
data$dlco_percent_cat <-
cut(as.numeric(data$dlco_percent),
breaks = c(0,50,60,70,80,90,100,200),
labels = c("< 50",
"[50 - 60)",
"[60 - 70)",
"[70 - 80)",
"[80 - 90)",
"[90 - 100)",
">= 100"),
right = FALSE)
set.seed(1)
data %>%
select(cvf_percent, cvf_percent_cat, fev1_percent,
fev1_percent_cat, dlco_percent, dlco_percent_cat) %>%
slice_sample(n = 5) %>%
gt() %>%
opt_row_striping(row_striping = TRUE)
cvf_percent | cvf_percent_cat | fev1_percent | fev1_percent_cat | dlco_percent | dlco_percent_cat |
---|---|---|---|---|---|
110 | >= 100 | 64 | [60 - 70) | 59 | [50 - 60) |
100 | >= 100 | 88 | [80 - 90) | 74 | [70 - 80) |
93 | [90 - 100) | 96 | [90 - 100) | 91 | [90 - 100) |
80 | [80 - 90) | 76 | [70 - 80) | NA | NA |
102 | >= 100 | 91 | [90 - 100) | 49 | < 50 |
Surgery
Surgical Duration
- Creation of the variable
tiempo_quirurgico_min_cat
as a way of categorising the duration of surgery in 30 min intervals.
Code
data$tiempo_quirurgico_min_cat <-
cut(as.numeric(data$tiempo_quirurgico_min),
breaks = c(0,60,90,120,150,180,210,240,300,1000),
labels = c("< 60","[60 - 90)", "[90 - 120)", "[120 - 150)",
"[150 - 18)", "[180 - 210)", "[210 - 240)",
"[240 - 300)" , ">= 300"),
right = FALSE)
set.seed(1)
data %>%
select(tiempo_quirurgico_min, tiempo_quirurgico_min_cat) %>%
slice_sample(n = 5) %>%
gt() %>%
opt_row_striping(row_striping = TRUE)
tiempo_quirurgico_min | tiempo_quirurgico_min_cat |
---|---|
NA | NA |
245 | [240 - 300) |
NA | NA |
150 | [150 - 18) |
210 | [210 - 240) |
Number of Segments
- Creation of the variable
segmentos_pulmon_a_operar
which represents the number of lung segments to be operated on. Consideration is given to the laterality of the procedure, the number of segments removed in previous surgeries and the completeness of the current procedure. - Creation of the variable
total_segmentos
which represents the total number of segments of the patient before surgery, taking into account only the number of segments previously removed.
Code
# Creación de la Variable Segmentos del Pulmón a Operar a partir de las variables: cirugía torácica previa,
# número de segmentos resecados en cirugías previas, lateralidad del procedimiento y carácter de compleción del procedimiento.
data <- data %>%
mutate(segmentos_pulmon_a_operar =
case_when(
data$lateralidad == "Derecha" & data$cirugia_toracica_previa %in% c("No", "Sí, Contralateral", NA) ~ 10,
data$lateralidad == "Izquierda" & data$cirugia_toracica_previa %in% c("No", "Sí, Contralateral", NA) ~ 9,
data$lateralidad == "Derecha" & data$numero_de_segmentos_resecados_previamente %in% c("0", NA) ~ 10,
data$lateralidad == "Izquierda" & data$numero_de_segmentos_resecados_previamente %in% c("0",NA) ~ 9,
data$lateralidad == "Derecha" & !(data$numero_de_segmentos_resecados_previamente %in% c("0",NA)) &
data$cirugia_toracica_previa %in% c("Sí, Homolateral") ~
10 - as.numeric(data$numero_de_segmentos_resecados_previamente),
data$lateralidad == "Izquierda" & !(data$numero_de_segmentos_resecados_previamente %in% c("0",NA)) &
data$cirugia_toracica_previa %in% c("Sí, Homolateral") ~
9 - as.numeric(data$numero_de_segmentos_resecados_previamente),
data$lateralidad == "Derecha" & !(data$numero_de_segmentos_resecados_previamente %in% c("0",NA)) &
data$cirugia_toracica_previa %in% c("Sí, Bilateral") & (data$tipo_de_bi_lobectomia %in%
c("De Compleción después de una Segmentectomía Anatómica sobre el lóbulo o los lóbulos implicados") |
!(data$tipo_de_neumonectomia %in% c("Convencional", NA))) ~
10 - as.numeric(data$numero_de_segmentos_resecados_previamente),
data$lateralidad == "Izquierda" & !(data$numero_de_segmentos_resecados_previamente %in% c("0",NA)) &
data$cirugia_toracica_previa %in% c("Sí, Bilateral") & (data$tipo_de_bi_lobectomia %in%
c("De Compleción después de una Segmentectomía Anatómica sobre el lóbulo o los lóbulos implicados") |
!(data$tipo_de_neumonectomia %in% c("Convencional", NA))) ~
9 - as.numeric(data$numero_de_segmentos_resecados_previamente)
)
)
data <- data %>%
mutate(total_segmentos=
case_when(
data$cirugia_toracica_previa %in% c("No", NA) |
data$numero_de_segmentos_resecados_previamente %in% c("0",NA) ~ 19,
!(data$cirugia_toracica_previa %in% c("No", NA)) ~
19 - as.integer(data$numero_de_segmentos_resecados_previamente)
)
)
set.seed(2)
data %>%
filter(cirugia_toracica_previa != "No") %>%
select (
cir_tor_previa=cirugia_toracica_previa,
seg_resecados_previamente=numero_de_segmentos_resecados_previamente,
lateralidad,
tipo_lobectomia=tipo_de_bi_lobectomia,
seg_pulmon_a_operar=segmentos_pulmon_a_operar,
total_seg=total_segmentos) %>%
slice_sample(n = 5) %>%
gt() %>%
gt::cols_width(
cir_tor_previa~ px(140),
seg_resecados_previamente ~px(170),
lateralidad~px(100),
tipo_lobectomia~px(120),
seg_pulmon_a_operar ~px(160),
total_seg~px(100)) %>%
gt::tab_footnote(
"Nota: En caso de valores faltantes para `cirugía torácica previa` o
`numero de segmentos previamente resecados`, se considera que ambos valores
son `No` y `0`, respectivamente.") %>%
opt_row_striping(row_striping = TRUE)
cir_tor_previa | seg_resecados_previamente | lateralidad | tipo_lobectomia | seg_pulmon_a_operar | total_seg |
---|---|---|---|---|---|
Sí, Homolateral | 5 | Derecha | Convencional | 5 | 14 |
Sí, Contralateral | 5 | Derecha | Convencional | 10 | 14 |
Sí, Contralateral | 5 | Izquierda | NA | 9 | 14 |
Sí, Bilateral | 0 | Izquierda | De Compleción después de una Resección Atípica sobre el lóbulo o los lóbulos implicados | 9 | 19 |
Sí, Contralateral | 0 | Derecha | Convencional | 10 | 19 |
Nota: En caso de valores faltantes para `cirugía torácica previa` o `numero de segmentos previamente resecados`, se considera que ambos valores son `No` y `0`, respectivamente. |
PPO Values
- Calculation of ppo values
fev1_ppo_percent
,cvf_ppo_percent
ydlco_ppo_percent
based on the number of functioning and non-functioning segments removed, as well as the percentage of perfusion of the lung to be operated. In case of NO pneumonectomy and perfusion scan, the calculation takes into account the laterality of the procedure and the number of lung segments to be operated.
Code
# FEV1
data <- data %>% mutate(
fev1_ppo_percent = case_when(
is.na(data$porcentaje_de_perfusion_del_pulmon_intervenido) ~
(
as.numeric(data$fev1_percent) -
(as.numeric(numero_de_segmentos_funcionantes_resecados) * as.numeric(data$fev1_percent)/
(data$total_segmentos - as.numeric(data$numero_de_segmentos_no_funcionantes_resecados))
)
),
data$procedimiento_pulmonar == "Neumonectomía" & !is.na(data$porcentaje_de_perfusion_del_pulmon_intervenido) ~
(
as.numeric(data$fev1_percent) - (as.numeric(data$fev1_percent) * 0.01 * as.numeric(data$porcentaje_de_perfusion_del_pulmon_intervenido))
),
data$procedimiento_pulmonar != "Neumonectomía" & !is.na(data$porcentaje_de_perfusion_del_pulmon_intervenido) ~
(
as.numeric(data$fev1_percent) - (as.numeric(numero_de_segmentos_funcionantes_resecados) * as.numeric(data$fev1_percent) * 0.01 * as.numeric(data$porcentaje_de_perfusion_del_pulmon_intervenido))/
(data$segmentos_pulmon_a_operar)
)
)
)
data$fev1_ppo_percent <- round(data$fev1_ppo_percent,1)
# CVF
data <- data %>% mutate(
cvf_ppo_percent = case_when(
is.na(data$porcentaje_de_perfusion_del_pulmon_intervenido) ~
(
as.numeric(data$cvf_percent) -
(as.numeric(numero_de_segmentos_funcionantes_resecados) * as.numeric(data$cvf_percent)/
(data$total_segmentos - as.numeric(data$numero_de_segmentos_no_funcionantes_resecados))
)
),
data$procedimiento_pulmonar == "Neumonectomía" & !is.na(data$porcentaje_de_perfusion_del_pulmon_intervenido) ~
(
as.numeric(data$cvf_percent) - (as.numeric(data$cvf_percent) * 0.01 * as.numeric(data$porcentaje_de_perfusion_del_pulmon_intervenido))
),
data$procedimiento_pulmonar != "Neumonectomía" & !is.na(data$porcentaje_de_perfusion_del_pulmon_intervenido) ~
(
as.numeric(data$cvf_percent) - (as.numeric(numero_de_segmentos_funcionantes_resecados) * as.numeric(data$cvf_percent) * 0.01 * as.numeric(data$porcentaje_de_perfusion_del_pulmon_intervenido))/
(data$segmentos_pulmon_a_operar)
)
)
)
data$cvf_ppo_percent <- round(data$cvf_ppo_percent,1)
# DLCO
data <- data %>% mutate(
dlco_ppo_percent = case_when(
is.na(data$porcentaje_de_perfusion_del_pulmon_intervenido) ~
(
as.numeric(data$dlco_percent) -
(as.numeric(numero_de_segmentos_funcionantes_resecados) * as.numeric(data$dlco_percent)/
(data$total_segmentos - as.numeric(data$numero_de_segmentos_no_funcionantes_resecados))
)
),
data$procedimiento_pulmonar == "Neumonectomía" & !is.na(data$porcentaje_de_perfusion_del_pulmon_intervenido) ~
(
as.numeric(data$dlco_percent) - (as.numeric(data$dlco_percent) * 0.01 * as.numeric(data$porcentaje_de_perfusion_del_pulmon_intervenido))
),
data$procedimiento_pulmonar != "Neumonectomía" & !is.na(data$porcentaje_de_perfusion_del_pulmon_intervenido) ~
(
as.numeric(data$dlco_percent) - (as.numeric(numero_de_segmentos_funcionantes_resecados) * as.numeric(data$dlco_percent) * 0.01 * as.numeric(data$porcentaje_de_perfusion_del_pulmon_intervenido))/
(data$segmentos_pulmon_a_operar)
)
)
)
data$dlco_ppo_percent <- round(data$dlco_ppo_percent,1)
set.seed(1)
data %>% select(fev1_percent, fev1_ppo_percent, cvf_percent, cvf_ppo_percent) %>%
slice_sample(n = 5) %>%
gt() %>%
opt_row_striping(row_striping = TRUE)
fev1_percent | fev1_ppo_percent | cvf_percent | cvf_ppo_percent |
---|---|---|---|
64 | 53.9 | 110 | 92.6 |
88 | 82.8 | 100 | 94.1 |
96 | NA | 93 | NA |
76 | 64.0 | 80 | 67.4 |
91 | 67.1 | 102 | 75.2 |
Diagnosis
Tumour Size
- Tumour size categorisation. This characteristic,
tamaño_tumoral_mm_cat
, only includes patients with primary malignant lung tumours.
Code
#|label: tamaño tumoral
data$tamano_tumoral_mm_cat <-
cut(as.numeric(data$tamano_tumoral_mm),
breaks = c(0,10,20,30,40,50,60,70,80,90,100,1000),
labels = c("< 10","[10 - 20)", "[20 - 30)", "[30 - 40)",
"[40 - 50)", "[50 - 60)", "[60 - 70)", "[70 - 80)",
"[80 - 90)", "[90 - 100)", ">= 100"),
right = FALSE)
set.seed(1)
data %>%
filter(grupo_diagnostico == "Tumoral Maligno Primario") %>%
select(grupo_diagnostico,tamano_tumoral_mm, tamano_tumoral_mm_cat) %>%
slice_sample(n = 5) %>%
gt() %>%
opt_row_striping(row_striping = TRUE)
grupo_diagnostico | tamano_tumoral_mm | tamano_tumoral_mm_cat |
---|---|---|
Tumoral Maligno Primario | 18 | [10 - 20) |
Tumoral Maligno Primario | 30 | [30 - 40) |
Tumoral Maligno Primario | 63 | [60 - 70) |
Tumoral Maligno Primario | 25 | [20 - 30) |
Tumoral Maligno Primario | 22 | [20 - 30) |
Mediastinal Staging
Code
data <- data %>% mutate(estadificacion_invasiva = case_when(
data$ebus %in% c("N0", "N1", "N2", "N3", "Ni") & data$estadificacion_quirurgica_del_mediastino %in% c("N0", "N1", "N2", "N3", "Ni") ~ "Both",
data$ebus %in% c("N0", "N1", "N2", "N3", "Ni") & data$estadificacion_quirurgica_del_mediastino == "No Realizado" ~ "EBUS",
data$ebus == "No Realizado" & data$estadificacion_quirurgica_del_mediastino %in% c("N0", "N1", "N2", "N3", "Ni") ~ "Surgical",
data$estadificacion_invasiva_ganglionar == "No" ~ "None"))
data$estadificacion_invasiva <- as.factor(data$estadificacion_invasiva)
set.seed(1)
data %>%
filter(estadificacion_invasiva_ganglionar == "Sí") %>%
select(ebus, estadificacion_quirurgica_del_mediastino, estadificacion_invasiva) %>%
slice_sample(n = 5) %>%
gt() %>%
opt_row_striping(row_striping = TRUE)
ebus | estadificacion_quirurgica_del_mediastino | estadificacion_invasiva |
---|---|---|
N2 | No Realizado | EBUS |
N2 | No Realizado | EBUS |
N0 | Ni | Both |
N0 | No Realizado | EBUS |
N0 | N0 | Both |
Clinical Stage
- Clinical stage according to 8th Ed. TNM
estadio_clinico
. Includes patients with a primary malignant lung tumour that is not compatible with recurrence of a previous known tumour.
Code
#|label: tnm_clinico
data <- data %>% mutate(
estadio_clinico = case_when(
data$descriptor_m_clinico %in% c("M1c") ~ "IVB",
data$descriptor_m_clinico %in% c("M1a", "M1b") ~ "IVA",
data$descriptor_n_clinico %in% c("N3") &
data$descriptor_t_clinico %in% c("T3", "T4") ~ "IIIC",
data$descriptor_n_clinico %in% c("N3") &
data$descriptor_t_clinico %in% c("T0", "T1a", "T1b", "T1c", "T2a", "T2b") ~ "IIIB",
data$descriptor_n_clinico %in% c("N2") &
descriptor_t_clinico %in% c("T3", "T4") ~ "IIIB",
data$descriptor_n_clinico %in% c("N2") &
descriptor_t_clinico %in% c("T0", "T1a", "T1b", "T1c", "T2a", "T2b") ~ "IIIA",
data$descriptor_n_clinico %in% c("N1") &
descriptor_t_clinico %in% c("T3", "T4") ~ "IIIA",
data$descriptor_n_clinico %in% c("N0") &
descriptor_t_clinico %in% c("T4") ~ "IIIA",
data$descriptor_n_clinico %in% c("N1") ~ "IIB",
data$descriptor_n_clinico %in% c("N0") &
descriptor_t_clinico %in% c("T3") ~ "IIB",
data$descriptor_n_clinico %in% c("N0") &
descriptor_t_clinico %in% c("T2b") ~ "IIA",
data$descriptor_n_clinico %in% c("N0") &
descriptor_t_clinico %in% c("T2a") ~ "IB",
data$descriptor_n_clinico %in% c("N0") &
descriptor_t_clinico %in% c("T1c") ~ "IA3",
data$descriptor_n_clinico %in% c("N0") &
descriptor_t_clinico %in% c("T1b") ~ "IA2",
data$descriptor_n_clinico %in% c("N0") &
data$descriptor_t_clinico == "T1a" ~ "IA1",
data$descriptor_n_clinico %in% c("N0") &
data$descriptor_t_clinico == "T0" ~ "0",
)
)
set.seed(1)
data %>%
filter(caracter_de_la_enfermedad == "Nueva Enfermedad sin Antecedente de otro Tumor Maligno Primario") %>%
select(descriptor_t_clinico, descriptor_n_clinico, descriptor_m_clinico, estadio_clinico) %>%
slice_sample(n = 5) %>%
gt() %>%
opt_row_striping(row_striping = TRUE)
descriptor_t_clinico | descriptor_n_clinico | descriptor_m_clinico | estadio_clinico |
---|---|---|---|
T3 | N0 | M0 | IIB |
T1b | N0 | M0 | IA2 |
T1b | N0 | M0 | IA2 |
T1c | N0 | M0 | IA3 |
T1c | N0 | M0 | IA3 |
Pathological Stage
- Pathological stage according to 8th Ed. TNM ‘estadio_patologico’. Includes patients with a primary malignant lung tumour that is not compatible with recurrence of a previous known tumour.
Code
data <- data %>% mutate(
estadio_patologico = case_when(
data$descriptor_m_patologico %in% c("M1c") ~ "IVB",
data$descriptor_m_patologico %in% c("M1a", "M1b") ~ "IVA",
data$descriptor_n_patologico %in% c("N3") &
data$descriptor_t_patologico %in% c("T3", "T4") ~ "IIIC",
data$descriptor_n_patologico %in% c("N3") &
data$descriptor_t_patologico %in% c("T0", "T1a", "T1b", "T1c", "T2a", "T2b") ~ "IIIB",
data$descriptor_n_patologico %in% c("N2") &
descriptor_t_patologico %in% c("T3", "T4") ~ "IIIB",
data$descriptor_n_patologico %in% c("N2") &
descriptor_t_patologico %in% c("T0", "T1a", "T1b", "T1c", "T2a", "T2b") ~ "IIIA",
data$descriptor_n_patologico %in% c("N1") &
descriptor_t_patologico %in% c("T3", "T4") ~ "IIIA",
data$descriptor_n_patologico %in% c("N1") ~ "IIB",
data$descriptor_n_patologico %in% c("N0") &
descriptor_t_patologico %in% c("T4") ~ "IIIA",
data$descriptor_n_patologico %in% c("N0") &
descriptor_t_patologico %in% c("T3") ~ "IIB",
data$descriptor_n_patologico %in% c("N0") &
descriptor_t_patologico %in% c("T2b") ~ "IIA",
data$descriptor_n_patologico %in% c("N0") &
descriptor_t_patologico %in% c("T2a") ~ "IB",
data$descriptor_n_patologico %in% c("N0") &
descriptor_t_patologico %in% c("T1c") ~ "IA3",
data$descriptor_n_patologico %in% c("N0") &
descriptor_t_patologico %in% c("T1b") ~ "IA2",
data$descriptor_n_patologico %in% c("N0") &
data$descriptor_t_patologico == "T1a" ~ "IA1",
data$descriptor_n_patologico %in% c("N0") &
data$descriptor_t_patologico == "T0" ~ "0",
)
)
set.seed(1)
data %>%
filter(caracter_de_la_enfermedad == "Nueva Enfermedad sin Antecedente de otro Tumor Maligno Primario") %>%
filter(!is.na(descriptor_t_patologico)) %>%
select(descriptor_t_patologico, descriptor_n_patologico, descriptor_m_patologico, estadio_patologico) %>%
slice_sample(n = 5) %>%
gt() %>%
opt_row_striping(row_striping = TRUE)
descriptor_t_patologico | descriptor_n_patologico | descriptor_m_patologico | estadio_patologico |
---|---|---|---|
T0 | N0 | M0 | 0 |
T1b | N0 | M0 | IA2 |
T1b | N0 | M0 | IA2 |
T2a | N0 | M0 | IB |
T2b | N0 | M0 | IIA |
Code
datosClinicos_rpa <- data