Creating spaghetti plots of eye-tracking data in R

Posted on Updated on


I’ve been working on consolidating all the different R functions I’ve written over the years for plotting my eye-tracking data and creating just one amazing super-function (based on the ggplot2 package) that can do it all. Here’s a first attempt that anybody with the right kind of dataset should be able to use to create plots like the ones below (generated from fake data. The R code that generates the data is included at the end of the post). If you find this code helpful, please consider acknowledging it via the following URL in your paper/presentation to spread the word:
https://hlplab.wordpress.com/2012/02/27/creating-spaghetti-plots-of-eye-tracking-data-in-r/

Left: Empirical means with error bars indicating standard error for four experimental conditions. Contrast presence is coded in color, adjective type in line type. The first vertical line indicates adjective onset, the second ones indicate mean noun onset in each contrast condition. Right: Smoothed model estimates of proportions in each condition, with ribbons indicating 95% confidence intervals. Data from different subjects is plotted in different panels.

Here is the R function. See the TODO section for what is not yet implemented. Standard errors are computed via the “se” function that is provided below. See way down below for the code that generates the fake data used to create above plot, plus some sample calls to the spaghetti function. The easiest way to figure out how this function works is to read the comments provided in the function head and to run some of the sample calls provided below. Questions, comments, feature requests welcome!

library(ggplot2)

spaghetti = function(
# version 0.1
# written by jdegen@bcs.rochester.edu
# 01/11/2012

# TODO:
# 1. Implement plotting of multiple regions in one plot. Currently only plotting one region at a time (by conditions) is possible. But sometimes we want to plot looks to eg target/competitor/distractor in one plot.
# 2. Give users more control over where to add vertical lines. So far, only adding one vertical line relative to one event and another (or group of others) relative to another event. Important is that there's a time column for each of these events. Ideally, you would also have the option of passing a vector of times, where each designates an x intercept. Or a combination of the two.
# 3. Add shapevariable as an option (important for writing papers where you can only do black and white prints and you want to use shapes to distinguish conditions).
# 4. Add "TimePlot" to vector of meltids so user doesn't have to do it
# 5. Generate meltfactors automatically by probing the type of variables in meltids
# 6. If errbarvars is NA, create vector automatically by creating vector of all of {color/line/size/facet}variable that aren't NA

# assumes that there are no rows where the value in the column coding region that is currently being looked at is empty. ie for ExAnalysis data.frames: rp_RegionType != ""
	data,
	meltids=c("TimePlot"), # vector of column names (as strings) to melt the dataset by (i.e. that you want to have the option of plotting by)
	meltfactors, # the variables in meltids that are factors. Necessary for as.factor(as.character) calls		
	region=c("Target"), # vector of regions - region names must be column names in data
	colorvariable=NA, # variable you want to assign different colors by
	colormapping=NA, # a data.frame with two factor columns. One column with name of variable you want to map different colors to. One called "Color" with the color for each factor level. Data.frame row names must be levels of mapping variable, and data.frame must be sorted by mapping variable. 
	linevariable=NA, # variable (column name in data), levels of which you want to plot in different line types. Currently support only for two levels.
	sizevariable=NA, # variable (column name in data), levels of which you want to plot in different sizes. you can't specify a sizevariable if you haven't specified a linevariable. Currently support only for two levels.
	facetvariable=NA, # variable (column name in data), levels of which you want to plot in different grids. uses facet_wrap.
	errbars=TRUE, # plot error bars by default	
	errbarvars=NA, # vector of column names (as strings) in the melted dataset, the combination of which error bars are to be calculated from (only relevant if drawing ribbons/error bars)
	dataid="DataID", # name of the variable in the data.frame that uniquely codes samples. downsampling assumes that the data.frame is ordered by this variable, and that sample i and sample i+1 are in fact taken at sampling points t and t+1 (where t is the time step defined by the sampling frequency of the tracker, e.g. 4ms is the default for the EyeLink 500)
	sample=20, # ms to downsample to
	downsample="down", # down: downsample. bin: bin data into time bins of size sample
	srate=4, # sampling rate in ms. 4ms is the default for the EyeLink 500	
	half="both", # only generalized for half="both". if half = "first" or "second", assumes number of trials in my color_gumballs dataset
	exclude=NA, # don't exclude data by default. if exclude is list of vectors of character strings, interpret character strings as levels of the variables denoted by the names of the list elements and exclude those from data. e.g. exclude=list(Var1=c("a","b"),Var2=c("1")). only works with factors.
	onset = 100, # time that 0-point of linguistic event of interest should be aligned to, in ms
	upperlimit = 500, # upper limit on x axis (time in ms)
	timeAlignVar=NA, # column that contains time variable that you want to align by (ie you want to align at the 0 point of that variable)
	addOnsetLine=TRUE, # add a vertical line at linguistic event onset
	addVerticalLine=FALSE, # add a second vertical line at eg mean onset of another event. provide name of that time variable where 0 is the onset of that event, e.g. "Time_rel_stim_Adjective"
	extraVerticalType="collapsed", # one of "individual" or "collapsed". Either prints mean onset of each level of the additional linguistic event of interest (individual) or overall mean (sollapsed).
#	onsetReg=c("competitor","target"), # regions that can have been looked to at onset of linguistic event of interest
	plottype="empirical", # one of "empirical" or "smoothed". The former plots empirical means (without error bars), the latter plots smoothed predicted means based on y~x. If you want to change the formula for the smoother, set form.
	form=formula("y~x"), # formula to pass to smoother. 
	method = "auto", # method for smoother to use. can be e.g. lm, glm, loess, etc. see stat_smooth for details
#	aggregateby=FALSE, # if downsample == "bin" and aggregateby is a string, proportions will be computed for timewindows of size sample and aggregated by aggregateby (eg "Subject")
	analysiswindow=FALSE, # don't plot analysis window by default. when TRUE, not yet generalized. maybe add extra argument of c(window_start,window_end) times, relative to onset of linguistic event of interest?
	analysisonset=200, 
	extraopts=FALSE, # don't change the text size etc defaults. when TRUE, sets legend text size to 20, axis text size to 15, axis title size to 20, legend background to white, and panel label size to 20
#	align="qonset", # figure out if you can generalize this at all
	xbreakstep=200, # size of tick mark spacing on x-axis
	plotwidth=800, # width of the plot in pixels
	plotheight=450, # height of the plot in pixels
	fname=NA, # file name (quoted). if left NA, generates a filename for pdf that is not generalized	
	fileformat="png", # can be one of "png" or "pdf"
	showplot=FALSE, # don't print plot by default (save straight to file instead). If TRUE, will both print plot in quartz window and print plot to file
	graphdir="./", # path to directory where graph will be saved	
	...
)
{
	setwd(graphdir)
	# remove data based on specified factor levels	
	if (!is.na(exclude))
	{
		for (e in names(exclude))
		{
		data = data[!data[,e] %in% exclude[[e]],]
		print(paste("removed levels:",exclude[[e]],"of variable",e))
		}
	print(paste("after removing factor levels:",nrow(data)))
	}

	# remove first/second half
	if (half == "first")
	{
		data <- subset(data, TrialNumber <= 120/2)
		print(paste("after removing second half:",nrow(data)))
	}
	if (half == "second")
	{
		data <- subset(data, TrialNumber > 120/2)
		print(paste("after removing first half:",nrow(data)))		
	}		

	# create time variable to plot on x axis and remove samples not in desired time window
	print(paste("event onset at", onset))
	data$TimePlot = data[,timeAlignVar] + onset
	if (addVerticalLine != FALSE) { data$NewAdj = onset + (data[,timeAlignVar] - data[,addVerticalLine]) }
	data = subset(data, TimePlot >= 0 & TimePlot <= upperlimit)
	print(paste("after removing samples before sentence onset and after upper limit:",nrow(data)))
	
	if (downsample == "bin")
	{
	print(paste("binning into time bins of",sample,"ms"))
	data$TimePlot <- (data$TimePlot %/% sample) * sample
	print(paste("after binning data:",nrow(data)))		
	} else {
	if (downsample == "down")	
	{
	print(paste("downsampling to",sample,"ms"))	
	data <- data[as.numeric(as.character(data[,"TimePlot"])) %% (sample/srate) == 0,]
	print(paste("after downsampling:",nrow(data)))			
	}
	}
	
	if (is.na(fname)) 
	{ 
		if (fileformat == "png")
		{
			fname = "spaghettiplot.png" 
		} else {
		if (fileformat == "pdf")	
		{
			fname = "spaghettiplot.pdf"
		}
		}	
	}

	# create a reduced dataset containing only the data you need
	for (me in meltfactors)
	{
		data[,me] = as.factor(as.character(data[,me]))
	}
	melted = melt(data,id = meltids,measure=region)
	melted$value = as.numeric(as.character(melted$value))
	i=0
	texty=c()
	
	if (is.na(errbarvars))
	{
		errbarvars = c(colorvariable,linevariable,sizevariable,facetvariable)
		errbarvars = errbarvars[!is.na(errbarvars)]
	}
	
	for (v in errbarvars)
	{
		if (i==0)
		{
		texty=paste(texty,paste("data[,\"",v,"\"]",sep=""),sep="")
		i=1
		} else {
		texty=paste(texty,paste("data[,\"",v,"\"]",sep=""),sep=",")			
		}
	}
	texty=paste("paste(",texty,")",sep="")
	melted$errbargroup = eval(parse(text=texty))
	melted$errbargroup = as.factor(as.character(melted$errbargroup))

		
	# create the base plot
	if (is.na(facetvariable))
	{
		if (plottype == "empirical")
		{
			if (is.na(linevariable))
			{
				agr <- with(melted, aggregate(value,by=list(TimePlot,eval(parse(text=colorvariable)),errbargroup),FUN="mean"))
				colnames(agr) = c("TimePlot",colorvariable,"errbargroup","value")		
				agr$SE = with(melted, aggregate(value,by=list(TimePlot,eval(parse(text=colorvariable)),errbargroup),FUN="se"))$x
					agr$YMin = agr$value - agr$SE
					agr$YMax = agr$value + agr$SE
					limits = aes(ymin=YMin,ymax=YMax)					
				p = ggplot(agr, aes_string(x="TimePlot",y="value",color=colorvariable))
			} else {			
				if (is.na(sizevariable))
				{
					agr <- with(melted, aggregate(value,by=list(TimePlot,eval(parse(text=colorvariable)),eval(parse(text=linevariable)),errbargroup),FUN="mean"))
					colnames(agr) = c("TimePlot",colorvariable,linevariable,"errbargroup","value")
					agr$SE = with(melted, aggregate(value,by=list(TimePlot,eval(parse(text=colorvariable)),eval(parse(text=linevariable)),errbargroup),FUN="se"))$x	
					agr$YMin = agr$value - agr$SE
					agr$YMax = agr$value + agr$SE
					limits = aes(ymin=YMin,ymax=YMax)		
					p = ggplot(agr, aes_string(x="TimePlot",y="value",color=colorvariable,linetype=linevariable))
				} else {
					agr <- with(melted, aggregate(value,by=list(TimePlot,eval(parse(text=colorvariable)),eval(parse(text=linevariable)),eval(parse(text=sizevariable)),errbargroup),FUN="mean"))
					colnames(agr) = c("TimePlot",colorvariable,linevariable,sizevariable,"errbargroup","value")
					agr$SE = with(melted, aggregate(value,by=list(TimePlot,eval(parse(text=colorvariable)),eval(parse(text=linevariable)),eval(parse(text=sizevariable)),errbargroup),FUN="se"))$x
					agr$YMin = agr$value - agr$SE
					agr$YMax = agr$value + agr$SE
					limits = aes(ymin=YMin,ymax=YMax)						
	
					p = ggplot(agr, aes_string(x="TimePlot",y="value",color=colorvariable,linetype=linevariable,size=sizevariable)) +
						scale_size_manual(values=c(3,1.5))	
				}
			}
			p = p +
				geom_line(aes_string(group="errbargroup"),size=I(2))
			if (errbars)
			{
				p = p + geom_errorbar(limits,linetype=I(1))	
			}
				
		} else
		{
			if (is.na(linevariable))
			{
				p = ggplot(melted, aes_string(x="TimePlot",y="value",color=colorvariable))
			} else {
				if (is.na(sizevariable))
				{
					p = ggplot(melted, aes_string(x="TimePlot",y="value",color=colorvariable,linetype=linevariable))			
				} else {
					p = ggplot(melted, aes_string(x="TimePlot",y="value",color=colorvariable,linetype=linevariable,size=sizevariable))						
				}
			}
			p = p +
				stat_smooth(aes_string(group="errbargroup",fill=colorvariable),size=I(1.5),method=method,formula=form)
			if (!is.na(colormapping)) {
				p = p + scale_fill_manual(values=colormapping[sort(as.character(unique(melted[,colorvariable]))),]$Color)
			}				
		}
	} else {
		if (plottype == "empirical")
		{
			if (is.na(linevariable))
			{
				agr <- with(melted, aggregate(value,by=list(TimePlot,eval(parse(text=colorvariable)),errbargroup,eval(parse(text=facetvariable))),FUN="mean"))
				colnames(agr) = c("TimePlot",colorvariable,"errbargroup",facetvariable,"value")		
				agr$SE = with(melted, aggregate(value,by=list(TimePlot,eval(parse(text=colorvariable)),errbargroup,eval(parse(text=facetvariable))),FUN="se"))$x
					agr$YMin = agr$value - agr$SE
					agr$YMax = agr$value + agr$SE
					limits = aes(ymin=YMin,ymax=YMax)					
				p = ggplot(agr, aes_string(x="TimePlot",y="value",color=colorvariable)) #+
			} else {			
				if (is.na(sizevariable))
				{
					agr <- with(melted, aggregate(value,by=list(TimePlot,eval(parse(text=colorvariable)),eval(parse(text=linevariable)),errbargroup,eval(parse(text=facetvariable))),FUN="mean"))
					colnames(agr) = c("TimePlot",colorvariable,linevariable,"errbargroup",facetvariable,"value")
					agr$SE = with(melted, aggregate(value,by=list(TimePlot,eval(parse(text=colorvariable)),eval(parse(text=linevariable)),errbargroup,eval(parse(text=facetvariable))),FUN="se"))$x	
					agr$YMin = agr$value - agr$SE
					agr$YMax = agr$value + agr$SE
					limits = aes(ymin=YMin,ymax=YMax)		
					p = ggplot(agr, aes_string(x="TimePlot",y="value",color=colorvariable,linetype=linevariable)) #+
				} else {
					agr <- with(melted, aggregate(value,by=list(TimePlot,eval(parse(text=colorvariable)),eval(parse(text=linevariable)),eval(parse(text=sizevariable)),errbargroup,eval(parse(text=facetvariable))),FUN="mean"))
					colnames(agr) = c("TimePlot",colorvariable,linevariable,sizevariable,"errbargroup",facetvariable,"value")
					agr$SE = with(melted, aggregate(value,by=list(TimePlot,eval(parse(text=colorvariable)),eval(parse(text=linevariable)),eval(parse(text=sizevariable)),errbargroup,eval(parse(text=facetvariable))),FUN="se"))$x
					agr$YMin = agr$value - agr$SE
					agr$YMax = agr$value + agr$SE
					limits = aes(ymin=YMin,ymax=YMax)						
	
					p = ggplot(agr, aes_string(x="TimePlot",y="value",color=colorvariable,linetype=linevariable,size=sizevariable)) +
						scale_size_manual(values=c(3,1.5))	
				}
			}
			p = p +
				geom_line(aes(group=errbargroup),size=I(1))
			if (errbars)
			{
				p = p + geom_errorbar(limits,linetype=I(1))	
			}
				
		} else
		{
			if (is.na(linevariable))
			{
				p = ggplot(melted, aes_string(x="TimePlot",y="value",color=colorvariable))
			} else {
				if (is.na(sizevariable))
				{
					p = ggplot(melted, aes_string(x="TimePlot",y="value",color=colorvariable,linetype=linevariable))			
				} else {
					p = ggplot(melted, aes_string(x="TimePlot",y="value",color=colorvariable,linetype=linevariable,size=sizevariable))						
				}
			}
			p = p +
				stat_smooth(aes_string(group="errbargroup",fill=colorvariable),size=I(1.5))
			if (!is.na(colormapping)) {
				p = p + scale_fill_manual(values=colormapping[sort(as.character(unique(melted[,colorvariable]))),]$Color)			
				}	
		}
		form = as.formula(paste("~",facetvariable,sep=""))
		p = p +
			facet_wrap(form)			
	}

	# extra stuff that's common to all plots
	if (analysiswindow) { onset = onset + analysisonset }
	
	p <- p + scale_y_continuous("Proportion of fixations") +
		coord_cartesian(ylim=c(0,1)) +				
		scale_x_continuous("Time",breaks=seq(0,upperlimit,by=xbreakstep)) 
	if (addOnsetLine)
	{
		p = p + geom_vline(xintercept=onset, colour="black",size=I(1.5),legend=FALSE)
	}
	if (!is.na(colormapping))	
	{
		p = p +	
	scale_colour_manual(values=colormapping[sort(as.character(unique(melted[,colorvariable]))),]$Color)

	}
				
	if (addVerticalLine != FALSE)
	{
		means <- with(data,aggregate(NewAdj, by=list(eval(parse(text=colorvariable))), FUN=mean))
		row.names(means) <- means$Group.1
		means <- means[order(means$Group.1),]
		
		if (extraVerticalType == "individual")
		{
			for (i in 1:length(means$x))
			{
			xi <- means$x[i]
			if (analysiswindow) { xi <- xi + analysisonset }
			if (!is.na(colormapping))
			{
				co <- as.character(colormapping[as.character(means$Group.1[i]),]$Color)
			} else {
				co <- "black"
			}
			p <- p + geom_vline(xintercept=xi, colour=I(co),size=I(1.5),legend=FALSE)
			}
		} else {
			if (extraVerticalType == "collapsed")	
			{
				xi <- mean(means$x)				
				if (analysiswindow) { xi <- xi + analysisonset }
				p <- p + geom_vline(xintercept=xi, colour="black", size=I(1.5),legend=FALSE)
			}
		}
	}
	
	
	if (extraopts)
	{
	p <- p +
		opts(legend.text = theme_text(size = 20), axis.text.x = theme_text(size=15), axis.text.y = theme_text(size=15,hjust=1), axis.title.x = theme_text(size=20), axis.title.y = theme_text(size=20,angle=90),legend.background=theme_rect(fill="white"),strip.text.x = theme_text(size=15)) 
	}
	
	if (showplot) { print(p) }
	# print plot to file	
	print(paste("printing to",fname))	
	if (fileformat == "png")
	{
		png(file=fname, width=plotwidth, height=plotheight)
	} else {
		if (fileformat == "pdf")
		{
			pdf(file=fname, width=plotwidth, height=plotheight)
		}
	}
	print(p)
	dev.off()
	
	rm(p)
	gc()
	
}


##########################################
# get standard error
##########################################

se <- function(x)
      {
        y <- x[!is.na(x)] # remove the missing values, if any
        sqrt(var(as.vector(y))/length(y))
}

Finally, here is the code that generated the plotted data and some sample calls to spaghetti(), including the ones that generated above plots.

# created by jdegen@bcs.rochester.edu
# 02/27/2012

# Before you begin, make sure to find and replace graphdir="/Users/jdegen/Dropbox/spaghettigraphs/" with the path to your own graph directory

# generate some fake data of a 2x2 design (contrast present/absent x adjective scalar/color) with four subjects
d = data.frame(DataID=seq(1,4000),Subject=factor(levels=c("dartagnan","athos","aramis","porthos"),x=rep(c("dartagnan","athos","aramis","porthos"),each=1000)),Time=rep(seq(20,500,by=20),160),Contrast=factor(levels=c("present","absent"),x=c(rep(rep(c("present","absent"),each=25),4))),Adjective=factor(levels=c("color","scalar"),x=c(rep(rep(c("color","scalar"),each=50),2))),Time_rel_Adjective=rep(c(seq(-80,400,by=20),seq(-100,380,by=20),seq(-120,360,by=20),seq(-140,340,by=20)),40),Time_rel_Noun=rep(c(seq(-420,60,by=20),seq(-400,80,by=20),seq(-380,100,by=20),seq(-400,80,by=20)),40))

# time of adjective/noun onset relative to stimulus onset
d$Time_Adjective = d$Time-d$Time_rel_Adjective
d$Time_Noun = d$Time-d$Time_rel_Noun

# generate looks to target and competitor region
d$rp_RegionType = factor(levels=c("target","competitor"),x=NA)
d[d$Contrast == "absent" & d$Time_rel_Noun < 50,]$rp_RegionType = rep(sample(x=c("target","competitor"),size=nrow(d[d$Contrast == "absent" & d$Time_rel_Noun < 50,]),replace=TRUE,prob=c(.5,.5)))
d[d$Contrast == "absent" & d$Time_rel_Noun >= 50,]$rp_RegionType = rep(sample(x=c("target","competitor"),size=nrow(d[d$Contrast == "absent" & d$Time_rel_Noun >= 50,]),replace=TRUE,prob=c(.8,.2)))
d[d$Contrast == "present" & d$Adjective == "scalar" & d$Time_rel_Adjective <= 150,]$rp_RegionType = rep(sample(x=c("target","competitor"),size=nrow(d[d$Contrast == "present" & d$Adjective == "scalar"  & d$Time_rel_Adjective <= 150,]),replace=TRUE,prob=c(.5,.5)))
d[d$Contrast == "present" & d$Adjective == "scalar" & d$Time_rel_Adjective > 150,]$rp_RegionType = rep(sample(x=c("target","competitor"),size=nrow(d[d$Contrast == "present" & d$Adjective == "scalar"  & d$Time_rel_Adjective > 150,]),replace=TRUE,prob=c(.8,.2)))
d[d$Contrast == "present" & d$Adjective == "color" & d$Time_rel_Adjective <= 250,]$rp_RegionType = rep(sample(x=c("target","competitor"),size=nrow(d[d$Contrast == "present" & d$Adjective == "color"  & d$Time_rel_Adjective <= 250,]),replace=TRUE,prob=c(.5,.5)))
d[d$Contrast == "present" & d$Adjective == "color" & d$Time_rel_Adjective > 250,]$rp_RegionType = rep(sample(x=c("target","competitor"),size=nrow(d[d$Contrast == "present" & d$Adjective == "color"  & d$Time_rel_Adjective > 250,]),replace=TRUE,prob=c(.8,.2)))

# columns coding looks to regions as binary variables
d$Target=ifelse(d$rp_RegionType == "target", 1, 0)
d$Target = as.factor(as.character(d$Target))
d$Competitor=ifelse(d$rp_RegionType == "competitor", 1, 0)
d$Competitor = as.factor(as.character(d$Competitor))

# to get a feel for the dataset
summary(d)
head(d)

# plot looks to target in the contrast present vs. absent condition
spaghetti(d,meltids=c("TimePlot","Subject","Contrast","Adjective"),meltfactors=c("Subject","Contrast","Adjective"),colorvariable="Contrast",errbarvars=c("Contrast"),srate=20,graphdir="/Users/jdegen/Dropbox/spaghettigraphs/",timeAlignVar="Time_rel_Adjective",fname="firstplot.png")

# remove error bars
spaghetti(d,meltids=c("TimePlot","Subject","Contrast","Adjective"),meltfactors=c("Subject","Contrast","Adjective"),graphdir="/Users/jdegen/Dropbox/spaghettigraphs/",timeAlignVar="Time_rel_Adjective",errbarvars=c("Contrast"),srate=20,colorvariable="Contrast",errbars=FALSE)

# smoothed plot
spaghetti(d,meltids=c("TimePlot","Subject","Contrast","Adjective"),meltfactors=c("Subject","Contrast","Adjective"),timeAlignVar="Time_rel_Adjective",colorvariable="Contrast",errbarvars=c("Contrast"),srate=20,graphdir="/Users/jdegen/Dropbox/spaghettigraphs/",plottype="smoothed")

# smoothed linearplot
spaghetti(d,meltids=c("TimePlot","Subject","Contrast","Adjective"),meltfactors=c("Subject","Contrast","Adjective"),timeAlignVar="Time_rel_Adjective",colorvariable="Contrast",errbarvars=c("Contrast"),srate=20,graphdir="/Users/jdegen/Dropbox/spaghettigraphs/",plottype="smoothed",method="lm",fname="linearsmooth.png")

# change color
# color mapping data.frame to pass to function
cmap = data.frame(Contrast=c("present","absent"),Color=c("red","blue"))
row.names(cmap) <- cmap$Contrast
cmap <- cmap[order(cmap$Contrast),]

spaghetti(d,meltids=c("TimePlot","Subject","Contrast","Adjective"),meltfactors=c("Subject","Contrast","Adjective"),timeAlignVar="Time_rel_Adjective",colorvariable="Contrast",errbarvars=c("Contrast"),srate=20,graphdir="/Users/jdegen/Dropbox/spaghettigraphs/",plottype="smoothed",colormapping=cmap)

# plot different adjective conditions
spaghetti(d,meltids=c("TimePlot","Subject","Contrast","Adjective"),meltfactors=c("Subject","Contrast","Adjective"),timeAlignVar="Time_rel_Adjective",colorvariable="Contrast",errbarvars=c("Contrast","Adjective"),srate=20,graphdir="/Users/jdegen/Dropbox/spaghettigraphs/",colormapping=cmap,linevariable="Adjective")

# increase bin size to 40ms
spaghetti(d,meltids=c("TimePlot","Subject","Contrast","Adjective"),meltfactors=c("Subject","Contrast","Adjective"),timeAlignVar="Time_rel_Adjective",colorvariable="Contrast",errbarvars=c("Contrast","Adjective"),srate=20,graphdir="/Users/jdegen/Dropbox/spaghettigraphs/",colormapping=cmap,linevariable="Adjective",sample=40,downsample="bin")

# add second line at mean noun onset
spaghetti(d,meltids=c("TimePlot","Subject","Contrast","Adjective"),meltfactors=c("Subject","Contrast","Adjective"),timeAlignVar="Time_rel_Adjective",colorvariable="Contrast",errbarvars=c("Contrast","Adjective"),srate=20,graphdir="/Users/jdegen/Dropbox/spaghettigraphs/",colormapping=cmap,linevariable="Adjective",sample=40,downsample="bin",addVerticalLine="Time_rel_Noun")

# add second line at mean noun onset by contrast condition
spaghetti(d,meltids=c("TimePlot","Subject","Contrast","Adjective"),meltfactors=c("Subject","Contrast","Adjective"),timeAlignVar="Time_rel_Adjective",colorvariable="Contrast",errbarvars=c("Contrast","Adjective"),srate=20,graphdir="/Users/jdegen/Dropbox/spaghettigraphs/",colormapping=cmap,linevariable="Adjective",sample=40,downsample="bin",addVerticalLine="Time_rel_Noun",extraVerticalType="individual",extraopts=TRUE)

# create individual plots by subject
spaghetti(d,meltids=c("TimePlot","Subject","Contrast","Adjective"),meltfactors=c("Subject","Contrast","Adjective"),timeAlignVar="Time_rel_Adjective",colorvariable="Contrast",errbarvars=c("Contrast","Adjective"),srate=20,graphdir="/Users/jdegen/Dropbox/spaghettigraphs/",fname="smoothedplot.png",colormapping=cmap,sample=40,downsample="bin",plottype="smoothed",linevariable="Adjective",facetvariable="Subject",extraopts=TRUE)

# remove data from one subject
spaghetti(d,meltids=c("TimePlot","Subject","Contrast","Adjective"),meltfactors=c("Subject","Contrast","Adjective"),timeAlignVar="Time_rel_Adjective",colorvariable="Contrast",errbarvars=c("Contrast","Adjective"),srate=20,graphdir="/Users/jdegen/Dropbox/spaghettigraphs/",colormapping=cmap,sample=40,downsample="bin",plottype="smoothed",facetvariable="Subject",linevariable="Adjective",exclude=list(Subject=c("dartagnan"),Contrast=c("absent")))

# remove data from subject "dartagnan" and contrast "absent" condition
spaghetti(d,meltids=c("TimePlot","Subject","Contrast","Adjective"),meltfactors=c("Subject","Contrast","Adjective"),timeAlignVar="Time_rel_Adjective",colorvariable="Contrast",errbarvars=c("Contrast","Adjective"),srate=20,graphdir="/Users/jdegen/Dropbox/spaghettigraphs/",colormapping=cmap,sample=40,downsample="bin",plottype="smoothed",facetvariable="Subject",linevariable="Adjective",exclude=list(Subject=c("dartagnan"),Contrast=c("absent")))

3 thoughts on “Creating spaghetti plots of eye-tracking data in R

    […] on how to create nice spaghetti plots (proportions of fixations plots) of eye-tracking data in r here. the function code, code for generating fake data, and sample calls to the function for plotting […]

    Like

    Kostka Brukowa said:
    March 15, 2012 at 9:15 am

    For some reason the code is showing out of post content :/

    Like

      jdegen responded:
      March 15, 2012 at 10:28 am

      It does that because there are no line breaks in those lines. I will update the code within the next 5 days to include some of the features mentioned in the TODO list, and I’ll make sure that there are line breaks at the appropriate positions. In the meantime if you copy paste the code, it will copy everything just fine.

      Like

Leave a reply to how to create spaghetti plots of eye-tracking data in r « die welt ist alles, was der blog ist Cancel reply