## This approach is based on a folder with several data files to upload to an empty data base ## Bulk conversion of stata files to CSV library(haven) library(dplyr) ## Export output files?? exp_out=TRUE id_data=c("record_id","cpr","rtreat") # Data from keyfile is all marked as identifier auto_upload=FALSE ## Loading filenames setwd("/Volumes/Data/") files<-list.files("STATA13/.", pattern="*.dta", full.names=FALSE) ## Loading randomisation key file, keeping only necessary variables, ## renaming rnumb to record_id key<-as_factor(read_dta("/Volumes/Data/STATA13/inkl_rev_v13.dta")) %>% filter(.,rnumb!="999") %>% select(.,c("rnumb","cpr","rtreat")) %>% rename(.,record_id=rnumb) ## Excluding undesired datasets ## DAP, logfile, randomisation log, randomisation key ## These will be kept and uploaded seperately files<-files[!grepl("dap",files)& !grepl("transactions",files)& !grepl("rand",files)] ## Get "first name" from all files fnames<-sapply(strsplit(files, "[_]"),"[[",1) # Just discovered the subset functions "[" and "[[". Wow! ## Loading data sets to list ls<-list() for (i in 1:length(files)){ ## Get dataset, factorise labels, ## join with key-file, drop cpr and remove NA's d<-left_join(key, as_factor(read_dta(paste0("STATA13/",files[i]))),by="cpr") %>% # select(.,!"cpr") %>% filter(.,!is.na(SYS_SITE)) ## SYS_DATA is in all sets, but fmed, uses SYS_SITE d<-data.frame(d) ## Append dataframe to list ls[[i]]<-d names(ls)[i]<-fnames[i] ## Export original data frame if (exp_out){ write.csv(d,paste0("REDCap/orig/",fnames[i],".csv"),row.names = FALSE) } } ## DAP variables to include, incl record_id vars<-c("record_id","hojde","vaegt","vaegt_anslaaet","rygning","alkohol","civil", "bolig","diabetes","hyperten","perifer_arteriel","atriefli","ami", "tidl_tci","trombolyse","trombektomi","nhiss_foer") # ## Importing and subsetting DAP-data- All in one style dap_d<-read_dta("/Volumes/Data/STATA13/dap_rapport_2017.dta") %>% ## Loading file with correct name filter(.,!duplicated(cpr))%>% ## Limiting to first event left_join(key,.,by="cpr")%>% ## Join with key for record_id's, to only keep entries also in key select(.,all_of(vars)) ## Select only specified variables, leaving out cpr's ls[[length(ls)+1]]<-dap_d names(ls)[length(ls)]<-"reg" ## Exporting attributes for (i in 1:length(ls)) { ## Lists all attributes for export l <- lapply(ls[[i]], attr, "label") for (j in 1:length(l)){ l[[j]]<-ifelse(is.null(l[[j]]),"No attr",l[[j]]) } la<-data.frame(names=names(l),attr=unlist(l)) ## Export individual attribute files for data overview if (exp_out){ write.csv(la,paste0("REDCap/attr/attr_",fnames[i],".csv"),row.names = FALSE) } } ## Naming and splitting in unique instruments for DataDictionary creation and data upload ## Leave inklusion file, adverse events and other medication. ## These are not needed for upload or will be uploaded seperately r_sel<-!(grepl("inkl",files)|grepl("ae_",files)|grepl("medicin",files)) # Limiting name variables # r_files<-files[r_sel] r_fnames<-c(fnames[r_sel],"reg") # Selecting elements in list r_ls<-"["(ls,names(ls)%in%r_fnames) # New list for wrangling to rename variables and splitting by instance r_lup<-list() for (i in 1:length(r_ls)){ ## Suffix generic variable names d<-r_ls[[i]] name<-names(r_ls)[[i]] dn<-!(grepl(name,colnames(d))|colnames(d)%in%id_data)## Test colnames that does not contain instrument name colnames(d)[dn]<-paste0(name,"_",colnames(d)[dn]) ## Adds suffix to colnames to ensure unique names # First entry is omitted, as this is the record_id ## Test for if conditions ## Uses redundant double if, as else wasn't consistent test<-grepl("INSTANCE",colnames(d),ignore.case = T ) if (any(test)){ d[,test]<-factor(d[,test]) # Factorise to secure ordering ins<-levels(d[,test]) # Instance numbers drawn from factor levels ds<-split(d[,!test] ,d[,test]) # Splits by instance and drops instance variable names(ds)<-paste0(name,"_",ins) # Names frames by instrument, appends instance for (j in 1:length(ds)){ colnames(ds[[j]])[!colnames(ds[[j]])%in%id_data]<-paste0(colnames(ds[[j]])[!colnames(ds[[j]])%in%id_data],"_",ins[j]) ## Append instance number } r_lup<-append(r_lup,ds) } if (!any(test)) { r_lup[[length(r_lup)+1]]<-d names(r_lup)[length(r_lup)]<-name } } ## Load REDCap instrument example file for variable names icname<-colnames(read.csv("/Volumes/Data/REDCap/examples/examlpe instrument.csv")) dd<-data.frame(matrix(ncol = length(icname))) ## Data frame to collect all colnames(dd)<-icname ## Instrument for DataDictionary ## Format dataset for REDCap upload for (i in 1:length(r_lup)){ dd_i<-data.frame(matrix(ncol = length(icname),nrow = ncol("[["(r_lup,i)))) ## Data frame to collect all colnames(dd_i)<-icname ## for easier reading ## Variable names dd_i[1]<-colnames("[["(r_lup,i)) ## Form Name dd_i[2]<-names(r_lup)[i] ## Field Type # dd_i[4]<-ifelse(sapply(r_lup[[i]], class)=="factor","radio","text") dd_i[4]<-"text" ## Field Label ## Using original attributes as field labels fl<-lapply(r_lup[[i]], attr, "label") for (j in 1:length(fl)){ fl[[j]]<-ifelse(is.null(fl[[j]]), names(fl)[[j]], fl[[j]]) ## If no attributes, variable name is used as "placeholder" } dd_i[5]<-unlist(fl) ## Choices # for (j in 1:ncol(r_lup[[i]])){ # if (is.factor(r_lup[[i]][[j]])){ # lvl<-levels(r_lup[[i]][[j]]) # lvl_ch<-paste("1,",lvl[1]) # for (k in 2:length(lvl)){ # lvl_ch<-c(paste0(lvl_ch," | ",k,", ",lvl[k])) # } # dd_i[j,6]<-lvl_ch # } # } ## Text Validation ## Only used for date and time data # for (j in 1:ncol(r_lup[[i]])){ # dd_i[j,8]<-case_when(class(r_lup[[i]][[j]])[1]%in%c("POSIXct","POSIXt") ~"datetime_seconds_ymd", # class(r_lup[[i]][[j]])[1]%in%c("Date") ~"date_ymd") # } ## Merge all dd<-rbind(dd,dd_i) if (exp_out){ # dir.create(file.path("/Volumes/Data/REDCap/data",names(r_lup)[[i]])) write.csv(r_lup[[i]],paste0("/Volumes/Data/REDCap/data/",names(r_lup)[[i]],".csv"),row.names = FALSE) } } # Readies the DataDictionary for export by limiting to unique identifier (leaving out multiple record ids) and omitting NAs dd_exp<-dd %>% filter(.,!(duplicated(Variable...Field.Name)|is.na(Variable...Field.Name))) ## Marking identifier variables dd_exp$Identifier.[dd_exp$Variable...Field.Name%in%id_data]<-"y" write.csv(dd_exp,"/Volumes/Data/REDCap/data_dictionary.csv",row.names = FALSE,na="") ## ONE DATA SET TO RULE THEM ALL ds_all<-key for (i in names(r_lup)){ ds_all<-left_join(ds_all,"[["(r_lup,i)) ## All non-identifier variable names are unique, joining variable is not specified. } colnames(ds_all)<-colnames(ds_all)%>%tolower() ## All names in REDCap are lower case. if (exp_out){ write.csv(ds_all,"/Volumes/Data/REDCap/complete_dataset.csv",row.names = FALSE,na="") } ## ============================================================================= ## REDCap upload ## - worked, but headers should be lower case ## ## - 02aug22 not allowed to export or import. ## Try manual data upload after attribute merge. ## ============================================================================= if (auto_upload==TRUE){ ## Trying out native piping token_talos<-read.csv("/Users/au301842/talos_redcap_token.csv",colClasses = "character")|> names()|> # (\(x){ ## Shorthand for "anonymous lambda function". New "_" placeholder does not work. # substr(x,2,33)})()|> suppressWarnings() ## See https://towardsdatascience.com/understanding-the-native-r-pipe-98dea6d8b61b" ## OBS: new placeholder "_" in >4.2. stts<-REDCapR::redcap_write(ds=ds_all, redcap_uri = "https://redcap.rm.dk/api/", token = token_talos ) records_mod <- REDCapR::redcap_read_oneshot( redcap_uri = "https://redcap.rm.dk/api/", token = token_talos ) } ## Notes: ## - Validation and data selection options are not applied, as these are not necessary for upload.