--- title: "'Accuracy and precision of energy expenditure, heart rate, and steps measured by combined-sensing Fitbits: Systematic Review and Meta-Analyses" author: "Guillaume Chevance" date: "last update July 2021" output: pdf_document: default html_document: default description: this code runs several meta-analyses of Bland-Altman studies protocol & pre-registration: https://osf.io/preprints/sportrxiv/tgk7a/ contact: guillaume.chevance@isglobal.org --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE) #Import library library(dplyr) library(outliers) ``` Import data ```{r} #rm(list=ls()) data <- read.csv("data_r_4.csv", sep=";") #library(readxl) #df <- read_excel("Individual subjects raw data.xlsx") ``` 1. Calculation of the mean bias and relative sd from means, sds and correlations ```{r} selectionCriterion1 <- c(1:13, 33:36, 76:85, 106:107, 118:119, 127, 142, 154, 167, 262:266, 287:288) data2 <- data[selectionCriterion1,] data2$BA_bias <- data2$Mean_Fitbit - data2$Mean_Criterion data2$BA_SD <- data2$SD_Fitbit^2 + data2$SD_Criterion^2 - 2*data2$Correlation*data2$SD_Fitbit*data2$SD_Criterion data2$BA_SD <- sqrt(data2$BA_SD) # For studies that have Two mean and two SD data[selectionCriterion1,] <- data2 ``` 2. Calculation of the bias's sd from LOA (Limits of Agreements) ```{r} selectionCriterion2<- c(37:38, 58:59, 62, 64:68, 104:105, 155:160, 210:218, 297:302) data3 <- data[selectionCriterion2,] data3$BA_SD <- (data3$LOA_U - data3$BA_bias)/1.96 # For studies that have bias and LOA # We have two methods to calculate the agreement, according to what we have in our raw data. data[selectionCriterion2,] <- data3 ``` 3. Rescale from absolute to relative values for steps and EE (energy expenditure, depedent) + remove NA for "duration_minutes" (i.e., divide mean bias and relative sd by the time of the task/protocol in minutes) ```{r} summary(data$BA_bias) selectionCriterion3 <- !(data$Outcome=="HR") & !is.na(data$Duration_minutes) & !(data$relative_absolute=="relative") data4 <- data[selectionCriterion3,] data4$BA_bias <- data4$BA_bias / data4$Duration_minutes # standirize procesdure to put all the bias in the "minute scale" data4$BA_SD <- data4$BA_SD / data4$Duration_minutes data[selectionCriterion3,] <- data4 data <- data[!is.na(data$BA_bias) & !is.na(data$BA_SD),] #Until here we are computing the effect size of the MA ## Now we have all variables of interest, and we do teh analysis ``` Descriptives ```{r} boxplot(BA_bias~Outcome, dat =data) # Boxplot with Outcome as categories n_distinct(data$Study) table(data$Outcome) unique(data$N_study) # 20 studies 3 172 subjects #PT unique(data$ï..Study) # 20 studies 3 172 subjects #PT table(data$ï..Study) # 20 studies 3 172 subjects #PT ``` Meta-analyses ```{r} #to avoid N (number of data points) > n (number of observation) # This statement is helping to re-define the unit of analysis. # This should be revised to understand the step well. # # N_study number of subjects # N_analysis number of observation (multiple measurements for a subject, always in HR and repeated measure) data$N_study <- ifelse((data$N_study > data$N_analysis) & (data$Repeated_measures=="no"), data$N_analysis, data$N_study) #data$N_study1 <- ifelse(data$N_study > data$N_analysis & data$Repeated_measures=="no", data$N_analysis, data$N_study) #data$N_study3 <- ifelse((data$N_study > data$N_analysis) , data$N_analysis, data$N_study) #Only for the row 127 and 128 #data$N_study-data$N_study2 #data$N_study3-data$N_study2 #data$N_study-data$N_study3 #This is for missingness: (data$N_study > data$N_analysis) CFM # # # #unique(data$N_analysis) dfdf<-cbind(N_study=data$N_study,N_analysis= data$N_analysis) # PT # call the BA_function to run the MA source("BA_functions.R") #MA loop runMeta <- function(data){ data$needed <- ifelse(data$Repeated_measures == "no", 1, 0) #------------------------------------------------ # Create imputed datasets by rho value #------------------------------------------------ bias <- data$BA_bias V_bias <- data$BA_SD^2/data$N_analysis logs2 <- log(data$BA_SD^2) # Log variance of the Bias. Accroding to Tripen the transformation was applied because the pool variance is not normal, when the SS is small. V_logs2 <- 2/(data$N_analysis - 1) N <- data$N_analysis n <- data$N_study needed <- data$needed # scenarios for the correlation for the repeated measures. Which ussually is unknown. # We should have values base on the experts new.8 <- rho_imputer(bias,V_bias,logs2,V_logs2,N,n,needed,.8) # Internal funcion inside BA_functions.R new.5 <- rho_imputer(bias,V_bias,logs2,V_logs2,N,n,needed,.5) # Not used in the study new1.0 <- rho_imputer(bias,V_bias,logs2,V_logs2,N,n,needed,1) # At The end they choose the correlation value 0.8, because give similar results for the pool bias estimation. #--------------------------------------------------- # Do analyses in various ways #This is creating scenarios for the tho assumption in the subject level data. #--------------------------------------------------- #For only included data inc <- subset(new.8, needed == "0") bias <- inc$bias V_bias <- inc$V_bias logs2 <- inc$logs2 V_logs2 <- inc$V_logs2 # loa_maker is doing the meata-analysis for the Bias and for the log of variance of bias # results_inc <- loa_maker(bias,V_bias,logs2,V_logs2) #For rho = .8 bias <- new.8$bias V_bias <- new.8$V_bias logs2 <- new.8$logs2 V_logs2 <- new.8$V_logs2 results_8 <- loa_maker(bias,V_bias,logs2,V_logs2) #For rho = .5 #bias <- new.5$bias #V_bias <- new.5$V_bias #logs2 <- new.5$logs2 #V_logs2 <- new.5$V_logs2 # results_5 <- loa_maker(bias,V_bias,logs2,V_logs2) #for rho = 1.0 bias <- new1.0$bias V_bias <- new1.0$V_bias logs2 <- new1.0$logs2 V_logs2 <- new1.0$V_logs2 results_1 <- loa_maker(bias,V_bias,logs2,V_logs2) #----------------------------------------------------- #Combine into table #---------------------------------------------------- results <- rbind(results_inc,results_1,results_8) results <- data.frame(results) results$rho <- c("na","1.0",".8") names(results) <- c("studies","bias","sd","tau2","LOA_L","LOA_U","CI_Lm","CI_Um","CI_Lr","CI_Ur","rho") return(results) } ``` Main analyses: full + without low quality + without outliers ```{r} #heart rate metaHR <- runMeta(data[data$Outcome== "HR",]) metaHR tapply(data$Total, data$Total, length) data_quality <- subset(data, Total>"1") metaHR_quality <- runMeta(data_quality[data$Outcome== "HR",]) metaHR_quality #energy expenditure metaEE <- runMeta(data[data$Outcome== "EE",]) metaEE metaEE_quality <- runMeta(data_quality[data$Outcome== "EE",]) metaEE_quality #steps ("NA produce" - it is normal here - repeated measures always "no" for steps) metaSTEPS <- runMeta(data[data$Outcome== "Steps",]) metaSTEPS metaSTEPS_quality <- runMeta(data_quality[data$Outcome== "Steps",]) metaSTEPS_quality ###outliers detection### boxplot(BA_bias~Outcome, dat =data) data_HR <- subset(data, Outcome=="HR") data_EE <- subset(data, Outcome=="EE") data_STEPS <- subset(data, Outcome=="Steps") grubbs.test(data_HR$BA_bias) grubbs.test(data_EE$BA_bias) grubbs.test(data_STEPS$BA_bias) data_HR_outlier <- subset(data_HR, BA_bias>-30.5) data_EE_outlier <- subset(data_EE, BA_bias<61) data_STEPS_outlier <- subset(data_STEPS, BA_bias>-53.07) boxplot(data_EE_outlier$BA_bias) boxplot(data_HR_outlier$BA_bias) boxplot(data_STEPS_outlier$BA_bias) metaHR_outlier <- runMeta(data_HR_outlier) metaHR_outlier metaEE_outlier <- runMeta(data_EE_outlier) metaEE_outlier metaSTEPS_outlier <- runMeta(data_STEPS_outlier) metaSTEPS_outlier ``` Subgroup analyses ```{r} ###participants' characteristics ################################ ### Maybe we do meta-regression, we need to check for the distirbution of the continuous variables. ### If not Ratko/Marco/Issac should provide a cut-off point ### Experts should provide clinical LOA criterias. #CHECK WHAT IS DOING THIS FUNCTION tapply(data$Participants, data$Participants, length) # Drop Unused Levels from Factors data_healthy <- droplevels(data[!data$Participants == 'clinical',]) #data_clin <- subset(data, Participants == 'clinical') metaHR_healthy <- runMeta(data_healthy[data_healthy$Outcome== "HR",]) metaHR_healthy metaEE_healthy <- runMeta(data_healthy[data_healthy$Outcome== "EE",]) metaEE_healthy metaSTEPS_healthy <- runMeta(data_healthy[data_healthy$Outcome== "Steps",]) metaSTEPS_healthy tapply(data$Age_group, data$Age_group, length) data_young <- droplevels(data[!data$Age_group == 'older',]) data_old <- subset(data, Age_group=="older") metaHR_young <- runMeta(data_young[data$Outcome== "HR",]) metaHR_young metaHR_old <- runMeta(data_old[data$Outcome== "HR",]) metaHR_old metaEE_young <- runMeta(data_young[data$Outcome== "EE",]) metaEE_young metaEE_old <- runMeta(data_old[data$Outcome== "EE",]) metaEE_old metaSTEPS_young <- runMeta(data_young[data$Outcome== "Steps",]) metaSTEPS_young metaSTEPS_old <- runMeta(data_old[data$Outcome== "Steps",]) metaSTEPS_old ### Fitbit devices ################## ################## Subgroup only the other device ################## tapply(data$Fitbit, data$Fitbit, length) data_blaze <- subset(data, Fitbit=="Blaze") data_charge2 <- subset(data, Fitbit=="Charge 2") data_chargeHR <- subset(data, Fitbit=="Charge HR") data_Surge <- subset(data, Fitbit=="Surge") metaHR_blaze <- runMeta(data_blaze[data$Outcome== "HR",]) metaHR_blaze metaEE_blaze <- runMeta(data_blaze[data$Outcome== "EE",]) metaEE_blaze metaSTEPS_blaze <- runMeta(data_blaze[data$Outcome== "Steps",]) metaSTEPS_blaze metaHR_charge2 <- runMeta(data_charge2[data$Outcome== "HR",]) metaHR_charge2 metaEE_charge2 <- runMeta(data_charge2[data$Outcome== "EE",]) metaEE_charge2 metaSTEPS_charge2 <- runMeta(data_charge2[data$Outcome== "Steps",]) metaSTEPS_charge2 metaHR_chargeHR <- runMeta(data_chargeHR[data$Outcome== "HR",]) metaHR_chargeHR metaEE_chargeHR <- runMeta(data_chargeHR[data$Outcome== "EE",]) metaEE_chargeHR metaSTEPS_chargeHR <- runMeta(data_chargeHR[data$Outcome== "Steps",]) metaSTEPS_chargeHR metaHR_Surge <- runMeta(data_Surge[data$Outcome== "HR",]) metaHR_Surge metaEE_Surge <- runMeta(data_Surge[data$Outcome== "EE",]) metaEE_Surge metaSTEPS_Surge <- runMeta(data_Surge[data$Outcome== "Steps",]) metaSTEPS_Surge data_versa <- subset(data, Fitbit=="Versa") metaHR_versa <- runMeta(data_versa[data$Outcome== "HR",]) metaHR_versa metaEE_versa <- runMeta(data_versa[data$Outcome== "EE",]) metaEE_versa metaSTEPS_versa <- runMeta(data_versa[data$Outcome== "Steps",]) metaSTEPS_versa ``` ```{r} ### Device placement #################### tapply(data$Fitbit, data$Fitbit, length) table(data$Fitbit) #data_placementOK <- subset(data, Q4_placement==1) data_placementOK <- subset(data, Q4_multiple.devices==1) metaHR_place <- runMeta(data_placementOK[data$Outcome== "HR",]) metaHR_place metaEE_place <- runMeta(data_placementOK[data$Outcome== "EE",]) metaEE_place metaSTEPS_place <- runMeta(data_placementOK[data$Outcome== "Steps",]) metaSTEPS_place ### Type of activity #################### tapply(data$Activities, data$Activities, length) data_cycling <- subset(data, Activities=="Cycling") metaHR_cy <- runMeta(data_cycling[data$Outcome== "HR",]) metaHR_cy metaEE_cy <- runMeta(data_cycling[data$Outcome== "EE",]) metaEE_cy data_daily <- subset(data, Activities=="Daily living") metaHR_dl <- runMeta(data_daily[data$Outcome== "HR",]) metaHR_dl metaEE_dl <- runMeta(data_daily[data$Outcome== "EE",]) metaEE_dl metaSTEPS_dl <- runMeta(data_daily[data$Outcome== "Steps",]) metaSTEPS_dl data_trdm<- subset(data, Activities=="Trdm") metaHR_trdm <- runMeta(data_trdm[data$Outcome== "HR",]) metaHR_trdm metaEE_trdm <- runMeta(data_trdm[data$Outcome== "EE",]) metaEE_trdm metaSTEPS_trdm<- runMeta(data_trdm[data$Outcome== "Steps",]) metaSTEPS_trdm data_owalk<- subset(data, Activities=="Walking") metaHR_owalk <- runMeta(data_owalk[data$Outcome== "HR",]) metaHR_owalk metaEE_owalk <- runMeta(data_owalk[data$Outcome== "EE",]) metaEE_owalk metaSTEPS_owalk <- runMeta(data_owalk[data$Outcome== "Steps",]) metaSTEPS_owalk ### Intensity ############# tapply(data$Intensities, data$Intensities, length) data_modvig<- subset(data, Intensities=="MV") metaHR_modvig <- runMeta(data_modvig[data$Outcome== "HR",]) metaHR_modvig metaEE_modvig <- runMeta(data_modvig[data$Outcome== "EE",]) metaEE_modvig metaSTEPS_modvig <- runMeta(data_modvig[data$Outcome== "Steps",]) metaSTEPS_modvig data_light <- subset(data, Intensities=="Light") metaHR_light <- runMeta(data_light[data$Outcome== "HR",]) metaHR_light metaEE_light <- runMeta(data_light[data$Outcome== "EE",]) metaEE_light metaSTEPS_light <- runMeta(data_light[data$Outcome== "Steps",]) metaSTEPS_light ``` ```{r} sessionInfo() #R version 4.0.3 (2020-10-10) #Platform: x86_64-apple-darwin17.0 (64-bit) #Running under: macOS Mojave 10.14.6 #Matrix products: default #BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib #LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib #Random number generation: # RNG: Mersenne-Twister # Normal: Inversion # Sample: Rounding #locale: #[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8 #attached base packages: #[1] stats graphics grDevices utils datasets methods base #other attached packages: #[1] stringr_1.4.0 outliers_0.14 dplyr_1.0.7 pracma_2.2.9 plyr_1.8.6 casnet_0.2.0 readxl_1.3.1 rio_0.5.16 #loaded via a namespace (and not attached): # [1] tidyselect_1.1.0 xfun_0.18 purrr_0.3.4 haven_2.3.1 lattice_0.20-41 colorspace_1.4-1 vctrs_0.3.8 generics_0.0.2 # [9] utf8_1.1.4 blob_1.2.1 rlang_0.4.11 pillar_1.6.1 foreign_0.8-80 glue_1.4.2 DBI_1.1.0 lifecycle_1.0.0 #[17] munsell_0.5.0 gtable_0.3.0 cellranger_1.1.0 zip_2.1.1 knitr_1.30 forcats_0.5.0 curl_4.3 fansi_0.4.1 #[25] Rcpp_1.0.6 scales_1.1.1 farver_2.0.3 ggplot2_3.3.2 hms_0.5.3 digest_0.6.25 stringi_1.5.3 openxlsx_4.2.3 #[33] grid_4.0.3 cli_2.1.0 tools_4.0.3 magrittr_2.0.1 tibble_3.0.4 crayon_1.3.4 tidyr_1.1.3 pkgconfig_2.0.3 #[41] ellipsis_0.3.2 Matrix_1.2-18 data.table_1.13.2 assertthat_0.2.1 rstudioapi_0.11 R6_2.4.1 igraph_1.2.6 compiler_4.0.3 #[49] invctr_0.1.0 ```