df.xx.yy for data frames, eg. df.qualtrics.raw, df.qualtrics.long mat.xx.yy for matrix list.xx.yy for lists, eg. list.design.choices tbl.xx.yy for tables, eg. tbl.COI.demographics.csv
3 Data import
In a first step, the necessary data are imported. The reports are called “COI Switzerland” and “Demographics Switzerland” in RedCap.
These include:
Demographic data
Medical Consumption Questionnaire (iMCQ) Switzerland, T0
# Import raw datasetsdf.COI.CH.raw <- rio::import("../data/COI_data.csv") # Cost of illness data (iMCQ, iPCQ, EQ5D5L)df.COI.demo.raw <- rio::import("../data/COI_demo_data.csv") # Dataset on demographics of participants at T0# Year of T0 is relevant for cost conversion. Therefore, this variable needs to be extracted from the demographics dataset# Use only rows with values from df.COI.CH.raw and the variable general_t0_date# Extract the `general_t0_date` for `t0_baseline_measur_arm_1`df.t0.date.data <- df.COI.CH.raw %>%filter(redcap_event_name =="t0_baseline_measur_arm_1") %>%select(general_id, general_t0_date)# Filter for rows with `t0_health_economic_arm_1` and merge `general_t0_date`df.COI.filtered <- df.COI.CH.raw %>%filter(redcap_event_name =="t0_health_economic_arm_1") %>%select(-general_t0_date) %>%left_join(df.t0.date.data , by ="general_id")# Filter df.COI.demo.raw to include only rows with non-NA values in demo_ch_t0_birthyeardf.COI.demo.filtered <- df.COI.demo.raw %>%filter(!is.na(demo_ch_t0_birthyear))
4 Coding reference datasets
Code
# reference dataset on how data is coded in the demographics dataset.df.demographics.coding <- rio::import("../coding/demographics_instrument_coding.csv")
5 Recalculation of costs
All costs are recalculated to report them in 2025 Swiss Francs or 2025 Belgium Euros.
We use the IMF dataset with the GDP deflator index and the PPP value for the different countries for the cost recalculation (Shemlit paper on CCEMG cost converter).
Code
df.IMF.conversion.raw <- rio::import("../reference data/IMF_cost_conversion_2025.csv", header =TRUE)df.IMF.conversion.raw <- df.IMF.conversion.raw %>%mutate(across(c("2023", "2024", "2025"), as.numeric))# Subset for Belgium and Switzerland for years 2023, 2024, 2025df.IMF.conversion.adapted <- df.IMF.conversion.raw %>%filter( ISO %in%c("BEL", "CHE") &`WEO Subject Code`%in%c("NGDP_D", "PPPEX") & (!is.na(`2023`) |!is.na(`2024`) |!is.na(`2025`)) # Keep rows where at least one year has a value ) %>%select(Country, ISO, `WEO Subject Code`, `Subject Descriptor`, `2023`, `2024`, `2025`)
# Subset data for Switzerlanddf.IMF.con.CH <- df.IMF.conversion.adapted %>%filter(Country =="Switzerland", `WEO Subject Code`%in%c("NGDP_D", "PPPEX")) %>%select(`WEO Subject Code`, `2023`, `2024`, `2025`) %>%pivot_longer(cols =`2023`:`2025`, names_to ="Year", values_to ="Value") %>%pivot_wider(names_from =`WEO Subject Code`, values_from ="Value") %>%rename(GDP = NGDP_D, PPP = PPPEX) %>%mutate(Year =as.numeric(Year))# Subset data for Belgiumdf.IMF.con.BE <- df.IMF.conversion.adapted %>%filter(Country =="Belgium", `WEO Subject Code`%in%c("NGDP_D", "PPPEX")) %>%select(`WEO Subject Code`, `2023`, `2024`, `2025`) %>%pivot_longer(cols =`2023`:`2025`, names_to ="Year", values_to ="Value") %>%pivot_wider(names_from =`WEO Subject Code`, values_from ="Value") %>%rename(GDP = NGDP_D, PPP = PPPEX) %>%mutate(Year =as.numeric(Year))# Function for inflation (Important variables: "Year T0" und "Price (CHF)")# Funktion zur Preisberechnungconvert_price_to_2025 <-function(price_chf, year_t0) {# Ensure year_t0 is numeric year_t0 <-as.numeric(year_t0)# GDP-Values for relevant years gdp_values <- df.IMF.con.CH %>%filter(Year %in%c(2025, unique(year_t0))) %>%select(Year, GDP) %>%pivot_wider(names_from = Year, values_from = GDP)# GDP values for 2025 gdp_2025 <- gdp_values[[as.character(2025)]]# GDP values for year_t0 gdp_year_t0 <- gdp_values[match(year_t0, colnames(gdp_values))]# If there are no GDP values, add NA gdp_ratio <-ifelse(!is.na(gdp_year_t0), gdp_2025 / gdp_year_t0, NA)# Adapt price price_2025 <-as.numeric(price_chf) *as.numeric(gdp_ratio)return(price_2025)}
Extracting the PPP values for Belgium and Switzerland:
Code
# Dataset for conversion from 2025 CHF to 2025 Belgium Euro# Extract PPP Value for 2025 for Belgium PPP_BE_2025 <- df.IMF.con.BE %>%filter(Year ==2025) %>%pull(PPP)# Extract PPP Value for 2025 for SwitzerlandPPP_CH_2025 <- df.IMF.con.CH %>%filter(Year ==2025) %>%pull(PPP)# Variable to convert 2025 CHF into 2025 Eurovar.conv.CHF.to.EUR <- PPP_BE_2025/PPP_CH_2025
# preparae demographics datasetdf.COI.demo.adapted <- df.COI.demo.filtered %>%mutate(# IDID = general_id,# Calculate AgeAge =as.numeric(format(Sys.Date(), "%Y")) -as.numeric(demo_ch_t0_birthyear),# Convert Gender to FactorSex =factor(demo_ch_t0_gender, levels =0:3, labels =c("Male", "Female", "Other", "I don't know")),# Convert BMI to NumericBMI =as.numeric(antro_bmi_autocalcu),# Categorize BMI"BMI category"=case_when( BMI >=25& BMI <30~"Overweight", BMI >=30~"Obesity",TRUE~NA_character_ ) %>%factor(levels =c("Overweight", "Obesity")),# Convert Marital Status to Factor"Marital status"=factor(demo_ch_t0_marital_status, levels =0:5, labels =c("Single", "Registered civil partnership", "Married", "Widowed", "Dissolved civil partnership", "Divorced")),# Work StatusWork =factor(demo_ch_t0_work_a___0, levels =0:6, labels =c("I am at school/training/studying", "I am employed", "I am self-employed", "I am a housewife/househusband", "I am unemployed", "I am retired or in early retirement", "I am doing something else")),# Work Details for "something else""Work details"=if_else(demo_ch_t0_work_a___0 ==6, demo_ch_t0_work_b, NA_character_),# ProfessionProfession = demo_ch_t0_profession,# Work Percentage"Work percentage"=as.numeric(demo_ch_t0_work_percentage),# EducationEducation =factor(demo_ch_t0_education, levels =0:8, labels =c("I have not completed any school or training", "Primary school or primary schools", "Recent vocational training", "General school of secondary level I", "Intermediate vocational training", "Higher general secondary school", "School for higher vocational training", "University", "Other education")),# Additional Education Details"Other education"=if_else(demo_ch_t0_education ==9, demo_ch_t0_other_education, NA_character_),# Sports and DurationSports = demo_ch_t0_sports,"Duration of weekly PA (min)"=as.numeric(demo_ch_t0_sport_duration),# LBP Duration"Duration of LBP (months)"=as.numeric(demo_ch_t0_lbp_duration),# Other Diseases"Other diseases"= demo_ch_t0_other_list,# DietDiet =factor(demo_ch_t0_diet, levels =c(0, 1), labels =c("No", "Yes")),# Diet Type"Diet type"=factor(demo_ch_t0_diet_2, levels =1:7, labels =c("Vegetarian", "Pescotarian", "Pescotarian", "Flexitarian", "Vegan", "Paleo", "Other")),# SupplementsSupplements =factor(demo_ch_t0_supplements, levels =c(0, 1), labels =c("No", "Yes")),"Supplement type"= demo_ch_t0_supplements_2,# IntolerancesIntolerances =factor(demo_ch_t0_intolerances, levels =c(0, 1), labels =c("No", "Yes")),"Kind of intolerances"= demo_ch_t0_intolerances_2,# AllergiesAllergies =factor(demo_ch_t0_allergies, levels =c(0, 1), labels =c("No", "Yes")),"Kind of allergy"= demo_ch_t0_allergies_2 ) %>%# Select only the specified variablesselect( ID, Sex, Age, BMI, "BMI category", "Marital status", Work, "Work details", Profession, "Work percentage", Education, "Other education", Sports, "Duration of weekly PA (min)", "Duration of LBP (months)", "Other diseases", Diet, "Diet type", Supplements, "Supplement type", Intolerances, "Kind of intolerances", Allergies, "Kind of allergy" )
Check the selected variables for missing values.
Code
DataExplorer::plot_missing(df.COI.demo.adapted, title ="Missing values COI Demographics Switzerland")
Important: Variables “Other_education”, “Diet_type” and “Work_detail” were variables that were only specified if needed. Therefore the missing data do not need to be taken into account. Therefore, there are no missing data in the demographics dataset.
7 EQ5D5L
The quality of life was evaluated using the EQ5D5L via an utility score.
Calculate utilities via 3 different value sets (DE, GB, BE) using the EQ5D package
Code
# Define the function to combine answers and create a 5-digit numberhealth_state <-function(q1, q2, q3, q4, q5) { answers <-c(q1, q2, q3, q4, q5) combined_number <-as.numeric(paste(answers, collapse =""))return(combined_number)}# Develop health states and utility scoresdf.EQ5D5L.adapted <- df.EQ5D5L.raw %>%mutate(ID = general_id) %>%rowwise() %>%mutate(# Calculation of health states`Health state`=health_state(eq5d5l_ch_q1, eq5d5l_ch_q2, eq5d5l_ch_q3, eq5d5l_ch_q4, eq5d5l_ch_q5),# Subjective health (NAS 0-100)`Subjective health`=as.numeric(eq5d5l_ch_q6),# Utilities for the different value sets`Utility (German value set)`=eq5d(scores =`Health state`, type ="VT", country ="Germany", version ="5L", ignore.invalid =TRUE),`Utility (Belgium value set)`=eq5d(scores =`Health state`, type ="VT", country ="Belgium", version ="5L", ignore.invalid =TRUE),`Utility (GB value set)`=eq5d(scores =`Health state`, type ="VT", country ="England", version ="5L", ignore.invalid =TRUE),`Utility (Canadian value set)`=eq5d(scores =`Health state`, type ="VT", country ="Canada", version ="5L", ignore.invalid =TRUE) ) %>%ungroup() %>%# Select only relevant variablesselect(ID, `Health state`, `Subjective health`, `Utility (German value set)`, `Utility (Belgium value set)`, `Utility (GB value set)`, `Utility (Canadian value set)`) %>%left_join(df.COI.demo.adapted %>%select(ID, Sex, Age, BMI, `BMI category`), by ="ID") %>%relocate(Sex, Age, BMI, `BMI category`, .after = ID)
# median values, Canadian value set included due to the comparison to the reference values for CLBP patients based on the study by Poder et al. tbl.EQ5D5L.median <-sapply(df.EQ5D5L.adapted[, c("Subjective health", "Utility (German value set)", "Utility (Belgium value set)", "Utility (GB value set)", "Utility (Canadian value set)")], function(x) median(x, na.rm =TRUE))tbl.EQ5D5L.median
Subjective health Utility (German value set)
75.5000 0.8710
Utility (Belgium value set) Utility (GB value set)
0.8060 0.8165
Utility (Canadian value set)
0.8280
Code
tableone::CreateTableOne(data = df.EQ5D5L.adapted, vars =c("Subjective health", "Utility (German value set)", "Utility (Belgium value set)", "Utility (GB value set)", "Utility (Canadian value set)"), includeNA =TRUE)
Overall
n 52
Subjective health (mean (SD)) 73.06 (14.50)
Utility (German value set) (mean (SD)) 0.84 (0.13)
Utility (Belgium value set) (mean (SD)) 0.78 (0.14)
Utility (GB value set) (mean (SD)) 0.81 (0.13)
Utility (Canadian value set) (mean (SD)) 0.81 (0.12)
Code
# EQ5D5L long format for utility scoresdf.EQ5D5L.utility.long <- df.EQ5D5L.adapted %>%pivot_longer(cols =c("Utility (German value set)", "Utility (Belgium value set)", "Utility (GB value set)"),names_to ="Variable",values_to ="Value" )# Plot for EQ5D5L values utility scoresplot.eq5d5l.utility <-ggplot(df.EQ5D5L.utility.long, aes(x = Variable, y = Value)) +geom_boxplot() +labs(title ="Boxplot of EQ5D5L Values",x ="Different value sets used to calculate utilities",y ="Utility") +theme_minimal() +theme(axis.text.x =element_text(angle =45, hjust =1)) +ylim(0, 1)plot.eq5d5l.utility
# Develop a table 1 to describe the demographics of the BO2WL trial- participants.# Define variables of interestvars.demographics.COI <-c("Sex", "Age", "BMI", "Marital status", "Work percentage", "Education", "Duration of weekly PA (min)", "Duration of LBP (months)")df.COI.demo.adapted.2<- df.COI.demo.adapted %>%mutate(`BMI category`=factor(`BMI category`, levels =c("Overweight", "Obese"))) %>%mutate(across(where(is.factor), droplevels)) # Entfernt ungenutzte Faktorlevel# Drop unused factor levelsdf.COI.demo.adapted.2<- df.COI.demo.adapted %>%mutate(across(where(is.factor), droplevels)) %>%left_join(df.EQ5D5L.adapted %>%select(ID, `Subjective health`, `Utility (German value set)`) %>%rename(`Subjective health (NAS 0-100)`=`Subjective health`,`Utility (German value set)`=`Utility (German value set)` ))
Code
# Export the datasetwrite.csv(df.COI.demo.adapted.2, "../exported_datasets/df.COI.demo.adapted.2.csv", row.names=TRUE)
Code
# Format labels for table1with(df.COI.demo.adapted.2, {label(Sex) <-"Sex"label(Age) <-"Age (years)"label(BMI) <-"Body Mass Index"label(`Marital status`) <-"Marital Status"label(`Work percentage`) <-"Work Percentage"label(Education) <-"Education level"label(`Duration of weekly PA (min)`) <-"Weekly Physical Activity (minutes)"label(`Duration of LBP (months)`) <-"Duration of Low Back Pain (months)"label(`Subjective health (NAS 0-100)`) <-"Subjective health (NAS 0-100)"label(`Utility (German value set)`) <-"Utility (German value set)"})# Create summary table stratified by BMI categorytbl.demographics <-table1(~Sex + Age + BMI +`Marital status`+`Work percentage`+ Education +`Duration of weekly PA (min)`+`Duration of LBP (months)`+`Subjective health (NAS 0-100)`+`Utility (German value set)`|`BMI category`, data = df.COI.demo.adapted.2, overall ="Overall", droplevels =FALSE)tbl.demographics
Convert the dataframe on supplements into a long formated dataframe.
Code
df.COI.supplements.long <- df.COI.supplements %>%# Split `Supplement type` into separate supplementsmutate(Supplement_type_split =str_split(`Supplement type`, ";")) %>%unnest_longer(Supplement_type_split) %>%# Unnest into longer format# Trim whitespace from split supplement detailsmutate(Supplement_type_split =str_trim(Supplement_type_split)) %>%# Separate the details of each supplement into columnsseparate( Supplement_type_split,into =c("Name", "Company", "Dose", "Frequency", "Days"),sep ="/", # Use '/' as the delimiterfill ="right", # Fill missing values with NAremove =TRUE# Remove the original column after splitting ) %>%# Convert Frequency and Days to numericmutate(Frequency =as.numeric(Frequency),Days =as.numeric(Days) ) %>%# Remove rows where Name, Company, or Dose are missingfilter(!is.na(Name) & Name !="",!is.na(Company) & Company !="",!is.na(Dose) & Dose !="") %>%# Select only the desired columnsselect(ID, Sex, Age, BMI, `BMI category`, Name, Company, Dose, Frequency, Days)
Now we want to analyse the data on the three questionnaires iMCQ, iPCQ and EQ-5D5L. We start with analysing the data on the iMCQ questionnaire. This questionnaire evaluates the health care use (e.g. GP or specialists consultations, therapy sessions with e.g. physiotherapist, medication use).
df.iMCQ.adapted <- df.iMCQ.raw %>%mutate(# IDID = general_id,# T0 year"Year T0"= lubridate::year(general_t0_date),# consultation of healthcare professional"HC professional"=factor(imcq_ch_q1, levels =0:1, labels =c("No", "Yes")),"GP or Medical assistant"=factor(imcq_ch_q1a, levels =0:1, labels =c("No", "Yes")),"GP"=as.numeric(imcq_ch_q1b1, na.rm =TRUE),"Medical assistant"=as.numeric(imcq_ch_q1_b2, na.rm =TRUE),"Social worker"=as.numeric(imcq_ch_q2, na.rm =TRUE),"Physical therapist"=as.numeric(imcq_ch_q3, na.rm =TRUE),"Occupational therapist"=as.numeric(imcq_ch_q4, na.rm =TRUE),"Logopaedist"=as.numeric(imcq_ch_q5, na.rm =TRUE),"Nutritionist"=as.numeric(imcq_ch_q6, na.rm =TRUE),"Complementary medicine"=as.numeric(imcq_ch_q7, na.rm =TRUE),"Psychological care"=as.numeric(imcq_ch_q8, na.rm =TRUE),"Company doctor"=as.numeric(imcq_ch_q9, na.rm =TRUE),"Care"=factor(imcq_ch_q10a, levels =0:1, labels =c("No", "Yes")),"Care kind"=factor(imcq_ch_q10b, levels =0:2, labels =c("Domestic care", "Personal care", "Nursing care")),"Domestic care (weeks)"=as.numeric(imcq_ch_q10c, na.rm =TRUE),"Personal care (weeks)"=as.numeric(imcq_ch_q10c_2, na.rm =TRUE),"Nursing care (weeks)"=as.numeric(imcq_ch_q10c_3, na.rm =TRUE),"Domestic care (hours/week)"=as.numeric(imcq_ch_q10d_1, na.rm =TRUE),"Personal care (hours/week)"=as.numeric(imcq_ch_q10d2, na.rm =TRUE),"Nursing care (hours/week)"=as.numeric(imcq_ch_q10d_3, na.rm =TRUE),"Medication"=factor(imcq_ch_q11a, levels =0:1, labels =c("No", "Yes")),"Medication kind"= imcq_ch_q11b,"Hospital"=factor(imcq_ch_q12a, levels =0:1, labels =c("No", "Yes")),"Hospital (days)"=as.numeric(imcq_ch_q12b, na.rm =TRUE),"Emergency department"=as.numeric(imcq_ch_q12b, na.rm =TRUE),"Ambulance"=as.numeric(imcq_ch_q13, na.rm =TRUE),"Outpatient clinic"=factor(imcq_ch_q14a, levels =0:1, labels =c("No", "Yes")),"Outpatient clinic kind"= imcq_ch_q14b,"Day clinic"=factor(imcq_ch_q15a, levels =0:1, labels =c("No", "Yes")),"Day clinic kind"= imcq_ch_q15b,"Day clinic (days)"=as.numeric(imcq_ch_q15c, na.rm =TRUE),"Day care"=factor(imcq_ch_q16a, levels =0:1, labels =c("No", "Yes")),"Day care kind"=case_when( imcq_ch_q16b1___1 ==1~"Residential home", imcq_ch_q16b1___2 ==1~"Rehabilitation centre", imcq_ch_q16b1___3 ==1~"Psychiatric facility", imcq_ch_q16b1___4 ==1~ imcq_ch_q16b2 # Use the value from imcq_ch_q16b2 if condition is met ),"Day care Residential home (days)"=as.numeric(imcq_ch_q16c1, na.rm =TRUE),"Day care Rehabilitation centre (days)"=as.numeric(imcq_ch_q16c2, na.rm =TRUE),"Day care Psychiatric facility (days)"=as.numeric(imcq_ch_q16c3, na.rm =TRUE),"Day care Other (days)"=as.numeric(imcq_ch_q16c4, na.rm =TRUE),"Hospital stays"=factor(imcq_ch_q17a, levels =0:1, labels =c("No", "Yes")),"Hospital stays (days)"=as.numeric(imcq_ch_q17b, na.rm =TRUE),"Hospital stays (days)"=as.numeric(imcq_ch_q17c, na.rm =TRUE),"Other inpatient stays"=factor(imcq_ch_q18a, levels =0:1, labels =c("No", "Yes")),"Other inpatient stays kind"=case_when( imcq_ch_q18b___0 ==1~"Residential home", imcq_ch_q18b___1 ==1~"Rehabilitation centre", imcq_ch_q18b___2 ==1~"Psychiatric facility", imcq_ch_q18b___3 ==1~ imcq_ch_q18b_1),"Inpatient Residential home (days)"=as.numeric(imcq_ch_q18c_1, na.rm =TRUE),"Inpatient Rehabilitation centre (days)"=as.numeric(imcq_ch_q18c_2, na.rm =TRUE),"Inpatient Psychiatric facility (days)"=as.numeric(imcq_ch_q18c_3, na.rm =TRUE),"Inpatient Other (days)"=as.numeric(imcq_ch_q18c_4, na.rm =TRUE),"Family help"=factor(imcq_ch_q19a, levels =0:1, labels =c("No", "Yes")),"Family help kind"=case_when( imcq_ch_q19b___0 ==1~"Domestic care", imcq_ch_q19b___1 ==1~"Personal care", imcq_ch_q19b___2 ==1~"Practical help"),"Family domestic (weeks)"=as.numeric(imcq_ch_q19c_1, na.rm =TRUE),"Family personal (weeks)"=as.numeric(imcq_ch_q19c_2, na.rm =TRUE),"Family practical (weeks)"=as.numeric(imcq_ch_q19c_3, na.rm =TRUE),"Family domestic (hours)"=as.numeric(imcq_ch_q19d_1, na.rm =TRUE),"Family personal (hours)"=as.numeric(imcq_ch_q19d_2, na.rm =TRUE),"Family practical (hours)"=as.numeric(imcq_ch_q19d_3, na.rm =TRUE),"Means transport hospital"=case_when( imcq_ch_q20a ==0~"Not applicable", imcq_ch_q20a ==1~"Pedestrian", imcq_ch_q20a ==2~"Bicycle", imcq_ch_q20a ==3~"Car", imcq_ch_q20a ==4~"Public transport", imcq_ch_q20a ==5~"Taxi", imcq_ch_q20a ==6~"Other"), # Use the value from imcq_ch_q20b for "Other""Distance from home to hospital"=as.numeric(imcq_ch_q20b, na.rm =TRUE) ) %>%select( ID, `Year T0`,`HC professional`, `GP or Medical assistant`, GP, `Medical assistant`, `Social worker`, `Physical therapist`, `Occupational therapist`, Logopaedist, Nutritionist, `Complementary medicine`, `Psychological care`, `Company doctor`, Care, `Care kind`, `Domestic care (weeks)`, `Personal care (weeks)`, `Nursing care (weeks)`, `Domestic care (hours/week)`, `Personal care (hours/week)`, `Nursing care (hours/week)`, Medication, `Medication kind`, Hospital, `Hospital (days)`, `Emergency department`, Ambulance, `Outpatient clinic`, `Outpatient clinic kind`, `Day clinic`, `Day clinic kind`, `Day clinic (days)`, `Day care`, `Day care kind`, `Day care Residential home (days)`, `Day care Rehabilitation centre (days)`, `Day care Psychiatric facility (days)`, `Day care Other (days)`, `Hospital stays`, `Hospital stays (days)`, `Other inpatient stays`, `Other inpatient stays kind`, `Inpatient Residential home (days)`, `Inpatient Rehabilitation centre (days)`, `Inpatient Psychiatric facility (days)`, `Inpatient Other (days)`, `Family help`, `Family help kind`, `Family domestic (weeks)`, `Family personal (weeks)`, `Family practical (weeks)`, `Family domestic (hours)`, `Family personal (hours)`, `Family practical (hours)`, `Means transport hospital`, `Distance from home to hospital` ) %>%mutate(`GP or Medical assistant`=replace_na(`GP or Medical assistant`, "No"),across(GP:`Company doctor`, ~replace_na(.x, 0)),across(`Domestic care (weeks)`:`Nursing care (hours/week)`, ~replace_na(.x, 0)),across(Hospital:`Ambulance`, ~replace_na(.x, 0)),`Outpatient clinic`=replace_na(`Outpatient clinic`, "No"),`Day clinic`=replace_na(`Day clinic`, "No"),`Day clinic (days)`=replace_na(`Day clinic (days)`, 0),across(`Day care Residential home (days)`:`Day care Other (days)`, ~replace_na(.x, 0)),across(`Hospital stays (days)`:`Hospital stays (days)`, ~replace_na(.x, 0)),across(`Inpatient Residential home (days)`:`Inpatient Other (days)`, ~replace_na(.x, 0)),across(`Family domestic (weeks)`:`Family practical (hours)`, ~replace_na(.x, 0)) ) # replace all values with NA with 0 or "No" for clarification
A separate dataset for the medication is developed in the long format to analyse medication use and further analyse the medication data reported in the iMCQ questionnaire.
Code
# New dataset for medication df.iMCQ.medication.long <- df.iMCQ.adapted %>%# Split `Medication kind` into separate medicationsmutate(Medication_kind_split =str_split(`Medication kind`, ";")) %>%# Use backticks for column nameunnest_longer(Medication_kind_split) %>%# Unnest into longer format# Trim whitespace from split medication detailsmutate(Medication_kind_split =str_trim(Medication_kind_split)) %>%# Separate the details of each medication into columnsseparate( Medication_kind_split,into =c("Name", "Company", "Dose", "Frequency", "Days", "Swissmedic"),sep ="/", # Use '/' as the delimiterfill ="right", # Fill missing values with NAremove =TRUE# Remove the original column after splitting ) %>%# Convert Frequency and Days to numericmutate(Frequency =as.numeric(Frequency),Days =as.numeric(Days) ) %>%# Select only the desired columnsselect(ID, `Year T0`, Name, Company, Dose, Frequency, Days, Swissmedic)
We also exclude the variable “Medication kind” in the df.iMCQ.adapted dataset. The relevant dataset for analysis of the iMCQ data is now “df.iMCQ.adapted.2”.
Code
df.iMCQ.adapted.2<- df.iMCQ.adapted %>%# Remove the column `Medication kind`select(-`Medication kind`)
Check the selected variables for missing values.
Code
# Evaluate missing data in the iMCQ.adapted.2 datasetDataExplorer::plot_missing(df.iMCQ.adapted.2, title ="Missing values iMCQ Switzerland")
Code
# Evaluate missing data in the medication datasetDataExplorer::plot_missing(df.iMCQ.medication.long, title ="Missing values Medication")
Similar to the demographics dataset, the variables with missing values are those that were only specified if needed.
Code
for (var innames(df.iMCQ.adapted)) {if (is.numeric(df.iMCQ.adapted[[var]])) {barplot(table(df.iMCQ.adapted[[var]]), main =paste("", var)) }}
Not a normal distribution for all values.
A table to report healthcare usage is developed.
Code
# Develop a summary table for the iMCQ outcomes and combine the dataset on iMCQ with individual specific data from the demographics dataset (df.COI.demo.adaoted.2). The new dataset is now called df.iMCQ.adapted.2.df.iMCQ.adapted.3<- df.iMCQ.adapted.2%>%left_join(df.COI.demo.adapted.2%>%select(ID, Sex, Age, BMI, `BMI category`, `Duration of LBP (months)`), by ="ID") %>%filter(`HC professional`=="Yes")summary(df.iMCQ.adapted.3)
ID Year T0 HC professional GP or Medical assistant
Length:34 Min. :2023 No : 0 No : 1
Class :character 1st Qu.:2023 Yes:34 Yes:33
Mode :character Median :2024
Mean :2024
3rd Qu.:2024
Max. :2025
GP Medical assistant Social worker Physical therapist
Min. :0.000 Min. : 0.000 Min. :0.00000 Min. : 0.000
1st Qu.:1.000 1st Qu.: 0.000 1st Qu.:0.00000 1st Qu.: 0.000
Median :2.000 Median : 0.000 Median :0.00000 Median : 0.000
Mean :1.588 Mean : 1.324 Mean :0.05882 Mean : 2.235
3rd Qu.:2.000 3rd Qu.: 2.000 3rd Qu.:0.00000 3rd Qu.: 4.000
Max. :4.000 Max. :12.000 Max. :2.00000 Max. :12.000
Occupational therapist Logopaedist Nutritionist Complementary medicine
Min. :0.0000 Min. :0 Min. :0.0000 Min. : 0.000
1st Qu.:0.0000 1st Qu.:0 1st Qu.:0.0000 1st Qu.: 0.000
Median :0.0000 Median :0 Median :0.0000 Median : 0.000
Mean :0.2647 Mean :0 Mean :0.1176 Mean : 1.176
3rd Qu.:0.0000 3rd Qu.:0 3rd Qu.:0.0000 3rd Qu.: 0.000
Max. :6.0000 Max. :0 Max. :2.0000 Max. :15.000
Psychological care Company doctor Care Care kind
Min. :0.0000 Min. :0.00000 No :33 Domestic care: 1
1st Qu.:0.0000 1st Qu.:0.00000 Yes: 1 Personal care: 0
Median :0.0000 Median :0.00000 Nursing care : 0
Mean :0.1176 Mean :0.08824 NA's :33
3rd Qu.:0.0000 3rd Qu.:0.00000
Max. :2.0000 Max. :2.00000
Domestic care (weeks) Personal care (weeks) Nursing care (weeks)
Min. : 0.0000 Min. :0 Min. :0
1st Qu.: 0.0000 1st Qu.:0 1st Qu.:0
Median : 0.0000 Median :0 Median :0
Mean : 0.3529 Mean :0 Mean :0
3rd Qu.: 0.0000 3rd Qu.:0 3rd Qu.:0
Max. :12.0000 Max. :0 Max. :0
Domestic care (hours/week) Personal care (hours/week)
Min. :0.00000 Min. :0
1st Qu.:0.00000 1st Qu.:0
Median :0.00000 Median :0
Mean :0.08824 Mean :0
3rd Qu.:0.00000 3rd Qu.:0
Max. :3.00000 Max. :0
Nursing care (hours/week) Medication Hospital Hospital (days)
Min. :0 No : 4 No :31 Min. :0
1st Qu.:0 Yes:30 Yes: 3 1st Qu.:0
Median :0 Median :0
Mean :0 Mean :0
3rd Qu.:0 3rd Qu.:0
Max. :0 Max. :0
Emergency department Ambulance Outpatient clinic Outpatient clinic kind
Min. :0 Min. :0 No :31 Length:34
1st Qu.:0 1st Qu.:0 Yes: 3 Class :character
Median :0 Median :0 Mode :character
Mean :0 Mean :0
3rd Qu.:0 3rd Qu.:0
Max. :0 Max. :0
Day clinic Day clinic kind Day clinic (days) Day care Day care kind
No :34 Mode:logical Min. :0 No :34 Length:34
Yes: 0 NA's:34 1st Qu.:0 Yes: 0 Class :character
Median :0 Mode :character
Mean :0
3rd Qu.:0
Max. :0
Day care Residential home (days) Day care Rehabilitation centre (days)
Min. :0 Min. :0
1st Qu.:0 1st Qu.:0
Median :0 Median :0
Mean :0 Mean :0
3rd Qu.:0 3rd Qu.:0
Max. :0 Max. :0
Day care Psychiatric facility (days) Day care Other (days) Hospital stays
Min. :0 Min. :0 No :34
1st Qu.:0 1st Qu.:0 Yes: 0
Median :0 Median :0
Mean :0 Mean :0
3rd Qu.:0 3rd Qu.:0
Max. :0 Max. :0
Hospital stays (days) Other inpatient stays Other inpatient stays kind
Min. :0 No :34 Length:34
1st Qu.:0 Yes: 0 Class :character
Median :0 Mode :character
Mean :0
3rd Qu.:0
Max. :0
Inpatient Residential home (days) Inpatient Rehabilitation centre (days)
Min. :0 Min. :0
1st Qu.:0 1st Qu.:0
Median :0 Median :0
Mean :0 Mean :0
3rd Qu.:0 3rd Qu.:0
Max. :0 Max. :0
Inpatient Psychiatric facility (days) Inpatient Other (days) Family help
Min. :0 Min. :0 No :33
1st Qu.:0 1st Qu.:0 Yes: 1
Median :0 Median :0
Mean :0 Mean :0
3rd Qu.:0 3rd Qu.:0
Max. :0 Max. :0
Family help kind Family domestic (weeks) Family personal (weeks)
Length:34 Min. : 0.0000 Min. :0
Class :character 1st Qu.: 0.0000 1st Qu.:0
Mode :character Median : 0.0000 Median :0
Mean : 0.3824 Mean :0
3rd Qu.: 0.0000 3rd Qu.:0
Max. :13.0000 Max. :0
Family practical (weeks) Family domestic (hours) Family personal (hours)
Min. :0 Min. :0.00000 Min. :0
1st Qu.:0 1st Qu.:0.00000 1st Qu.:0
Median :0 Median :0.00000 Median :0
Mean :0 Mean :0.05882 Mean :0
3rd Qu.:0 3rd Qu.:0.00000 3rd Qu.:0
Max. :0 Max. :2.00000 Max. :0
Family practical (hours) Means transport hospital
Min. :0 Length:34
1st Qu.:0 Class :character
Median :0 Mode :character
Mean :0
3rd Qu.:0
Max. :0
Distance from home to hospital Sex Age BMI
Min. : 1.000 Male :12 Min. :24.00 Min. :26.50
1st Qu.: 3.125 Female:22 1st Qu.:39.75 1st Qu.:29.57
Median : 5.250 Median :50.00 Median :30.95
Mean : 7.417 Mean :48.03 Mean :31.12
3rd Qu.:13.000 3rd Qu.:56.00 3rd Qu.:32.58
Max. :15.000 Max. :65.00 Max. :39.70
NA's :28
BMI category Duration of LBP (months)
Overweight:10 Min. : 3.00
Obesity :24 1st Qu.: 24.00
Median : 38.00
Mean : 81.59
3rd Qu.:120.00
Max. :324.00
The questionnaires covered only the last 3 months until baseline assessment.
Short-term sick leave (up to 6 months) was covered in this cost-of-illness study (van den Hout, 2010).
Societal perspective:
Costs of absenteeism were valued using the human capital approach (van den Hout, 2010).
It “considers the patient´s hours of productivity that are lost and calculates productivity costs as the product of those total lost hours with the hourly wage. Every hour not worked is an hour lost, possibly until the patient’s retirement age” (van den Hout, 2010).
The hourly wage is calculated using the following formula:
Hourly wage = [(Annual income before tax)/[(52*5) - (paid vacation days + public holidays)]/ hours of standard workday
In Switzerland, the following data are used:
average annual income before tax in Switzerland (latest available data from 2023):
2023 CHF 94’507
hours of standard workday: 8.4 hours
paid vacation days: 25 days
paid public holidays: 8 days (for Canton of Bern)
Bibliography:
OECD (2024), Average wages (indicator). doi: 10.1787/cc3e1387-en (Accessed on 19 February 2024)
van den Hout WB. The value of productivity: human-capital versus friction-cost method Annals of the Rheumatic Diseases 2010;69:i89-i91.
Bibliography used to recalculate costs in other currency and price year:
Shemilt I, James T, Marcello M. A web-based tool for adjusting costs to a specific target currency and price year. Evidence & Policy. 2010;6(1):51-9. –> http://eppi.ioe.ac.uk/costconversion/default.aspx
Presenteeism is valued using the Osterhaus method with the following formula:
Reduced monthly presenteism days = (DWsx) * PRODsx* ED
DWsx: days worked with symptoms (ipcq_ch_q8)
PRODsx: productivity when working with symptoms (ipcq_ch_q9)/10
ED: daily earnings (calculated in valuation of absenteeism section) –> variable: daily_wage_ch
The following part calculated the absenteeism costs in 2023 CHF.
Code
# Absenteeism# Average annual income in Switzerland in 2023 CHF (2023 US$ 83'332)average_annual_income_CHF <-94507workday_hours <-8.4paid_vacation <-25paid_public_holidays <-8# Calculation of hourly wage based on formula used in Lutz et al. 2022 + making the relevant variables numeric valueshourly_wage_ch <-as.numeric(round((((average_annual_income_CHF)/((52*5)-(paid_vacation+paid_public_holidays)))/workday_hours),2))daily_wage_ch <-as.numeric(round(hourly_wage_ch*workday_hours, 2))# Valuation of absenteeism days (ipcq_ch_q4_2) in a new column called absenteeism_costs_CHFdf.iPCQ.adapted$`Costs absenteeism (CHF)`<-ifelse(is.na(df.iPCQ.adapted$`Absenteeism (days)`), 0, df.iPCQ.adapted$`Absenteeism (days)`* daily_wage_ch)
Unpaid work: Valuation according to xy with CHF 27.72 (2023 CHF) and €18.81 (2023 Belgium €) (acc. to Koopmanschap et al. 2008)
Code
houry_wage_unpaid_work_CHF <-27.72# Unpaid workdf.iPCQ.adapted$`Costs impaired unpaid work (CHF)`<-ifelse(is.na(df.iPCQ.adapted$`Impaired unpaid work (days)`)|is.na(df.iPCQ.adapted$`Help during unpaid work (hours)`),0, (df.iPCQ.adapted$`Impaired unpaid work (days)`*df.iPCQ.adapted$`Help during unpaid work (hours)`)*houry_wage_unpaid_work_CHF)
Indirect costs are the sum of costs for absenteeism and presenteeism (reported in 2023 CHF).
# Reshape the dataset to long formatdf.iPCQ.long <- df.iPCQ.adapted %>%pivot_longer(cols =c(`Absenteeism (days)`, `Presenteeism (days)`, `Presenteeism workload fulfilment`, `Impaired unpaid work (days)`, `Help during unpaid work (hours)`, `Costs absenteeism (2025 CHF)`, `Costs presenteeism (2025 CHF)`, `Costs impaired unpaid work (2025 CHF)`, `Indirect costs (2025 CHF)`),names_to ="Variable",values_to ="Value" )# Separate data for non-cost variablesdf.iPCQ.noncost <- df.iPCQ.long %>%filter(Variable %in%c("Absenteeism (days)", "Presenteeism (days)", "Presenteeism workload fulfilment", "Impaired unpaid work (days)", "Help during unpaid work (hours)"))# Separate data for cost variablesdf.iPCQ.cost <- df.iPCQ.long %>%filter(Variable %in%c("Costs absenteeism (2025 CHF)", "Costs presenteeism (2025 CHF)", "Costs impaired unpaid work (2025 CHF)", "Indirect costs (2025 CHF)"))# Boxplot for non-cost variablesggplot(df.iPCQ.noncost, aes(x = Variable, y = Value)) +geom_boxplot() +labs(title ="Boxplot of iPCQ Data Without Costs",x ="Variables",y ="Values") +theme_minimal() +theme(axis.text.x =element_text(angle =45, hjust =1))
Code
# Boxplot for cost variablesggplot(df.iPCQ.cost, aes(x = Variable, y = Value)) +geom_boxplot() +labs(title ="Boxplot of iPCQ Data With Costs",x ="Variables",y ="Values (in 2025 CHF)") +theme_minimal() +theme(axis.text.x =element_text(angle =45, hjust =1))
A summary table reporting all relevant iPCQ data and indirect cost data (including absenteeism and presenteeism costs).
Code
# Format labels for table1with(df.iPCQ.adapted, {label(`Working (hours/week)`) <-"Working hours per week"label(`Workdays (week)`) <-"Workdays per week"label(Absenteeism) <-"Absenteeism (yes/no)"label(`Absenteeism (days)`) <-"Absenteeism days in the last 3 months"label(Presenteeism) <-"Presenteeism (yes/no)"label(`Presenteeism (days)`) <-"Presenteeism days in the last 3 months"label(`Presenteeism workload fulfilment`) <-"Presenteeism workload fulfilment"label(`Impaired unpaid work`) <-"Impairments during unpaid work"label(`Impaired unpaid work (days)`) <-"Days with impaired unpaid work"label(`Help during unpaid work (hours)`) <-"Hours of help during unpaid work"})# Add the variables Sex, Age, BMI, BMI category and Duration of LBP to the dataset df.iPCQ.adapteddf.iPCQ.adapted <- df.iPCQ.adapted %>%left_join(df.COI.demo.adapted.2%>%select(ID, Sex, Age, BMI, `BMI category`, `Duration of LBP (months)`), by ="ID") %>%relocate(Sex, Age, BMI, `BMI category`, `Duration of LBP (months)`, .after = ID)# Create summary table stratified by BMI categorytbl.iPCQ.summary <-table1(~`Working (hours/week)`+`Workdays (week)`+ Absenteeism +`Absenteeism (days)`+`Presenteeism`+`Presenteeism (days)`+`Presenteeism workload fulfilment`+`Impaired unpaid work`+`Impaired unpaid work (days)`+`Help during unpaid work (hours)`|`BMI category`, data = df.iPCQ.adapted, overall ="Overall", droplevels =FALSE)tbl.iPCQ.summary
Overweight (N=17)
Obesity (N=35)
Overall (N=52)
Working (hours/week)
Mean (SD)
31.6 (13.4)
31.6 (13.7)
31.6 (13.5)
Median [Min, Max]
36.0 [0, 43.0]
35.0 [0, 50.0]
35.0 [0, 50.0]
Workdays (week)
Mean (SD)
3.65 (1.61)
3.99 (1.55)
3.88 (1.56)
Median [Min, Max]
4.00 [0, 5.00]
5.00 [0, 6.00]
4.25 [0, 6.00]
Absenteeism
No
10 (58.8%)
21 (60.0%)
31 (59.6%)
Yes
5 (29.4%)
11 (31.4%)
16 (30.8%)
No paid work
2 (11.8%)
3 (8.6%)
5 (9.6%)
Absenteeism (days)
Mean (SD)
3.76 (11.3)
1.66 (4.55)
2.35 (7.42)
Median [Min, Max]
0 [0, 47.0]
0 [0, 25.0]
0 [0, 47.0]
Presenteeism
No
3 (17.6%)
9 (25.7%)
12 (23.1%)
Yes
12 (70.6%)
23 (65.7%)
35 (67.3%)
No paid work
2 (11.8%)
3 (8.6%)
5 (9.6%)
Presenteeism (days)
Mean (SD)
8.06 (14.2)
7.77 (10.9)
7.87 (11.9)
Median [Min, Max]
3.00 [0, 54.0]
3.00 [0, 50.0]
3.00 [0, 54.0]
Presenteeism workload fulfilment
Mean (SD)
7.92 (1.83)
7.43 (2.02)
7.60 (1.94)
Median [Min, Max]
8.50 [5.00, 10.0]
8.00 [3.00, 10.0]
8.00 [3.00, 10.0]
Missing
5 (29.4%)
12 (34.3%)
17 (32.7%)
Impaired unpaid work
No
13 (76.5%)
22 (62.9%)
35 (67.3%)
Yes
4 (23.5%)
13 (37.1%)
17 (32.7%)
Impaired unpaid work (days)
Mean (SD)
2.88 (7.30)
7.51 (18.0)
6.00 (15.4)
Median [Min, Max]
0 [0, 24.0]
0 [0, 90.0]
0 [0, 90.0]
Help during unpaid work (hours)
Mean (SD)
0.765 (2.19)
3.14 (8.39)
2.37 (7.05)
Median [Min, Max]
0 [0, 9.00]
0 [0, 40.0]
0 [0, 40.0]
Code
# Export the data frame to a CSV filewrite.csv(tbl.iPCQ.summary, "../tables/tbl.iPCQ.summary.csv", row.names =TRUE)write.csv(df.iPCQ.adapted, "../exported_datasets/df.iPCQ.adapted.csv", row.names=TRUE)
14 Bootstrapping of the data
To adress uncertainty, the cost data were bootstrapped. This enabled a reporting of cost values with 95% CI values in a disaggregated form per individual and as a mean overall value.
library(boot)set.seed(123)fct.boot.dir.med <-function(data, i) { df <- data[i, ] # Bootstrap sample# Group by BMI category and calculate the mean for 'Costs absenteeism (2025 CHF)' means_by_category <- df %>%group_by(`BMI category`) %>%summarise(mean_cost =mean(`Direct medical costs (2025 CHF)`, na.rm =TRUE), median_cost =median(`Direct medical costs (2025 CHF)`, na.rm =TRUE))# Calculate the total mean total_mean <-mean(df$`Direct medical costs (2025 CHF)`, na.rm =TRUE)# Calculate the total median total_median <-median(df$`Direct medical costs (2025 CHF)`, na.rm =TRUE)# Return both category means and the total meanreturn(c(means_by_category$mean_cost, means_by_category$median_cost,total_mean, total_median))}# Bootstrapping for direct medical costs with 5000 iterations (Costs in 2025 CHF)set.seed(123)boot.dir.med <-boot(df.dir.med.costs, fct.boot.dir.med, R =5000)# Extract original bootstrap estimates (Costs in 2025 CHF)t1_dir_med_mean_ov <-mean(boot.dir.med$t[, 1]) # t1: Mean cost for overweightt2_dir_med_mean_obe <-mean(boot.dir.med$t[, 2]) # t2: Mean cost for obesityt3_dir_med_median_ov <-mean(boot.dir.med$t[, 3]) # t3: Median cost for overweightt4_dir_med_median_obe <-mean(boot.dir.med$t[, 4]) # t4: Median cost for obesityt5_dir_med_total_mean <-mean(boot.dir.med$t[, 5]) # t5: Total mean costt6_dir_med_total_median <-mean(boot.dir.med$t[, 6]) # t6: Total median cost# CI calculation formula: 2*mean - quantileci_formula_dir_med <-function(orig_mean, boot_samples) { lower <-2* orig_mean -quantile(boot_samples, probs =0.975, na.rm =TRUE) upper <-2* orig_mean -quantile(boot_samples, probs =0.025, na.rm =TRUE)return(c(lower, upper))}# Calculate CIs for mean-based statsci_dir_med_mean_ov <-ci_formula_dir_med(t1_dir_med_mean_ov, boot.dir.med$t[, 1])ci_dir_med_mean_obe <-ci_formula_dir_med(t2_dir_med_mean_obe, boot.dir.med$t[, 2])ci_dir_med_total_mean <-ci_formula_dir_med(t5_dir_med_total_mean, boot.dir.med$t[, 5])# Calculate IRQ for medianiqr_dir_med_median_ov <-quantile(boot.dir.med$t[, 3], probs =c(0.25, 0.75)) # Median Overweightiqr_dir_med_median_obe <-quantile(boot.dir.med$t[, 4], probs =c(0.25, 0.75)) # Median Obesityiqr_dir_med_total_median <-quantile(boot.dir.med$t[, 6], probs =c(0.25, 0.75)) # Total Median# Create the results DataFrame (Costs in 2025 CHF)df.boot.direct.medical.costs <-data.frame(Statistic =c("Mean Direct medical Cost (Overweight)","Mean Direct medical Cost (Obesity)","Median Direct medical Cost (Overweight)","Median Direct medical Cost (Obesity)","Total Mean Direct medical Cost","Total Median Direct medical Cost" ),Value =c( t1_dir_med_mean_ov, t2_dir_med_mean_obe, t3_dir_med_median_ov, t4_dir_med_median_obe, t5_dir_med_total_mean, t6_dir_med_total_median ),Lower =c( ci_dir_med_mean_ov[1], ci_dir_med_mean_obe[1], iqr_dir_med_median_ov[1], iqr_dir_med_median_obe[1], ci_dir_med_total_mean[1], iqr_dir_med_total_median[1] ),Upper =c( ci_dir_med_mean_ov[2], ci_dir_med_mean_obe[2], iqr_dir_med_median_ov[2], iqr_dir_med_median_obe[2], ci_dir_med_total_mean[2], iqr_dir_med_total_median[2] ),Interval_Type =c("95% CI", "95% CI", "IQR", "IQR", "95% CI", "IQR")) %>%mutate(Value =round(Value, 2),Lower =round(Lower, 2),Upper =round(Upper, 2))# View the resultsprint(df.boot.direct.medical.costs)
Statistic Value Lower Upper Interval_Type
1 Mean Direct medical Cost (Overweight) 474.61 269.93 667.10 95% CI
2 Mean Direct medical Cost (Obesity) 577.02 355.51 768.09 95% CI
3 Median Direct medical Cost (Overweight) 438.26 335.38 540.43 IQR
4 Median Direct medical Cost (Obesity) 432.69 362.31 529.39 IQR
5 Total Mean Direct medical Cost 543.69 382.80 686.33 95% CI
6 Total Median Direct medical Cost 442.44 362.31 529.39 IQR
Code
set.seed(123)# Bootstrap functionfct.boot.dir.non.med <-function(data, i) { df <- data[i, ] # Bootstrap sample# Group by BMI category and calculate mean & median for 'Direct non-medical costs (2025 CHF)' means_by_category <- df %>%group_by(`BMI category`) %>%summarise(mean_cost =mean(`Direct non-medical costs (2025 CHF)`, na.rm =TRUE),median_cost =median(`Direct non-medical costs (2025 CHF)`, na.rm =TRUE) )# Calculate total mean & median total_mean <-mean(df$`Direct non-medical costs (2025 CHF)`, na.rm =TRUE) total_median <-median(df$`Direct non-medical costs (2025 CHF)`, na.rm =TRUE)# Return stats: mean overweight, mean obesity, median overweight, median obesity, total mean, total medianreturn(c(means_by_category$mean_cost, means_by_category$median_cost, total_mean, total_median))}# Run bootstrapset.seed(123)boot.dir.non.med <-boot(df.dir.non.med.costs, fct.boot.dir.non.med, R =5000)# Extract original bootstrap estimatest1_dir_non_med_mean_ov <-mean(boot.dir.non.med$t[, 1]) # Mean overweightt2_dir_non_med_mean_obe <-mean(boot.dir.non.med$t[, 2]) # Mean obesityt3_dir_non_med_median_ov <-mean(boot.dir.non.med$t[, 3]) # Median overweightt4_dir_non_med_median_obe <-mean(boot.dir.non.med$t[, 4]) # Median obesityt5_dir_non_med_total_mean <-mean(boot.dir.non.med$t[, 5]) # Total meant6_dir_non_med_total_median <-mean(boot.dir.non.med$t[, 6]) # Total median# CI formula: 2*mean - quantileci_formula_dir_nonmed <-function(orig_mean, boot_samples) { lower <-2* orig_mean -quantile(boot_samples, probs =0.975, na.rm =TRUE) upper <-2* orig_mean -quantile(boot_samples, probs =0.025, na.rm =TRUE) lower <-max(lower, 0) # Prevent negative lower boundreturn(c(lower, upper))}# Calculate CIs for mean-based statsci_dir_non_med_mean_ov <-ci_formula_dir_nonmed(t1_dir_non_med_mean_ov, boot.dir.non.med$t[, 1])ci_dir_non_med_mean_obe <-ci_formula_dir_nonmed(t2_dir_non_med_mean_obe, boot.dir.non.med$t[, 2])ci_dir_non_med_total_mean <-ci_formula_dir_nonmed(t5_dir_non_med_total_mean, boot.dir.non.med$t[, 5])# IQR for median-based statsiqr_dir_non_med_median_ov <-quantile(boot.dir.non.med$t[, 3], probs =c(0.25, 0.75), na.rm =TRUE)iqr_dir_non_med_median_obe <-quantile(boot.dir.non.med$t[, 4], probs =c(0.25, 0.75), na.rm =TRUE)iqr_dir_non_med_total_median <-quantile(boot.dir.non.med$t[, 6], probs =c(0.25, 0.75), na.rm =TRUE)# Create results DataFramedf.boot.direct.non.medical.costs <-data.frame(Statistic =c("Mean Direct non-medical Cost (Overweight)","Mean Direct non-medical Cost (Obesity)","Median Direct non-medical Cost (Overweight)","Median Direct non-medical Cost (Obesity)","Total Mean Direct non-medical Cost","Total Median Direct non-medical Cost" ),Value =c( t1_dir_non_med_mean_ov, t2_dir_non_med_mean_obe, t3_dir_non_med_median_ov, t4_dir_non_med_median_obe, t5_dir_non_med_total_mean, t6_dir_non_med_total_median ),Lower =c( ci_dir_non_med_mean_ov[1], ci_dir_non_med_mean_obe[1], iqr_dir_non_med_median_ov[1], iqr_dir_non_med_median_obe[1], ci_dir_non_med_total_mean[1], iqr_dir_non_med_total_median[1] ),Upper =c( ci_dir_non_med_mean_ov[2], ci_dir_non_med_mean_obe[2], iqr_dir_non_med_median_ov[2], iqr_dir_non_med_median_obe[2], ci_dir_non_med_total_mean[2], iqr_dir_non_med_total_median[2] ),Interval_Type =c("95% CI", "95% CI", "IQR", "IQR", "95% CI", "IQR")) %>%mutate(Value =round(Value, 2),Lower =pmax(round(Lower, 2), 0),Upper =round(Upper, 2))# Print resultsprint(df.boot.direct.non.medical.costs)
Statistic Value Lower Upper Interval_Type
1 Mean Direct non-medical Cost (Overweight) 0.85 0 1.70 95% CI
2 Mean Direct non-medical Cost (Obesity) 28.40 0 56.76 95% CI
3 Median Direct non-medical Cost (Overweight) 0.00 0 0.00 IQR
4 Median Direct non-medical Cost (Obesity) 0.00 0 0.00 IQR
5 Total Mean Direct non-medical Cost 19.39 0 38.63 95% CI
6 Total Median Direct non-medical Cost 0.00 0 0.00 IQR
Code
# Export the data frame to a CSV filewrite.csv(df.boot.direct.medical.costs, "../exported_datasets/tbl.boot.dir.medical.csv", row.names=TRUE)write.csv(df.boot.direct.non.medical.costs, "../exported_datasets/tbl.boot.dir.non.medical.csv", row.names=TRUE)
This includes the Absenteeism and Presenteeism costs
14.0.1 Absenteeism
Code
set.seed(123)# Bootstrap functionfct.boot.abs <-function(data, i) { df <- data[i, ] # Bootstrap sample# Group by BMI category and calculate mean & median for 'Costs absenteeism (2025 CHF)' means_by_category <- df %>%group_by(`BMI category`) %>%summarise(mean_cost =mean(`Costs absenteeism (2025 CHF)`, na.rm =TRUE), median_cost =median(`Costs absenteeism (2025 CHF)`, na.rm =TRUE) )# Calculate total mean & median total_mean <-mean(df$`Costs absenteeism (2025 CHF)`, na.rm =TRUE) total_median <-median(df$`Costs absenteeism (2025 CHF)`, na.rm =TRUE)# Return: mean overweight, mean obesity, median overweight, median obesity, total mean, total medianreturn(c(means_by_category$mean_cost, means_by_category$median_cost, total_mean, total_median))}# Run bootstrapset.seed(123)boot.abs <-boot(df.iPCQ.adapted, fct.boot.abs, R =5000)# Extract original bootstrap estimatest1_abs_mean_ov <-mean(boot.abs$t[, 1]) # Mean overweightt2_abs_mean_obe <-mean(boot.abs$t[, 2]) # Mean obesityt3_abs_median_ov <-mean(boot.abs$t[, 3]) # Median overweightt4_abs_median_obe <-mean(boot.abs$t[, 4]) # Median obesityt5_abs_total_mean <-mean(boot.abs$t[, 5]) # Total meant6_abs_total_median <-mean(boot.abs$t[, 6]) # Total median# CI formula for absenteeism: 2*mean - quantile, lower bound clipped at 0ci_formula_abs <-function(orig_mean, boot_samples) { lower <-2* orig_mean -quantile(boot_samples, probs =0.975, na.rm =TRUE) upper <-2* orig_mean -quantile(boot_samples, probs =0.025, na.rm =TRUE)return(c(lower, upper))}# Calculate CIs for mean-based statsci_abs_mean_ov <-ci_formula_abs(t1_abs_mean_ov, boot.abs$t[, 1])ci_abs_mean_obe <-ci_formula_abs(t2_abs_mean_obe, boot.abs$t[, 2])ci_abs_total_mean <-ci_formula_abs(t5_abs_total_mean, boot.abs$t[, 5])# IQR for median-based statsiqr_abs_median_ov <-quantile(boot.abs$t[, 3], probs =c(0.25, 0.75), na.rm =TRUE)iqr_abs_median_obe <-quantile(boot.abs$t[, 4], probs =c(0.25, 0.75), na.rm =TRUE)iqr_abs_total_median <-quantile(boot.abs$t[, 6], probs =c(0.25, 0.75), na.rm =TRUE)# Create results DataFramedf.boot.indirect.abs <-data.frame(Statistic =c("Mean Absenteeism Cost (Overweight)","Mean Absenteeism Cost (Obesity)","Median Absenteeism Cost (Overweight)","Median Absenteeism Cost (Obesity)","Total Mean Absenteeism Cost","Total Median Absenteeism Cost" ),Value =c( t1_abs_mean_ov, t2_abs_mean_obe, t3_abs_median_ov, t4_abs_median_obe, t5_abs_total_mean, t6_abs_total_median ),Lower =c( ci_abs_mean_ov[1], ci_abs_mean_obe[1], iqr_abs_median_ov[1], iqr_abs_median_obe[1], ci_abs_total_mean[1], iqr_abs_total_median[1] ),Upper =c( ci_abs_mean_ov[2], ci_abs_mean_obe[2], iqr_abs_median_ov[2], iqr_abs_median_obe[2], ci_abs_total_mean[2], iqr_abs_total_median[2] ),Interval_Type =c("95% CI", "95% CI", "IQR", "IQR", "95% CI", "IQR")) %>%mutate(Value =round(Value, 2),Lower =pmax(round(Lower, 2), 0), # Ensure no negative lower boundUpper =round(Upper, 2) )print(df.boot.indirect.abs)
Statistic Value Lower Upper Interval_Type
1 Mean Absenteeism Cost (Overweight) 1602.22 0.00 3062.50 95% CI
2 Mean Absenteeism Cost (Obesity) 705.91 0.00 1206.28 95% CI
3 Median Absenteeism Cost (Overweight) 34.58 0.00 0.00 IQR
4 Median Absenteeism Cost (Obesity) 5.54 0.00 0.00 IQR
5 Total Mean Absenteeism Cost 1000.39 2.63 1681.40 95% CI
6 Total Median Absenteeism Cost 0.81 0.00 0.00 IQR
14.0.2 Presenteeism
Code
set.seed(123)fct.boot.pres <-function(data, i) { df <- data[i, ] # Bootstrap sample# Group by BMI category and calculate the mean for 'Costs absenteeism (2025 CHF)' means_by_category <- df %>%group_by(`BMI category`) %>%summarise(mean_cost =mean(`Costs presenteeism (2025 CHF)`, na.rm =TRUE), median_cost =median(`Costs presenteeism (2025 CHF)`, na.rm =TRUE))# Calculate the total mean total_mean <-mean(df$`Costs presenteeism (2025 CHF)`, na.rm =TRUE)# Calculate the total median total_median <-median(df$`Costs presenteeism (2025 CHF)`, na.rm =TRUE)# Return both category means and the total meanreturn(c(means_by_category$mean_cost, means_by_category$median_cost,total_mean, total_median))}# Bootstrapping for presenteeism with 5000 iterations (Costs in 2025 CHF)set.seed(123)boot.pres <-boot(df.iPCQ.adapted, fct.boot.pres, R =5000)# Function to calculate CI and ensure lower bound >= 0ci_formula_pres <-function(orig_mean, boot_samples) { lower <-2* orig_mean -quantile(boot_samples, probs =0.975, na.rm =TRUE) upper <-2* orig_mean -quantile(boot_samples, probs =0.025, na.rm =TRUE)return(c(lower, upper))}# Correct calls to CI functionci_pres_mean_ov <-ci_formula_pres(orig_mean =mean(boot.pres$t[, 1], na.rm =TRUE), # original estimateboot_samples = boot.pres$t[, 1] # bootstrap distribution)ci_pres_mean_obe <-ci_formula_pres(orig_mean =mean(boot.pres$t[, 2], na.rm =TRUE),boot_samples = boot.pres$t[, 2])ci_pres_total_mean <-ci_formula_pres(orig_mean =mean(boot.pres$t[, 5], na.rm =TRUE),boot_samples = boot.pres$t[, 5])# IQR for median values (unchanged)iqr_pres_median_ov <-quantile(boot.pres$t[, 3], probs =c(0.25, 0.75))iqr_pres_median_obe <-quantile(boot.pres$t[, 4], probs =c(0.25, 0.75))iqr_pres_total_median <-quantile(boot.pres$t[, 6], probs =c(0.25, 0.75))# Extract original bootstrap estimatest1_pres_mean_ov <-mean(boot.pres$t[, 1])t2_pres_mean_obe <-mean(boot.pres$t[, 2])t3_pres_median_ov <-mean(boot.pres$t[, 3])t4_pres_median_obe <-mean(boot.pres$t[, 4])t5_pres_total_mean <-mean(boot.pres$t[, 5])t6_pres_total_median <-mean(boot.pres$t[, 6])# Create DataFramedf.boot.indirect.pres <-data.frame(Statistic =c("Mean Presenteeism Cost (Overweight)", "Mean Presenteeism Cost (Obesity)", "Median Presenteeism Cost (Overweight)", "Median Presenteeism Cost (Obesity)", "Total Mean Presenteeism Cost", "Total Median Presenteeism Cost" ),Value =c( t1_pres_mean_ov, t2_pres_mean_obe, t3_pres_median_ov, t4_pres_median_obe, t5_pres_total_mean, t6_pres_total_median ),Lower =c( ci_pres_mean_ov[1], ci_pres_mean_obe[1], iqr_pres_median_ov[1], iqr_pres_median_obe[1], ci_pres_total_mean[1], iqr_pres_total_median[1] ),Upper =c( ci_pres_mean_ov[2], ci_pres_mean_obe[2], iqr_pres_median_ov[2], iqr_pres_median_obe[2], ci_pres_total_mean[2], iqr_pres_total_median[2] ),Interval_Type =c("95% CI", "95% CI", "IQR", "IQR", "95% CI", "IQR")) %>%mutate(Value =round(Value, 2),Lower =pmax(round(Lower, 2), 0), # Ensure no negative lower boundUpper =round(Upper, 2) )# View resultsprint(df.boot.indirect.pres)
Statistic Value Lower Upper Interval_Type
1 Mean Presenteeism Cost (Overweight) 677.49 0.00 1166.90 95% CI
2 Mean Presenteeism Cost (Obesity) 486.74 246.28 701.70 95% CI
3 Median Presenteeism Cost (Overweight) 212.40 0.00 404.54 IQR
4 Median Presenteeism Cost (Obesity) 195.79 0.00 340.67 IQR
5 Total Mean Presenteeism Cost 549.47 281.67 774.67 95% CI
6 Total Median Presenteeism Cost 181.61 0.00 340.66 IQR
14.0.3 Unpaid work
Code
set.seed(123)fct.boot.unpaid.work <-function(data, i) { df <- data[i, ] # Bootstrap sample# Group by BMI category and calculate the mean for 'Costs absenteeism (2025 CHF)' means_by_category <- df %>%group_by(`BMI category`) %>%summarise(mean_cost =mean(`Costs impaired unpaid work (2025 CHF)`, na.rm =TRUE), median_cost =median(`Costs impaired unpaid work (2025 CHF)`, na.rm =TRUE))# Calculate the total mean total_mean <-mean(df$`Costs impaired unpaid work (2025 CHF)`, na.rm =TRUE)# Calculate the total median total_median <-median(df$`Costs impaired unpaid work (2025 CHF)`, na.rm =TRUE)# Return both category means and the total meanreturn(c(means_by_category$mean_cost, means_by_category$median_cost,total_mean, total_median))}# Bootstrapping for presenteeism with 5000 iterations (Costs in 2025 CHF)set.seed(123)boot.unpaid.work <-boot(df.iPCQ.adapted, fct.boot.unpaid.work, R =5000)# Function to calculate CI and ensure lower bound >= 0ci_calculate_unpaid_w <-function(boot_obj, index) { ci <-boot.ci(boot_obj, type ="perc", index = index)$perc[4:5] ci[1] <-max(ci[1], 0) # Cap lower bound at 0return(ci)}# Calculate the 95% Confidence Intervals (Costs in 2025 CHF)ci_unpaid_work_mean_ov <-ci_calculate_unpaid_w(boot.unpaid.work, 1) # Mean overweightci_unpaid_work_mean_obe <-ci_calculate_unpaid_w(boot.unpaid.work, 2) # Mean obesityci_unpaid_work_total_mean <-ci_calculate_unpaid_w(boot.unpaid.work, 5) # Total mean# IQR for median values (unchanged in calculation)iqr_unpaid_work_median_ov <-quantile(boot.unpaid.work$t[, 3], probs =c(0.25, 0.75))iqr_unpaid_work_median_obe <-quantile(boot.unpaid.work$t[, 4], probs =c(0.25, 0.75))iqr_unpaid_work_total_median <-quantile(boot.unpaid.work$t[, 6], probs =c(0.25, 0.75))# Extract original bootstrap estimatest1_unpaid_work_mean_ov <-mean(boot.unpaid.work$t[, 1])t2_unpaid_work_mean_obe <-mean(boot.unpaid.work$t[, 2])t3_unpaid_work_median_ov <-mean(boot.unpaid.work$t[, 3])t4_unpaid_work_median_obe <-mean(boot.unpaid.work$t[, 4])t5_unpaid_work_total_mean <-mean(boot.unpaid.work$t[, 5])t6_unpaid_work_total_median <-mean(boot.unpaid.work$t[, 6])# Create results DataFramedf.boot.indirect.unpaid.work <-data.frame(Statistic =c("Mean Unpaid Work Cost (Overweight)", "Mean Unpaid Work Cost (Obesity)", "Median Unpaid Work Cost (Overweight)", "Median Unpaid Work Cost (Obesity)", "Total Mean Unpaid Work Cost", "Total Median Unpaid Work Cost" ),Value =c( t1_unpaid_work_mean_ov, t2_unpaid_work_mean_obe, t3_unpaid_work_median_ov, t4_unpaid_work_median_obe, t5_unpaid_work_total_mean, t6_unpaid_work_total_median ),Lower =c( ci_unpaid_work_mean_ov[1], ci_unpaid_work_mean_obe[1], iqr_unpaid_work_median_ov[1], iqr_unpaid_work_median_obe[1], ci_unpaid_work_total_mean[1], iqr_unpaid_work_total_median[1] ),Upper =c( ci_unpaid_work_mean_ov[2], ci_unpaid_work_mean_obe[2], iqr_unpaid_work_median_ov[2], iqr_unpaid_work_median_obe[2], ci_unpaid_work_total_mean[2], iqr_unpaid_work_total_median[2] ),Interval_Type =c("95% CI", "95% CI", "IQR", "IQR", "95% CI", "IQR")) %>%mutate(Value =round(Value, 2),Lower =pmax(round(Lower, 2), 0), # Force lower bound >= 0Upper =round(Upper, 2) )# View resultsprint(df.boot.indirect.unpaid.work)
Statistic Value Lower Upper Interval_Type
1 Mean Unpaid Work Cost (Overweight) 436.71 2.18 1237.28 95% CI
2 Mean Unpaid Work Cost (Obesity) 894.42 232.81 1872.04 95% CI
3 Median Unpaid Work Cost (Overweight) 0.54 0.00 0.00 IQR
4 Median Unpaid Work Cost (Obesity) 4.53 0.00 0.00 IQR
5 Total Mean Unpaid Work Cost 745.56 241.02 1436.80 95% CI
6 Total Median Unpaid Work Cost 0.09 0.00 0.00 IQR
Code
# Export the data frame to a CSV filewrite.csv(df.boot.indirect.pres, "../exported_datasets/tbl.boot.indirect.pres.csv", row.names=TRUE)write.csv(df.boot.indirect.abs, "../exported_datasets/tbl.boot.indirect.abs.csv", row.names=TRUE)write.csv(df.boot.indirect.unpaid.work, "../exported_datasets/tbl.boot.indirect.unpaid.work.csv", row.names=TRUE)
Statistic Value Lower Upper
lower.97.5% Total Mean Societal Costs (Overweight) 3194.86 399.25 5049.41
lower.97.5%1 Total Mean Societal Costs (Obesity) 2691.96 1501.60 3646.68
lower.97.5%2 Total Mean Societal Costs 2850.44 1667.17 3829.17
25% Total Median Societal Costs (Overweight) 3043.84 2245.16 3974.46
25%1 Total Median Societal Costs (Obesity) 2648.21 2303.68 3035.18
25%2 Total Median Societal Costs 2810.81 2456.60 3204.59
Interval_Type
lower.97.5% 95% CI
lower.97.5%1 95% CI
lower.97.5%2 95% CI
25% IQR
25%1 IQR
25%2 IQR
df.boot.overall.perc <- df.boot.overall.raw %>%# Keep only rows where Statistic starts with "Total Mean"filter(str_starts(Statistic, "Total Mean")) %>%# Exclude Societal Costs rowsfilter(!str_detect(Statistic, "Societal Costs")) %>%# Keep only Statistic and Value columnsselect(Statistic, Value) %>%# Calculate share of each cost relative to the sum of all costsmutate(perc =round(100* Value /sum(Value), 2)) %>%# Sort by percentagearrange(desc(perc))df.boot.overall.perc
Statistic Value perc
...1 Total Mean Absenteeism Cost 1000.39 35.00
...2 Total Mean Unpaid Work Cost 745.56 26.08
...3 Total Mean Presenteeism Cost 549.47 19.22
...4 Total Mean Direct medical Cost 543.69 19.02
...5 Total Mean Direct non-medical Cost 19.39 0.68
Code
# Percentage of indirect costs on total societal costsdf.boot.overall.perc %>%filter(Statistic %in%c("Total Mean Absenteeism Cost","Total Mean Unpaid Work Cost","Total Mean Presenteeism Cost")) %>%summarise(total_perc =sum(perc))
total_perc
1 80.3
18 Rerun calculation for indirect costs via bootstrapping
Excluding participants 10046
Code
df.iPCQ.subset <- df.iPCQ.adapted %>%filter(ID !="BO2WL_F_10046")# Bootstrapping with dataset without 10046set.seed(123)fct.boot.abs <-function(data, i) { df <- data[i, ] # Bootstrap sample# Group by BMI category and calculate the mean for 'Costs absenteeism (2025 CHF)' means_by_category <- df %>%group_by(`BMI category`) %>%summarise(mean_cost =mean(`Costs absenteeism (2025 CHF)`, na.rm =TRUE), median_cost =median(`Costs absenteeism (2025 CHF)`, na.rm =TRUE))# Calculate the total mean total_mean <-mean(df$`Costs absenteeism (2025 CHF)`, na.rm =TRUE)# Calculate the total median total_median <-median(df$`Costs absenteeism (2025 CHF)`, na.rm =TRUE)# Return both category means and the total meanreturn(c(means_by_category$mean_cost, means_by_category$median_cost,total_mean, total_median))}# Bootstrapping for presenteeism with 5000 iterations (Costs in 2025 CHF)library(boot)set.seed(123)boot.abs.sub <-boot(df.iPCQ.subset, fct.boot.abs, R =5000)# Calculate the 95% Confidence Intervals (CI) (Costs in 2025 CHF)set.seed(123)ci_abs_mean_ov_sub <-boot.ci(boot.abs.sub, type ="perc", index =1)$perc [4:5] # CI for t1 (mean overweight)ci_abs_mean_obe_sub <-boot.ci(boot.abs.sub, type ="perc", index =2)$perc[4:5] # CI for t2 (mean obesity)ci_abs_median_ov_sub <-boot.ci(boot.abs.sub, type ="perc", index =3)$perc[4:5] # CI for t3 (median overweight)ci_abs_median_obe_sub <-boot.ci(boot.abs.sub, type ="perc", index =4)$perc[4:5] # CI for t4 (median obesity)ci_abs_total_mean_sub <-boot.ci(boot.abs.sub, type ="perc", index =5)$perc[4:5] # CI for t5 (total mean)ci_abs_total_median_sub <-boot.ci(boot.abs.sub, type ="perc", index =6)$perc[4:5] # CI for t6 (total median)# IRQ for medianiqr_abs_median_ov_sub <-quantile(boot.abs.sub$t[, 3], probs =c(0.25, 0.75)) # Median Overweightiqr_abs_median_obe_sub <-quantile(boot.abs.sub$t[, 4], probs =c(0.25, 0.75)) # Median Obesityiqr_abs_total_median_sub <-quantile(boot.abs.sub$t[, 6], probs =c(0.25, 0.75)) # Total Median# Extract original bootstrap estimates (Costs in 2025 CHF)t1_abs_mean_ov_sub <-mean(boot.abs.sub$t[, 1]) # t1: Mean cost for overweightt2_abs_mean_obe_sub <-mean(boot.abs.sub$t[, 2]) # t2: Mean cost for obesityt3_abs_median_ov_sub <-mean(boot.abs.sub$t[, 3]) # t3: Median cost for overweightt4_abs_median_obe_sub <-mean(boot.abs.sub$t[, 4]) # t4: Median cost for obesityt5_abs_total_mean_sub <-mean(boot.abs.sub$t[, 5]) # t5: Total mean costt6_abs_total_median_sub <-mean(boot.abs.sub$t[, 6]) # t6: Total median cost# Create the results DataFrame (Costs in 2025 CHF)df.boot.indirect.abs.sub <-data.frame(Statistic =c("Mean Absenteeism Cost (Overweight)", "Mean Absenteeism Cost (Obesity)", "Median Absenteeism Cost (Overweight)", "Median Absenteeism Cost (Obesity)", "Total Mean Absenteeism Cost", "Total Median Absenteeism Cost"),Value =c(t1_abs_mean_ov_sub, t2_abs_mean_obe_sub, t3_abs_median_ov_sub, t4_abs_median_obe_sub, t5_abs_total_mean_sub, t6_abs_total_median_sub),Lower =c(ci_abs_mean_ov_sub[1], ci_abs_mean_obe_sub[1], ci_abs_median_ov_sub[1], ci_abs_median_obe_sub[1], ci_abs_total_mean_sub[1], ci_abs_total_median_sub[1]),Upper =c(ci_abs_mean_ov_sub[2], ci_abs_mean_obe_sub[2], ci_abs_median_ov_sub[2], ci_abs_median_obe_sub[2], ci_abs_total_mean_sub[2], ci_abs_total_median_sub[2]))# Optionally round numeric columnsdf.boot.indirect.abs.sub <- df.boot.indirect.abs.sub %>%mutate(across(where(is.numeric), ~round(.x, 2)))# View the resultsprint(df.boot.indirect.abs.sub)
Statistic Value Lower Upper
1 Mean Absenteeism Cost (Overweight) 454.66 35.49 922.64
2 Mean Absenteeism Cost (Obesity) 700.41 206.46 1437.19
3 Median Absenteeism Cost (Overweight) 12.39 0.00 0.00
4 Median Absenteeism Cost (Obesity) 6.47 0.00 0.00
5 Total Mean Absenteeism Cost 623.62 250.49 1135.56
6 Total Median Absenteeism Cost 0.34 0.00 0.00
19 Descriptive reporting of HC consumption
Code
# 1. Filter for people who consulted a healthcare professionaldf.hc.users <- df.iMCQ.adapted.2%>%filter(`HC professional`=="Yes")with(df.hc.users, {label(`Year T0`) <-"Year of T0 measurement"label(`HC professional`) <-"Healthcare professional"label(`GP or Medical assistant`) <-"GP or Medical assistant"label(GP) <-"General Practitioner"label(`Medical assistant`) <-"Medical assistant"label(`Social worker`) <-"Social worker"label(`Physical therapist`) <-"Physical therapist"label(`Occupational therapist`) <-"Occupational therapist"label(Logopaedist) <-"Logopaedist"label(Nutritionist) <-"Nutritionist"label(`Complementary medicine`) <-"Complementary medicine"label(`Psychological care`) <-"Psychological care"label(`Company doctor`) <-"Company doctor"label(Care) <-"Care received"label(Medication) <-"Medication use (yes/no)"label(`Med.count`) <-"Number of medications used"label(Hospital) <-"Hospital visits (yes/no)"label(`Emergency department`) <-"Emergency department visits"label(Ambulance) <-"Ambulance use (yes/no)"label(`Outpatient clinic`) <-"Outpatient clinic visits"label(`Day clinic`) <-"Day clinic visits"label(`Day care`) <-"Day care received"label(`Hospital stays`) <-"Hospital stays (yes/no)"label(`Other inpatient stays`) <-"Other inpatient stays (yes/no)"})# Create summary table stratified by BMI categorytbl.hc.users <-table1(~ GP +`Medical assistant`+`Social worker`+`Physical therapist`+`Occupational therapist`+ Logopaedist + Nutritionist +`Complementary medicine`+`Psychological care`+`Company doctor`+ Care + Medication +`Med.count`+ Hospital +`Emergency department`+ Ambulance +`Outpatient clinic`+`Day clinic`+`Day care`+`Hospital stays`+`Other inpatient stays`|`BMI category`, data = df.hc.users, overall ="Overall", droplevels =FALSE)tbl.hc.users
Overweight (N=10)
Obesity (N=24)
Overall (N=34)
GP
Mean (SD)
1.60 (1.07)
1.58 (0.929)
1.59 (0.957)
Median [Min, Max]
2.00 [0, 3.00]
1.50 [0, 4.00]
2.00 [0, 4.00]
Medical assistant
Mean (SD)
1.40 (3.78)
1.29 (1.76)
1.32 (2.46)
Median [Min, Max]
0 [0, 12.0]
1.00 [0, 8.00]
0 [0, 12.0]
Social worker
Mean (SD)
0 (0)
0.0833 (0.408)
0.0588 (0.343)
Median [Min, Max]
0 [0, 0]
0 [0, 2.00]
0 [0, 2.00]
Physical therapist
Mean (SD)
3.50 (4.50)
1.71 (2.48)
2.24 (3.24)
Median [Min, Max]
1.50 [0, 12.0]
0 [0, 9.00]
0 [0, 12.0]
Occupational therapist
Mean (SD)
0 (0)
0.375 (1.35)
0.265 (1.14)
Median [Min, Max]
0 [0, 0]
0 [0, 6.00]
0 [0, 6.00]
Logopaedist
Mean (SD)
0 (0)
0 (0)
0 (0)
Median [Min, Max]
0 [0, 0]
0 [0, 0]
0 [0, 0]
Nutritionist
Mean (SD)
0 (0)
0.167 (0.482)
0.118 (0.409)
Median [Min, Max]
0 [0, 0]
0 [0, 2.00]
0 [0, 2.00]
Complementary medicine
Mean (SD)
0.200 (0.632)
1.58 (3.71)
1.18 (3.18)
Median [Min, Max]
0 [0, 2.00]
0 [0, 15.0]
0 [0, 15.0]
Psychological care
Mean (SD)
0.200 (0.632)
0.0833 (0.408)
0.118 (0.478)
Median [Min, Max]
0 [0, 2.00]
0 [0, 2.00]
0 [0, 2.00]
Company doctor
Mean (SD)
0.100 (0.316)
0.0833 (0.408)
0.0882 (0.379)
Median [Min, Max]
0 [0, 1.00]
0 [0, 2.00]
0 [0, 2.00]
Care
No
9 (90.0%)
24 (100%)
33 (97.1%)
Yes
1 (10.0%)
0 (0%)
1 (2.9%)
Medication
No
2 (20.0%)
2 (8.3%)
4 (11.8%)
Yes
8 (80.0%)
22 (91.7%)
30 (88.2%)
Med.count
Mean (SD)
2.38 (1.51)
3.14 (1.88)
2.93 (1.80)
Median [Min, Max]
2.00 [1.00, 5.00]
3.00 [1.00, 8.00]
2.50 [1.00, 8.00]
Missing
2 (20.0%)
2 (8.3%)
4 (11.8%)
Hospital
No
9 (90.0%)
22 (91.7%)
31 (91.2%)
Yes
1 (10.0%)
2 (8.3%)
3 (8.8%)
Emergency department
Mean (SD)
0 (0)
0 (0)
0 (0)
Median [Min, Max]
0 [0, 0]
0 [0, 0]
0 [0, 0]
Ambulance
Mean (SD)
0 (0)
0 (0)
0 (0)
Median [Min, Max]
0 [0, 0]
0 [0, 0]
0 [0, 0]
Outpatient clinic
No
9 (90.0%)
22 (91.7%)
31 (91.2%)
Yes
1 (10.0%)
2 (8.3%)
3 (8.8%)
Day clinic
No
10 (100%)
24 (100%)
34 (100%)
Yes
0 (0%)
0 (0%)
0 (0%)
Day care
No
10 (100%)
24 (100%)
34 (100%)
Yes
0 (0%)
0 (0%)
0 (0%)
Hospital stays
No
10 (100%)
24 (100%)
34 (100%)
Yes
0 (0%)
0 (0%)
0 (0%)
Other inpatient stays
No
10 (100%)
24 (100%)
34 (100%)
Yes
0 (0%)
0 (0%)
0 (0%)
20 Descriptive reporting of abs/pres
Code
df.abs <- df.iPCQ.adapted %>%filter(`Absenteeism`=="Yes")df.pres <- df.iPCQ.adapted %>%filter(`Presenteeism`=="Yes")df.unpaid.work <- df.iPCQ.adapted %>%filter(`Impaired unpaid work`=="Yes")# Format labels for table1with(df.iPCQ.adapted, {label(`Working (hours/week)`) <-"Working hours per week"label(`Workdays (week)`) <-"Workdays per week"label(Absenteeism) <-"Absenteeism (yes/no)"label(`Absenteeism (days)`) <-"Absenteeism days in the last 3 months"label(Presenteeism) <-"Presenteeism (yes/no)"label(`Presenteeism (days)`) <-"Presenteeism days in the last 3 months"label(`Presenteeism workload fulfilment`) <-"Presenteeism workload fulfilment"label(`Impaired unpaid work`) <-"Impairments during unpaid work"label(`Impaired unpaid work (days)`) <-"Days with impaired unpaid work"label(`Help during unpaid work (hours)`) <-"Hours of help during unpaid work"})# Create summary table stratified by BMI categorytbl.abs <-table1(~`Working (hours/week)`+`Workdays (week)`+ Absenteeism +`Absenteeism (days)`|`BMI category`, data = df.abs, overall ="Overall", droplevels =FALSE)tbl.abs
tbl.unpaid.work <-table1(~`Working (hours/week)`+`Impaired unpaid work`+`Impaired unpaid work (days)`+`Help during unpaid work (hours)`|`BMI category`, data = df.unpaid.work, overall ="Overall", droplevels =FALSE)tbl.unpaid.work