R/SPRData.R

Defines functions ReadSensorgramData GetObservedRUs sumRUs SaveSPRData

# #' @title an empty file to import necessary libraries 
# #' @import limma MASS
# #' @name _empty
# NULL
#  <======the above section is for importing libraries. change it whenever necessary by uncommenting 


#roxygen2 comments below
#' @title S4 class definition of SensorgramData
#'
#' @description \code{SensorgramData} define the S4 class of SensorgramData
#'
#' @details defining the S4 class of the SensorgrameData.
#' This is the data structure to hold the sensor grame Data. 
#' It contains different slots for holding both association and 
#'    dissociation phase data.It also defines 
#'    the steady state window if there is one. Analyte concentrations 
#'    also included. Sometimes the data could 
#'    only have either association or dissociation phase alone. 
#'
#' @concept SPR
#'
#' @param associationData Dataframe holding the association phase data. 
#'         It has a fixed format/template.
#'         See \code{\link{ReadSensorgramData}} for details.
#' @param dissociationData Dataframe holding the dissociation phase data. 
#'          It has a fixed format/template.
#'          See \code{\link{ReadSensorgramData}} for details.
#' @param analyteConcentrations, numeric vector for analyteConcentrations 
#'          of unit molar concentration (M/L)
#' @param steadyStateStart numeric indicating the starting time of 
#'          steady state (optional).
#' @param steadyStateEnd numeric indicating the ending time of 
#'          steady state (optional).
#' @param offset numeric indicating the RU levels shift at the
#'          begining of dissociation phase. Not used for now.
#'          will implement in the future release. (optional)
#' @slot associationData data.frame
#' @slot dissociationData data.frame
#' @slot analyteConcentrations numeric vector
#' @slot steadyStateStart numeric
#' @slot steadyStateEnd numeric
#' @slot offset numeric
#' @seealso \code{\link{ReadSensorgramData}} \code{\link{GetObservedRUs}} \code{\link{SaveSPRData}}
#' @examples
#'		dt<-new("SensorgramData",
#'		dissociationData=data.frame(time=1:5,RU=1:5, time2=1:5, RU=1:5))
#'		dt@associationData
#'
#'		dataFile<-list.files(system.file("extdata", package="SPRATS"),
#'		 pattern = "CBSInhibitor_raw.txt", full.names=TRUE) 
#'		rawData<-ReadSensorgramData(dataFile, skip=4, 
#'		header=T, sep="\t", associationPhaseEnd=80, dissociationPhaseEnd=150)
#'
#'		plot(rawData)
#' @export
setClass( "SensorgramData",
		representation(associationData="data.frame",
					   dissociationData="data.frame",
					   analyteConcentrations="numeric",
					   steadyStateStart="numeric", #optional, but necessary for fitting two state
					   steadyStateEnd="numeric",#optional, but necessary for fitting two state
					   offset="numeric" ###optional, might be used in the future
		               ),
		prototype(associationData=data.frame(),
					   dissociationData=data.frame(),
					   analyteConcentrations=numeric(),
					   steadyStateStart=-1, 
					   steadyStateEnd=-1,
					   offset=0.0 
				)	   
		)

setValidity("SensorgramData", function(object){
				#first check for association data
				if(!is.null(object@associationData)&&length(object@associationData)!=0){
					#the columns must be pairs of Time and RUs
					if(dim(object@associationData)[2]%%2!=0)
					{
						cat("AssociationData is not in correct format!!\n")
						cat("dimension of association data:",dim(object@associationData)[2], "\n");
					}
					else if (!is.null(object@dissociationData)&&length(object@dissociationData)!=0){
					    if(dim(object@dissociationData)[2]%%2!=0)
							"DissociationData is not in correct format!!"
						else if(dim(object@associationData)[2]!=dim(object@dissociationData)[2]){
							"AssociationData and DissociationData are not consistent with each other!"
						}
						else {
							#cat("we are here\n")
							TRUE
						}
					}
					else 
						TRUE
				}
				#now check for dissociation data
				else if(!is.null(object@dissociationData)&&length(object@dissociationData)!=0){
					if(dim(object@dissociationData)[2]%%2!=0){
						"dissociationData is not in correct format!!"
					}
					else
						TRUE
				}
				#all the data is null or empty, go ahead
				
				else
				    TRUE
				#in this checking, we did not check for the number of concentration elements. leave it to the future
			}
		   )
		   
#' @title S3 method to read in the data
#'	
#' @description It read in data as a txt file according to
#'    exported SPR file from SensiQ
#' @details
#'  Data file has the following format\cr
#'	\tabular{llllllll}{
#'   Curve\tab	1\tab	2\tab		3\tab		4\cr	
#'   Analyte\tab	CBS\tab	CBS\tab		CBS\tab		CBS\cr	
#'  Ligand\tab	Ch 1\tab	Ch 1\tab		Ch 1\tab		Ch 1\cr	
#'  Conc\tab	0.00E+00\tab		5.00E-05\tab		0.00E+00\tab		0.00E+00\cr
#'	Time1\tab	Data1\tab	Time2\tab	Data2\tab	Time3\tab	Data3\tab	Time4\tab	Data4\cr
#'	-2.6\tab	0\tab	-2.6\tab	0.2839\tab	-2.6\tab	0\tab	-2.6\tab	0\cr
#'	-2.4\tab	0\tab	-2.4\tab	0.3632\tab	-2.4\tab	0\tab	-2.4\tab	0\cr
#'	-2.2\tab	0\tab	-2.2\tab	0.6978\tab	-2.2\tab	0\tab	-2.2\tab	0\cr
#'	}
#'	Each file contains the header about the information of the data
#'	Data colums for the raw sensorgram data with header for each column
#'	each "curve" has two column, one time and the other RUs. 
#'	The number of curves are variable. Please check the example
#'	data file in the example/ folder.

#'	Note: after reading the dissociation phase is rescaled to 
#'	start at time zero.!!! relative to the 
#'	dissociationPhaseStart/associationPhaseEnd
#'
#'	@param file the directory path to the input file with the
#'		correct format as mentioned above
#'	@param associationPhaseStart numeric indiciation the starting 
#'		time for associationPhase
#'	@param associationPhaseEnd numeric indiciation the starting 
#'		time for associationPhase. This is actually the starting
#'		time for dissociationPhase
#'	@param dissociationPhaseEnd numeric indiciation the starting 
#'		time for dissociationPhase
#' @param steadyStateStart numeric indicating the starting time of 
#'          steady state (optional).
#' @param steadyStateEnd numeric indicating the ending time of 
#'          steady state (optional).
#' @param analyteConcentrations numeric vector for analyte concentrations
#' @param skip numeric the options for control the text reading. indicating
#'		number of line to skip
#' @param header boolean header for the table reading (T/F)
#' @param sep character for the delimiter of the text
#'
#' @return a sensorgram data object
#'
#' @seealso  \code{\link{SensorgramData-class}} \code{\link{plot}} \code{\link{SaveSPRData}}
#' @examples
#'
#'		dataFile<-list.files(system.file("extdata", package="SPRATS"),
#'		 pattern = "CBSInhibitor_raw.txt", full.names=TRUE) 
#'		rawData<-ReadSensorgramData(dataFile, skip=4, 
#'		header=T, sep="\t", associationPhaseEnd=80, dissociationPhaseEnd=150)
#'
#'		plot(rawData)
#' @export
ReadSensorgramData<-function(file, associationPhaseStart=0, associationPhaseEnd=-1,
								dissociationPhaseEnd=-1, steadyStateStart=-1, steadyStateEnd=-1,
								analyteConcentrations=numeric(0),
								skip=0, header=TRUE, sep="\t")
				{
					#first we need to check for validity of the input
					if(associationPhaseEnd<0 ||associationPhaseEnd< associationPhaseStart)
						cat("Warning: the associationPhase window is not correctly (?) set......\n")
					if(dissociationPhaseEnd<0||dissociationPhaseEnd<associationPhaseEnd)
						cat("Warning: the dissociationPhase window is not correctly (?) set......\n")
					cat("Start reading the data files.......\n")
					x_rawData<-read.table(file,skip=skip, header=header,sep=sep)
					cat("Done with reading the raw text file....\n")
					cat("Start parsing.....\n");
					x_sgdata<-new("SensorgramData")
					x_sgdata@analyteConcentrations<-analyteConcentrations
					
					#assuming formation is Time1 RU1 Time2 RU2....
					x_Ass<-data.frame();
					x_Dis<-data.frame();
					for(i in seq(1,dim(x_rawData)[2], by=2))
					{
						#cat("i:",i,"....\n")
						if(i+1>dim(x_rawData)[2])
							break;
							
						x_temp<-x_rawData[,c(i,i+1)]
						x_tempAss<-x_temp[x_temp[,1]>=associationPhaseStart&x_temp[,1]<=associationPhaseEnd,]
						x_tempDis<-x_temp[x_temp[,1]>=associationPhaseEnd&x_temp[,1]<=dissociationPhaseEnd,]
						x_tempDis[,1]<-x_tempDis[,1]-associationPhaseEnd
						if(i==1){
							x_Ass<-x_tempAss
							x_Dis<-x_tempDis
						}
						else
						{
							x_Ass<-cbind(x_Ass, x_tempAss)
							x_Dis<-cbind(x_Dis, x_tempDis)
						}
					}
					#now we have the data,
					cat("Setting sensorgram data......\n")
					x_sgdata@associationData<-x_Ass
					x_sgdata@dissociationData<-x_Dis
					x_sgdata@steadyStateStart<-steadyStateStart
					x_sgdata@steadyStateEnd<-steadyStateEnd
					cat("Done......\n")
					return(x_sgdata)
				}				

#' @title Generic plot function for SensorgramData
#' 
#' @description \code{plot} plot the sensorgram
#' @details Generic function to plot the sensorgram of SPR.
#'		Both the dissociation and association phase as well
#'		the steady state window.
#' 
#' @param SensorgramData the SensorgramData object to be plotted
#'
#' @seealso \code{\link{ReadSensorgramData}} \code{\link{SensorgramData-class}} \code{\link{SaveSPRData}}
#' @export
setMethod("plot", "SensorgramData",
		function(x, col=1, lty=1, lwd=1, main="",xlab="time (sec)", ylab="RUs" )
		{
			#cat("times:",ceiling(length(x@associationData)/2/col))
			col<-rep(col,ceiling(length(x@associationData)/2/length(col)))
			lty<-rep(lty,ceiling(length(x@associationData)/2/length(lty)))
			lwd<-rep(lwd,ceiling(length(x@associationData)/2/length(lwd)))
			#cat("lty:",lty,"\n")
			max_x1<-0;
			max_x2<-0;
			max_y1<-0
			max_y2<-0;
			meanRUs<-rep(0, length(x@associationData)/2)
			for(i in 1:(length(x@associationData)/2))
			{
				if(max_x1>min(x@associationData[,i*2-1]))
				{
					max_x1<-min(x@associationData[,i*2-1])
				}
				if(max_x2<max(x@associationData[,i*2-1]))
				{
					max_x2<-max(x@associationData[,i*2-1])
				}
				if(max_y1>min(x@associationData[,i*2]))
				{
					max_y1<-min(x@associationData[,i*2])
				}
				if(max_y2<max(x@associationData[,i*2]))
				{
					max_y2<-max(x@associationData[,i*2])
				}
				#meanRUs[i]<-
			}
			
			
			for(i in 1:(length(x@dissociationData)/2))
			{
				if(max_x1>min(max(x@associationData[,i*2-1])+x@dissociationData[,i*2-1]))
				{
					max_x1<-min(max(x@associationData[,i*2-1])+x@dissociationData[,i*2-1])
				}
				if(max_x2<max(max(x@associationData[,i*2-1])+x@dissociationData[,i*2-1]))
				{
					max_x2<-max(max(x@associationData[,i*2-1])+x@dissociationData[,i*2-1])
				}
				if(max_y1>min(x@dissociationData[,i*2]))
				{
					max_y1<-min(x@dissociationData[,i*2])
				}
				if(max_y2<max(x@dissociationData[,i*2]))
				{
					max_y2<-max(x@dissociationData[,i*2])
				}
				#meanRUs[i]<-
			}
			
			plot(c(max_x1, max_x2), c(max_y1, max_y2), type="n",main=main, xlab=xlab, ylab=ylab)
			
			for(i in 1:(length(x@associationData)/2))
			{
			#cat("plotting i:",i,"lty[i]:",lty[i],"\n")
				lines(x@associationData[,i*2-1], x@associationData[,i*2],col=col[i], lty=lty[i], lwd=lwd[i])
				lines(max(x@associationData[,i*2-1])+x@dissociationData[,i*2-1], x@dissociationData[,i*2],col=col[i],lty=lty[i],lwd=lwd[i])
			}
		}
	)
#'@title S3 function to combine/add two SensorgramData
#'
#'@description Combine two SensorgramData object to simulate
#'		the real readout on a SPR machine.  
#'
#'@details The idea is to combine the RUs from two different source
#'		e.g. from two complexes with different comfirmations, as
#'		an real readout on the SPR machine. It adds up the RUs at
#'		the same time points to get the sum. Do this through the 
#'		association and dissociation. Don't try to modify any other 
#'		slots for the object. 
#'
#'@param DATA1 SensorgramData 
#'@param DATA2 SensorgramData 

#'@return a SensorgramData object with the sum of the two input objects
#'		on both association and dissociation data. It takes other slot
#'		values from DATA1 assuming DATA2 has the identical values on
#'		these other slots. 
#'@seealso \code{\link{ReadSensorgramData}} \code{\link{SensorgramData-class}} \code{\link{plot}} \code{\link{SaveSPRData}}
#' @examples
#'
#'		dataFile<-list.files(system.file("extdata", package="SPRATS"),
#'		 pattern = "CBSInhibitor_raw.txt", full.names=TRUE) 
#'		rawData<-ReadSensorgramData(dataFile, skip=4, 
#'		header=T, sep="\t", associationPhaseEnd=80, dissociationPhaseEnd=150)
#'      rawData2<-GetObservedRUs(rawData,rawData)
#'		plot(rawData)
#' @export
GetObservedRUs<-function(DATA1, DATA2)
	{
		if(class(DATA1)!="SensorgramData"||class(DATA2)!="SensorgramData")
		{
			cat("*******ERROR:SensorgramData are expected for both input")
			return(FALSE)
		}
		if(length(DATA1@associationData)!=length(DATA2@associationData))
		{
			cat("********ERROR:input data are not consistent with each other\n")
			return(FALSE);
		}
		if(length(DATA1@associationData[,1])!=length(DATA2@associationData[,1]))
		{
			cat("********ERROR:input data are not consistent with each other\n")
			return(FALSE);
		}
		#add all the AB together to get RU observed
		dt_RUs<-DATA1
		
		#new to decide the dimension of association and dissociation data
		#assuming they are of each dimension
		#
		lenData<-dim(DATA1@associationData)[2]
		lenData<-lenData/2
		if(lenData<1)
			return(dt_RUs)
		for(i in 1:lenData)
		{
			dt_RUs@associationData[,i*2]<-DATA1@associationData[,i*2]+DATA2@associationData[,i*2]
			dt_RUs@dissociationData[,i*2]<-DATA1@dissociationData[,i*2]+DATA2@dissociationData[,i*2]
		}
		dt_RUs
	}
	
#'@title S3 function to sum SensorgramData
#'
#'@description sum all SensorgramData objects to simulate
#'		the real readout on a SPR machine.  
#'
#'@details The idea is to combine the RUs from many different sources
#'		in the multi-liganded system as
#'		an real readout on the SPR machine. It adds up the RUs at
#'		the same time points to get the sum. Do this through the 
#'		association and dissociation. Don't try to modify any other 
#'		slots for the object. 
#'
#'@param DATA List of SensorgramData 
#'

#'@return a SensorgramData object with the sum of the all input objects
#'		in the list on both association and dissociation data. It takes other slot
#'		values from DATA[[1]] assuming DATA objects all have the identical values on
#'		these other slots. 
#'@seealso \code{\link{ReadSensorgramData}} \code{\link{SensorgramData-class}} \code{\link{plot}} \code{\link{SaveSPRData}}
#' @examples
#'
#'		dataFile<-list.files(system.file("extdata", package="SPRATS"),
#'		 pattern = "CBSInhibitor_raw.txt", full.names=TRUE) 
#'		rawData<-ReadSensorgramData(dataFile, skip=4, 
#'		header=T, sep="\t", associationPhaseEnd=80, dissociationPhaseEnd=150)
#'      
#'		rawDataSum<-sumRUs(list(rawData,rawData,rawData))
#'		plot(rawData)
#' @export
sumRUs<-function(DATA)
	{
		#check data integrity 
		if(missing(DATA))
		{
			stop("DATA field has not been specified!");
		}
		if(class(DATA)!="list"&&class(DATA)!="SensorgramData")
		{
			stop("unexpected object class for DATA!");
		}
		
		#in this case, we can only have one input object
		if(class(DATA)=="SensorgramData")
		{
			return(DATA)
		}
		#now could be many of them, check for sure
		if(length(DATA)==0)
		{
			return(NULL)
		}
		if(length(DATA)==1)
		{
			return(DATA[[1]])
		}
		
		#now more than 2 element
		lenList<-length(DATA)
		#lenData<-length(DATA[[1]])
		origDimAssData<-c(0,0)
		origDimDisData<-c(0,0)
		for(i in c(1:lenList))
		{
			if(class(DATA[[i]])!="SensorgramData")
			{
				stop("SensorgramData is expected for input list");
			}
			dimAssData<-dim(DATA[[i]]@associationData)
			dimDisData<-dim(DATA[[i]]@dissociationData)
			if(i==1)#first one, set original
			{
				origDimAssData<-dim(DATA[[i]]@associationData)
				origDimDisData<-dim(DATA[[i]]@dissociationData)
			}else
			{
				if((dimAssData[1]!=origDimAssData[1])||(dimAssData[2]!=origDimAssData[2])||(dimDisData[1]!=origDimDisData[1])||(dimDisData[2]!=origDimDisData[2]))	
				{
					cat("********ERROR:input data are not consistent with each other (at ",i,"th element\n")
					return(NULL)
				}
				origDimAssData<-dimAssData
				origDimDisData<-dimDisData
			}
		}
		#add all the AB together to get RU observed
		dt_RUs<-DATA[[1]]
		
		#new to decide the dimension of association and dissociation data
		#assuming they are of each dimension
		#
		lenData<-dim(dt_RUs@associationData)[2]
		lenData<-lenData/2
		if(lenData<1)
			return(dt_RUs)
			
		for(j in 2:lenList)
		{
			for(i in 1:lenData)
			{
				dt_RUs@associationData[,i*2]<-dt_RUs@associationData[,i*2]+DATA[[j]]@associationData[,i*2]
				dt_RUs@dissociationData[,i*2]<-dt_RUs@dissociationData[,i*2]+DATA[[j]]@dissociationData[,i*2]
			}
		}
		dt_RUs
	}	
	
#'@title S3 function to save SensorgramData data
#'
#'@description save SensorgramData object data to the disk.
#'		  
#'@details It save the object data to the disk in a text format,
#'		following the SensiQ format. See the detail \code{\link{ReadSensorgramData}}
#'		Note: it will issue a warning to complain about appending the table.
#'		There is nothing wrong with it. Simple ignore this warning.
#'
#'@param DATA SensorgramData to be saved 
#'@param file the directory path to the file
#'@param sep character to delimit the text fields 

#'
#'@seealso \code{\link{ReadSensorgramData}} \code{\link{SensorgramData-class}} \code{\link{plot}}	
#' @export
SaveSPRData<-function(DATA, file, sep="\t")
	{
		if(class(DATA)!="SensorgramData")
		{	
			cat("***ERROR***:sensorgramData is expected for the input\n")
			return(FALSE)
		}
		dtfm_RUs_a<-DATA@associationData
		dtfm_RUs_d<-DATA@dissociationData
		#check the dissociation data first, if the first row is zero for time, we need to combine this one 
		#with the last one of the association data to make it consistent
		if(dtfm_RUs_d[1,1]==0)
		{
			#get the mean of both values
			dtfm_RUs_a[length(dtfm_RUs_a[,1]),seq(2,length(dtfm_RUs_a),2)]<-
				(dtfm_RUs_a[length(dtfm_RUs_a[,1]),seq(2,length(dtfm_RUs_a),2)]+
					dtfm_RUs_d[1,seq(2,length(dtfm_RUs_d),2)])/2;
			#also remove the first one of the dissociation data
			dtfm_RUs_d<-dtfm_RUs_d[c(-1),]
		}
		#also need to add the time together for dissociation data
		for(i in 1:length(DATA@analyteConcentrations))
		{
			dtfm_RUs_d[,i*2-1]<-max(dtfm_RUs_a[,i*2-1])+dtfm_RUs_d[,i*2-1]
		}
		dtfm_RUs_a<-rbind(dtfm_RUs_a, dtfm_RUs_d)

		#now save it to the disk
		
		write(DATA@analyteConcentrations,file=file, sep=sep)
		write.table(dtfm_RUs_a, file=file, sep=sep,append=TRUE, row.names=FALSE, quote=FALSE)
		#TRUE
	}
	
				
				
ffeng23/ADASPR documentation built on July 13, 2019, 1:15 p.m.