# @example # qiat.parse.quoted(df, 'ResponseId', 'Q_47') # qiat.parse(df, ResponseId, Q_47) # # @returns data frame with parsed data, rows with NA or '' are omitted. qiat.parse.quoted = function(df, id, data){ # @TODO does not work? # if (is.factor(df[id])) stop(paste0(data, ' column must not be a factor')) filteredDF = df[df[,data]!='' & !is.na(df[,data]) ,] # parse data -> list of data data frames csvList = lapply(filteredDF[,data], function(str) tryCatch({ read.csv(text=str,stringsAsFactors = FALSE) }, error = function(err){ message('woa there is a malformed csv here') return(NA) } )) #browser() # add id to each data DF mask = which(sapply(csvList,nrow)>0) dataPages = mapply( function(id, df) cbind(id,df), filteredDF[mask,id], csvList[mask], SIMPLIFY = FALSE ) if (!length(dataPages)) { return(data.frame()) } # concat pages do.call(rbind,dataPages) } qiat.parse = function(df, id, data){ qiat.parse.quoted(df, deparse(substitute(id)), deparse(substitute(data))) } ###Read your file here. #We're reading two files from our two examples. df.no = read.csv(paste(dir, 'exampleBIAT_update.csv',sep = '\\')) df.img = read.csv(paste(dir, 'exampleBIATimage_update.csv',sep = '\\')) #We bind the two datasets together, but you're probably going to have only one file df <- rbind(df.no[,c('StartDate','EndDate','ResponseId', 'Q2')], df.img[,c('StartDate','EndDate','ResponseId', 'Q2')]) #Q2 is the question that displayed the IAT and all the IAT data was saved under Q2. #If the text 'block' does not appear in Q2's response, then the IAT data were not saved in that question. Somehow, the IAT was not completed, or perhaps this is from an earlier stage before your IAT was setup correctly. df <- df[which(grepl('block', df$Q2)),] #The escaping in the data sometimes uses "" which we need to change to " for the parsing. #Note Q3 in my data was the columns that saved the IAT data for each participant. It might have a different name in your data. library(kutils) df$IAT <- mgsub(df$Q2, pattern = c('""'), replacement = c('"')) #Use Elad's parsing function df2 <- qiat.parse.quoted(df = df, id='ResponseId', data = 'IAT') df <- rbind(df.no[,c('StartDate','EndDate','ResponseId', 'Q2')], df.img[,c('StartDate','EndDate','ResponseId', 'Q2')]) #Q2 is the question that displayed the IAT and all the IAT data was saved under Q2. #If the text 'block' does not appear in Q2's response, then the IAT data were not saved in that question. Somehow, the IAT was not completed, or perhaps this is from an earlier stage before your IAT was setup correctly. df <- df[which(grepl('block', df$Q2)),] library(kutils) df$IAT <- mgsub(df$Q2, pattern = c('""'), replacement = c('"')) View(df) df2 <- qiat.parse.quoted(df = df, id='ResponseId', data = 'IAT') nrow(df) df2 <- qiat.parse.quoted(df = df[5:7,], id='ResponseId', data = 'IAT') df[5:7,] df2 <- qiat.parse.quoted(df = df, id='ResponseId', data = 'IAT') # Copied from Elad Zlotnick (from here: https://github.com/eladzlot/minnojs-qiat/blob/master/qiat.R) # Parse csv generated by minnoJS # # @param df a data frame # @param id the column name holding the ids # @param data the column name holding the data (we assume the data holds an array of unnested objects) # # @example # qiat.parse.quoted(df, 'ResponseId', 'Q_47') # qiat.parse(df, ResponseId, Q_47) # # @returns data frame with parsed data, rows with NA or '' are omitted. qiat.parse.quoted = function(df, id, data){ # @TODO does not work? # if (is.factor(df[id])) stop(paste0(data, ' column must not be a factor')) browser() filteredDF = df[df[,data]!='' & !is.na(df[,data]) ,] # parse data -> list of data data frames csvList = lapply(filteredDF[,data], function(str) tryCatch({ read.csv(text=str,stringsAsFactors = FALSE) }, error = function(err){ message('woa there is a malformed csv here') return(NA) } )) #browser() # add id to each data DF mask = which(sapply(csvList,nrow)>0) dataPages = mapply( function(id, df) cbind(id,df), filteredDF[mask,id], csvList[mask], SIMPLIFY = FALSE ) if (!length(dataPages)) { return(data.frame()) } # concat pages do.call(rbind,dataPages) } qiat.parse = function(df, id, data){ qiat.parse.quoted(df, deparse(substitute(id)), deparse(substitute(data))) } #Use Elad's parsing function df2 <- qiat.parse.quoted(df = df, id='ResponseId', data = 'IAT') View(dataPages) View(csvList) View(csvList) names(dataPages) names(csvList[[1]]) names(dataPages[[1]]) names(csvList[[2]]) names(csvList[[3]]) names(csvList[[4]]) names(csvList[[5]]) names(csvList[[6]]) names(csvList[[7]]) names(datapge[[7]]) names(datapage[[7]]) names(dataPages[[7]]) names(dataPages[[5]]) names(dataPages[[6]]) df2 <- qiat.parse.quoted(df = df[6:7,], id='ResponseId', data = 'IAT') names(csvList[[1]]) names(csvList[[2]]) names(csvList[[3]]) df$StartDate df$StartDate df$StartDate[2] df$StartDate[3] df$StartDate[4] df$StartDate[1:7] df$StartDate[1:6] View(df) df[1] df$EndDate[2] df$EndDate[3] df$EndDate[4] df$EndDate[5] df$EndDate[6] df$EndDate[7] df[c(4,7),] df$EndDate[c(4,7),] df2 <- qiat.parse.quoted(df = df[c(4,7),], id='ResponseId', data = 'IAT') #We bind the two datasets together, but you're probably going to have only one file df <- rbind(df.no[,c('StartDate','EndDate','ResponseId', 'Q2')], df.img[,c('StartDate','EndDate','ResponseId', 'Q2')]) #NO NEED FOR THE NEXT LINE IN YOUR OWN DATA. In our test data, the other rows had data with some errors df = df[c(4,7),] #Q2 is the question that displayed the IAT and all the IAT data was saved under Q2. #If the text 'block' does not appear in Q2's response, then the IAT data were not saved in that question. Somehow, the IAT was not completed, or perhaps this is from an earlier stage before your IAT was setup correctly. df <- df[which(grepl('block', df$Q2)),] #The escaping in the data sometimes uses "" which we need to change to " for the parsing. #Note Q3 in my data was the columns that saved the IAT data for each participant. It might have a different name in your data. library(kutils) df$IAT <- mgsub(df$Q2, pattern = c('""'), replacement = c('"')) #Use Elad's parsing function df2 <- qiat.parse.quoted(df = df, id='ResponseId', data = 'IAT') # Copied from Elad Zlotnick (from here: https://github.com/eladzlot/minnojs-qiat/blob/master/qiat.R) # Parse csv generated by minnoJS # # @param df a data frame # @param id the column name holding the ids # @param data the column name holding the data (we assume the data holds an array of unnested objects) # # @example # qiat.parse.quoted(df, 'ResponseId', 'Q_47') # qiat.parse(df, ResponseId, Q_47) # # @returns data frame with parsed data, rows with NA or '' are omitted. qiat.parse.quoted = function(df, id, data){ # @TODO does not work? # if (is.factor(df[id])) stop(paste0(data, ' column must not be a factor')) filteredDF = df[df[,data]!='' & !is.na(df[,data]) ,] # parse data -> list of data data frames csvList = lapply(filteredDF[,data], function(str) tryCatch({ read.csv(text=str,stringsAsFactors = FALSE) }, error = function(err){ message('woa there is a malformed csv here') return(NA) } )) #browser() # add id to each data DF mask = which(sapply(csvList,nrow)>0) dataPages = mapply( function(id, df) cbind(id,df), filteredDF[mask,id], csvList[mask], SIMPLIFY = FALSE ) if (!length(dataPages)) { return(data.frame()) } # concat pages do.call(rbind,dataPages) } qiat.parse = function(df, id, data){ qiat.parse.quoted(df, deparse(substitute(id)), deparse(substitute(data))) } ###Read your file here. #We're reading two files from our two examples. df.no = read.csv(paste(dir, 'exampleBIAT_update.csv',sep = '\\')) df.img = read.csv(paste(dir, 'exampleBIATimage_update.csv',sep = '\\')) #We bind the two datasets together, but you're probably going to have only one file df <- rbind(df.no[,c('StartDate','EndDate','ResponseId', 'Q2')], df.img[,c('StartDate','EndDate','ResponseId', 'Q2')]) #NO NEED FOR THE NEXT LINE IN YOUR OWN DATA. In our test data, the other rows had data with some errors df = df[c(4,7),] #Q2 is the question that displayed the IAT and all the IAT data was saved under Q2. #If the text 'block' does not appear in Q2's response, then the IAT data were not saved in that question. Somehow, the IAT was not completed, or perhaps this is from an earlier stage before your IAT was setup correctly. df <- df[which(grepl('block', df$Q2)),] #The escaping in the data sometimes uses "" which we need to change to " for the parsing. #Note Q3 in my data was the columns that saved the IAT data for each participant. It might have a different name in your data. library(kutils) df$IAT <- mgsub(df$Q2, pattern = c('""'), replacement = c('"')) #Use Elad's parsing function df2 <- qiat.parse.quoted(df = df, id='ResponseId', data = 'IAT') View(df) df <- rbind(df.no[,c('StartDate','EndDate','ResponseId', 'Q2')], df.img[,c('StartDate','EndDate','ResponseId', 'Q2')]) View(df) df <- rbind(df.no[,c('StartDate','EndDate','ResponseId', 'Q2')], df.img[,c('StartDate','EndDate','ResponseId', 'Q2')]) #Q2 is the question that displayed the IAT and all the IAT data was saved under Q2. #If the text 'block' does not appear in Q2's response, then the IAT data were not saved in that question. Somehow, the IAT was not completed, or perhaps this is from an earlier stage before your IAT was setup correctly. df <- df[which(grepl('block', df$Q2)),] #NO NEED FOR THE NEXT LINE IN YOUR OWN DATA. In our test data, the other rows had data with some errors df = df[c(4,7),] #The escaping in the data sometimes uses "" which we need to change to " for the parsing. #Note Q3 in my data was the columns that saved the IAT data for each participant. It might have a different name in your data. library(kutils) df$IAT <- mgsub(df$Q2, pattern = c('""'), replacement = c('"')) #Use Elad's parsing function df2 <- qiat.parse.quoted(df = df, id='ResponseId', data = 'IAT') #Sanity check: see the names of the variables. nrow(df2) names(df2) #If successful, these should be: "id" "block" "trial" "cond" "comp" "type" "cat" "stim" "resp" "err" "rt" "d" "fb" "bOrd" table(df2$block, exclude=NULL) iat.raw <- df2[which(df2$block > 1),] table(iat.raw$cond, exclude=NULL) iat.raw$cong <- ifelse(iat.raw$cond=='White people/Pleasant', 'cong', ifelse(iat.raw$cond=='Black people/Pleasant', 'incong', NA)) table(iat.raw$cong, exclude=NULL) View(df2) iat.raw$cong <- ifelse(iat.raw$cond=='White people/Pleasant', 'cong', ifelse(iat.raw$cond %in% c('Black people/Pleasant','Black People/Pleasant'), 'incong', NA)) table(iat.raw$cong, exclude=NULL) iat.raw <- iat.raw[!is.na(iat.raw$cong),] table(df2$block, exclude=NULL) #Keep only the critical blocks (the first block was practice) iat.raw <- df2[which(df2$block > 1),] #Make sure the 'compatible' trials are indeed what you expected them to be. table(iat.raw$cond, exclude=NULL) iat.raw$cong <- ifelse(iat.raw$cond=='White people/Pleasant', 'cong', ifelse(iat.raw$cond %in% c('Black people/Pleasant','Black People/Pleasant'), 'incong', NA)) table(iat.raw$cong, exclude=NULL) iat.raw <- iat.raw[which(!is.na(iat.raw$cong)),] table(iat.raw$block) iat.raw$blockName <- ifelse(iat.raw$comp == 'cong', ifelse(iat.raw$block < 6, "B3", ifelse(iat.raw$block > 5, "B4", NA)), ifelse(iat.raw$comp == 'incong', ifelse(iat.raw$block < 6, "B6", ifelse(iat.raw$block >5, "B7", NA)), NA)) iat.raw$blockName <- ifelse(iat.raw$cong == 'cong', ifelse(iat.raw$block < 6, "B3", ifelse(iat.raw$block > 5, "B4", NA)), ifelse(iat.raw$cong == 'incong', ifelse(iat.raw$block < 6, "B6", ifelse(iat.raw$block >5, "B7", NA)), NA)) table(iat.raw$blockName, exclude=NULL) iat.raw <- iat.raw[which(!is.na(iat.raw$blockName)),] class(iat.raw$rt) class(iat.raw$err) library(doBy) nTrials.long <- summaryBy(formula = err ~ id + blockName, data=iat.raw, FUN = length) library(reshape2) nTrials <- dcast(nTrials.long, id ~ blockName, value.var = 'err.length') View(nTrials) ##Sanity check. For each participant, we expect a certain number of trials for in each block. ##We will indicate whether the participant has the expected number of trials within each of the critical block. library(doBy) nTrials.long <- summaryBy(formula = err ~ id + blockName, data=iat.raw, FUN = length) library(reshape2) nTrials <- dcast(nTrials.long, id ~ blockName, value.var = 'err.length') #You can change those number if your BIAT had different numbers library(dplyr) nTrials <- nTrials %>% mutate(ntrials.ok = case_when( nTrials$B3==32 & nTrials$B4==32 & nTrials$B6==32 & nTrials$B7==32 ~ TRUE, TRUE ~ FALSE )) #If not all are TRUE, then you some participants are missing data table(nTrials$ntrials.ok, exclude=NULL) library(IAT) iatscore <- cleanIAT(iat.raw, block_name="blockName", trial_blocks = c("B3", "B4", "B6", "B7"), session_id="id", trial_latency="rt", trial_error = "err", v_error=2, v_extreme=2, v_std=1) #v_error=2 means recode error latency to m+600, v_error=1 mean the standard (onset of stimuli until the correct response is pressed). v_extreme=2(current standard)=delete trial latencies < 400ms. v_std=1 (current standard), block SD is performed including error trials #How many participants were excluded for problematic performance? table(iatscore$SUBEXCL, exclude=NULL) #Summary of those who were not excluded. summary(iatscore$IAT[which(iatscore$SUBEXCL==0)]) ##Simple graph library(ggplot2) iatscore$dummy <- '' box_plot <- ggplot(iatscore, aes(x = dummy, y = IAT)) box_plot + geom_boxplot() + geom_dotplot(binaxis = 'y', dotsize = 0.4, stackdir = 'center') + theme_classic() + stat_summary(geom = "point", fun.y = "mean", col = "black", size = 3, shape = 24, fill = "grey") iatscore$dummy <- NULL #Get the bord variable block.order1 <- df2[which(!is.na(df2$bOrd) & nchar(df2$bOrd)>0),c('id', 'bOrd')] #Change to your own directory (where you keep the data file) dir = 'C:\\Users\\yoav\\Documents\\bigfiles\\qualtrics.iat\\gal.elinor\\raw' # Copied from Elad Zlotnick (from here: https://github.com/eladzlot/minnojs-qiat/blob/master/qiat.R) # Parse csv generated by minnoJS # # @param df a data frame # @param id the column name holding the ids # @param data the column name holding the data (we assume the data holds an array of unnested objects) # # @example # qiat.parse.quoted(df, 'ResponseId', 'Q_47') # qiat.parse(df, ResponseId, Q_47) # # @returns data frame with parsed data, rows with NA or '' are omitted. qiat.parse.quoted = function(df, id, data){ # @TODO does not work? # if (is.factor(df[id])) stop(paste0(data, ' column must not be a factor')) filteredDF = df[df[,data]!='' & !is.na(df[,data]) ,] # parse data -> list of data data frames csvList = lapply(filteredDF[,data], function(str) tryCatch({ read.csv(text=str,stringsAsFactors = FALSE) }, error = function(err){ message('woa there is a malformed csv here') return(NA) } )) #browser() # add id to each data DF mask = which(sapply(csvList,nrow)>0) dataPages = mapply( function(id, df) cbind(id,df), filteredDF[mask,id], csvList[mask], SIMPLIFY = FALSE ) if (!length(dataPages)) { return(data.frame()) } # concat pages do.call(rbind,dataPages) } qiat.parse = function(df, id, data){ qiat.parse.quoted(df, deparse(substitute(id)), deparse(substitute(data))) } ###Read your file here. #We're reading two files from our two examples. df.no = read.csv(paste(dir, 'exampleBIAT_update.csv',sep = '\\')) df.img = read.csv(paste(dir, 'exampleBIATimage_update.csv',sep = '\\')) ###Read your file here. #We're reading two files from our two examples. df.no = read.csv(paste(dir, 'exampleBIAT_update.csv',sep = '\\')) df.img = read.csv(paste(dir, 'exampleBIATimage_update.csv',sep = '\\')) #We bind the two datasets together, but you're probably going to have only one file df <- rbind(df.no[,c('StartDate','EndDate','ResponseId', 'Q2')], df.img[,c('StartDate','EndDate','ResponseId', 'Q2')]) #Q2 is the question that displayed the IAT and all the IAT data was saved under Q2. #If the text 'block' does not appear in Q2's response, then the IAT data were not saved in that question. Somehow, the IAT was not completed, or perhaps this is from an earlier stage before your IAT was setup correctly. df <- df[which(grepl('block', df$Q2)),] #NO NEED FOR THE NEXT LINE IN YOUR OWN DATA. In our test data, the other rows had data with some errors df = df[c(4,7),] #The escaping in the data sometimes uses "" which we need to change to " for the parsing. #Note Q3 in my data was the columns that saved the IAT data for each participant. It might have a different name in your data. library(kutils) df$IAT <- mgsub(df$Q2, pattern = c('""'), replacement = c('"')) #Use Elad's parsing function df2 <- qiat.parse.quoted(df = df, id='ResponseId', data = 'IAT') #Sanity check: see the names of the variables. nrow(df2) names(df2) #If successful, these should be: "id" "block" "trial" "cond" "type" "cat" "stim" "resp" "err" "rt" "d" "fb" "bOrd" table(df2$block, exclude=NULL) #Keep only the critical blocks (the first block was practice) iat.raw <- df2[which(df2$block > 1),] #Make sure the 'compatible' trials are indeed what you expected them to be. table(iat.raw$cond, exclude=NULL) iat.raw$cong <- ifelse(iat.raw$cond=='White people/Pleasant', 'cong', ifelse(iat.raw$cond %in% c('Black people/Pleasant','Black People/Pleasant'), 'incong', NA)) table(iat.raw$cong, exclude=NULL) iat.raw <- iat.raw[which(!is.na(iat.raw$cong)),] table(iat.raw$block) #The combination of compatibility condition and block number determined the blockName for the scoring function. iat.raw$blockName <- ifelse(iat.raw$cong == 'cong', ifelse(iat.raw$block < 6, "B3", ifelse(iat.raw$block > 5, "B4", NA)), ifelse(iat.raw$cong == 'incong', ifelse(iat.raw$block < 6, "B6", ifelse(iat.raw$block >5, "B7", NA)), NA)) #Make sure you only have B3,B4,B6, and B7 and no NAs. table(iat.raw$blockName, exclude=NULL) #Not supposed to have any NA at this point. So, if you see here, investigate why #(e.g., perhaps some of your data has different cond values than expected, if you changed the cond after running a few participants) iat.raw <- iat.raw[which(!is.na(iat.raw$blockName)),] #Verify that both are numbers (probably, integer) class(iat.raw$rt) class(iat.raw$err) ##Sanity check. For each participant, we expect a certain number of trials for in each block. ##We will indicate whether the participant has the expected number of trials within each of the critical block. library(doBy) nTrials.long <- summaryBy(formula = err ~ id + blockName, data=iat.raw, FUN = length) library(reshape2) nTrials <- dcast(nTrials.long, id ~ blockName, value.var = 'err.length') #You can change those number if your BIAT had different numbers library(dplyr) nTrials <- nTrials %>% mutate(ntrials.ok = case_when( nTrials$B3==32 & nTrials$B4==32 & nTrials$B6==32 & nTrials$B7==32 ~ TRUE, TRUE ~ FALSE )) #If not all are TRUE, then you some participants are missing data table(nTrials$ntrials.ok, exclude=NULL) iat.raw$blockName <- ifelse(iat.raw$cong == 'cong', "B3", ifelse(iat.raw$cong == 'incong', "B6", NA)) #Make sure you only have B3,B4,B6, and B7 and no NAs. table(iat.raw$blockName, exclude=NULL) iat.raw <- iat.raw[which(!is.na(iat.raw$blockName)),] library(doBy) nTrials.long <- summaryBy(formula = err ~ id + block, data=iat.raw, FUN = length) library(reshape2) nTrials <- dcast(nTrials.long, id ~ blockName, value.var = 'err.length') nTrials <- dcast(nTrials.long, id ~ block, value.var = 'err.length') nTrials nTrials <- nTrials %>% mutate(ntrials.ok = case_when( nTrials$`2`==16 & nTrials$`3`==16 & nTrials$`5`==16 & nTrials$`6`==16 & nTrials$`7`==16 & nTrials$`8`==16 & nTrials$`9`==16 ~ TRUE, TRUE ~ FALSE )) #If not all are TRUE, then you some participants are missing data table(nTrials$ntrials.ok, exclude=NULL) iat.raw$trial.latency <- ifelse(iat.raw$rt<400, 400, ifelse(iat.raw$rt>2000, 2000, iat.raw$rt)) min(iat.raw$trial.latency) max(iat.raw$trial.latency) iat.raw$blockName <- ifelse(iat.raw$cong == 'cong', ifelse(iat.raw$block %in% c(2,3,6,7), 'B3', ifelse(iat.raw$block %in% c(4,5,8,9), 'B4', NA)), ifelse(iat.raw$cong == 'incong', ifelse(iat.raw$block %in% c(2,3,6,7), 'B6', ifelse(iat.raw$block %in% c(4,5,8,9), 'B7', NA)), NA)) #Make sure you only have B3,B4,B6, and B7 and no NAs. table(iat.raw$blockName, exclude=NULL) iat.raw <- iat.raw[which(!is.na(iat.raw$blockName)),] iat.raw1 <- iat.raw[which(iat.raw$block %in% c(2,3,4,5)),] iat.raw2 <- iat.raw[which(iat.raw$block %in% c(6,7,8,9)),] iatscore1 <- cleanIAT(iat.raw1, block_name="blockName", trial_blocks = c("B3", "B4", "B6", "B7"), session_id="id", trial_latency="trial.latency", trial_error = "err", v_error=2, v_extreme=2, v_std=1) library(IAT) iatscore1 <- cleanIAT(iat.raw1, block_name="blockName", trial_blocks = c("B3", "B4", "B6", "B7"), session_id="id", trial_latency="trial.latency", trial_error = "err", v_error=2, v_extreme=2, v_std=1) names(iatscore1) names(iatscore1)[names(iatscore1)!='id'] <- paste0(names(iatscore1)[names(iatscore1)!='id'],'.1') names(iatscore1) iatscore2 <- cleanIAT(iat.raw2, block_name="blockName", trial_blocks = c("B3", "B4", "B6", "B7"), session_id="id", trial_latency="trial.latency", trial_error = "err", v_error=2, v_extreme=2, v_std=1) #v_error=2 means recode error latency to m+600, v_error=1 mean the standard (onset of stimuli until the correct response is pressed). v_extreme=2(current standard)=delete trial latencies < 400ms. v_std=1 (current standard), block SD is performed including error trials names(iatscore2) names(iatscore2)[names(iatscore2)!='id'] <- paste0(names(iatscore2)[names(iatscore2)!='id'],'.1') names(iatscore2) iatscore2 <- cleanIAT(iat.raw2, block_name="blockName", trial_blocks = c("B3", "B4", "B6", "B7"), session_id="id", trial_latency="trial.latency", trial_error = "err", v_error=2, v_extreme=2, v_std=1) #v_error=2 means recode error latency to m+600, v_error=1 mean the standard (onset of stimuli until the correct response is pressed). v_extreme=2(current standard)=delete trial latencies < 400ms. v_std=1 (current standard), block SD is performed including error trials names(iatscore2) names(iatscore2)[names(iatscore2)!='id'] <- paste0(names(iatscore2)[names(iatscore2)!='id'],'.2') names(iatscore2) iatscore <- merge(iatscore1, iatscore2, all = T, by='id') View(iatscore) iatscore$IAT <- rowMeans(iatscore[,c('IAT1.1', 'IAT1.2', 'IAT2.1', 'IAT2.2')], na.rm = T) View(iatscore) iatscore$SUBEXCL = ifelse(iatscore$SUBEXCL.1>iatscore$SUBEXCL.2, iatscore$SUBEXCL.1, iatscore$SUBEXCL.2) table(iatscore$SUBEXCL, exclude=NULL) summary(iatscore$IAT[which(iatscore$SUBEXCL==0)]) library(ggplot2) iatscore$dummy <- '' box_plot <- ggplot(iatscore, aes(x = dummy, y = IAT)) box_plot + geom_boxplot() + geom_dotplot(binaxis = 'y', dotsize = 0.4, stackdir = 'center') + theme_classic() + stat_summary(geom = "point", fun.y = "mean", col = "black", size = 3, shape = 24, fill = "grey") iatscore$dummy <- NULL