#' A S4 class to analyze spatial properties in 2D environments
#'
#' Use to get firing rate maps of neurons in an open field.
#' It can get information scores, sparsity, etc.
#' You can use it to analyze the firing properties of grid cells or border cells.
#' Can do some shuffling to get the spatial properties that you would get by chance.
#'
#' @slot session Name of the recording session
#' @slot cmPerBin Number of cm in each bin of a firing rate map, 2 by default
#' @slot smoothOccupancySd Standard deviation in cm of the gaussian kernel used to smooth the occupancy map, 3 by default
#' @slot smoothRateMapSd Standard deviation in cm of the gaussian kernel used to smooth the firing rate histograms, 3 by default
#' @slot nColMap Number of columns in the firing rate maps
#' @slot nRowMap Number of rows in the firing rate maps
#' @slot nColAuto Number of columns in the spatial autocorrelation
#' @slot nRowAuto Number of rows in the spatial autocorrelation
#' @slot nColCross Number of columns in the spatial crosscorrelation
#' @slot nRowCross Number of rows in the spatial crosscorrelation
#' @slot xSpikes x position of the animal for each spike time
#' @slot ySpikes y position of the animal for each spike time
#' @slot maps Array containing the firing rate maps of the neurons
#' @slot ccMaps Array containing the crosscorrelation of firing rate maps
#' @slot occupancy Matrix containing the occupancy map
#' @slot occupancy3D Array containing the occupancy for x and y positions and head-direction.
#' @slot autos Array containing the spatial autocorrelation of the firing rate maps
#' @slot autosDetect Array with the spatial autocorrelation once the firing fields have been removed
#' @slot autosDoughnut Array with the spatial autocorrelation containing only the ring of the 6 closest fields (excluding central field)
#' @slot autosDoughnutRotate Array with the rotated spatial autocorrelations
#' @slot cellList List of cells
#' @slot cellPairList List of cell pairs
#' @slot reduceSize Logical indicating if the size of map should be minimized by setting smallest x and y to 0
#' @slot minValidBinsAuto Number of valid bins to calculate a r value in the spatial autocorrelation maps
#' @slot minValidBinsCross Number of valid bins to calculate a r value in the spatial crosscorrelation maps
#' @slot peakRate Peak firing rates in the maps
#' @slot infoScore Information score of the firing rate maps
#' @slot sparsity Sparsity of the firing rate maps
#' @slot borderScore Border score for each firing rate maps
#' @slot borderCM Value of CM when calculating the border score
#' @slot borderDM Value of DM when calculating the border score
#' @slot borderNumFieldsDetected Number of detected fields when calculating the border score
#' @slot borderPercentageThresholdField Threshold for a bin being part of a field, expressed as percentage of peak
#' @slot borderMinBinsInField Minimum number of bins to be considered a field
#' @slot mapPolarity How polarized is the firing rate of the cell in the map. Each bin of the map is given an angle and firing rate.
#' mapPolarity is the resultant mean vector length
#' @slot gridScore Grid score calculated form the spatial autocorrelation
#' @slot gridOrientation Grid orientation
#' @slot gridSpacing Grid spacing in cm
#' @slot gridScoreNumberFieldsToDetect Maximal number of fields that will be detected
#' @slot gridScoreMinNumBinsPerField Minimum number of bins to be considered a field
#' @slot gridScoreFieldThreshold Minimum value of firing fields in the spatial autocorrelation map
#' @slot nAutoRotations Number of rotations when rotating the spatial autocorrelation map
#' @slot AutoRotationDegree Angle of rotation of the spatial autocorrelation map
#' @slot speedScore Speed score of the neurons
#' @slot speedRateSlope Slope of the linear regression line between speed and rate
#' @slot speedRateIntercept Rate intercept of the linear regression line between speed and rate
#' @slot nShufflings Number of shufflings to get chance levels, by default 100
#' @slot minShiftMs Minimum time shift of the position data when doing shuffling
#' @slot peakRateShuffle peak firing rate in the shuffling analysis
#' @slot infoScoreShuffle Information score from the shuffling analysis
#' @slot sparsityShuffle Sparsity from the shuffling analysis
#' @slot borderScoreShuffle Border score form the shuffling analysis
#' @slot borderCMShuffle Border CM from the shuffling analysis
#' @slot borderDMShuffle Border DM from the shuffling analysis
#' @slot borderNumFieldsDetectedShuffle Number of detected fields when calculating shufflings of the border score
#' @slot mapPolarityShuffle Map polarity from the shuffling analysis
#' @slot gridScoreShuffle Grid score from the shuffling analysis
#' @slot speedScoreShuffle Speed score from the shuffling analysis
SpatialProperties2d<- setClass(
"SpatialProperties2d", ## name of the class
slots=c(session="character",
cmPerBin="numeric",
smoothOccupancySd="numeric", ## in cm
smoothRateMapSd="numeric", ## in cm
nColMap="integer",
nRowMap="integer",
nColAuto="integer",
nRowAuto="integer",
nColCross="integer",
nRowCross="integer",
xSpikes="numeric",
ySpikes="numeric",
maps="array",
ccMaps="array",
occupancy="matrix",
occupancy3D="array",
autos="array",
autosDetect="array",
autosDoughnut="array",
autosDoughnutRotate="array",
cellList="numeric",
cellPairList="matrix",
reduceSize="logical",
minValidBinsAuto="numeric",
minValidBinsCross="numeric",
##
peakRate="numeric",
infoScore="numeric",
sparsity="numeric",
##
borderScore="numeric",
borderCM="numeric",
borderDM="numeric",
borderNumFieldsDetected="numeric",
borderPercentageThresholdField="numeric",
borderMinBinsInField="numeric",
mapPolarity="numeric",
##
gridScore="numeric",
gridOrientation="numeric",
gridSpacing="numeric",
gridScoreNumberFieldsToDetect="numeric",
gridScoreMinNumBinsPerField="numeric",
gridScoreFieldThreshold="numeric",
nAutoRotations="numeric",
AutoRotationDegree="numeric",
##
speedScore="numeric",
speedRateSlope="numeric",
speedRateIntercept="numeric",
speedPValue='numeric',
##
nShufflings="numeric",
minShiftMs="numeric",
peakRateShuffle="numeric",
infoScoreShuffle="numeric",
sparsityShuffle="numeric",
borderScoreShuffle="numeric",
borderCMShuffle="numeric",
borderDMShuffle="numeric",
borderNumFieldsDetectedShuffle="numeric",
mapPolarityShuffle="numeric",
gridScoreShuffle="numeric",
speedScoreShuffle="numeric"
),
prototype = list(session="",cmPerBin=2,smoothOccupancySd=3,smoothRateMapSd=3,minValidBinsAuto=20,minValidBinsCross=20,reduceSize=T,
gridScoreNumberFieldsToDetect=40,gridScoreMinNumBinsPerField=50,gridScoreFieldThreshold=0.1,
borderPercentageThresholdField=20,borderMinBinsInField=10,nShufflings=100,minShiftMs=20000,
nAutoRotations=5,AutoRotationDegree=30))
### show ###
setMethod("show", "SpatialProperties2d",
function(object){
print(paste("session:",object@session))
print(paste("cmPerBin:",object@cmPerBin))
print(paste("smoothOccupancySd:",object@smoothOccupancySd))
print(paste("smoothRateMapSd:",object@smoothRateMapSd))
print(paste("reduceSize:",object@reduceSize))
print(paste("nColMap:",object@nColMap))
print(paste("nRowMap:",object@nRowMap))
if(length(object@cellList)!=0){
print(paste("nCells:",length(object@cellList)))
print(paste("cellList:"))
print(object@cellList)
}
if(length(object@peakRate)!=0){
print("peakRate:")
print(paste(object@peakRate))
print("infoScore:")
print(paste(object@infoScore))
print("borderScore:")
print(paste(object@borderScore))
print("DM:")
print(paste(object@borderDM))
print("CM:")
print(paste(object@borderCM))
print("gridScore:")
print(paste(object@gridScore))
}
if(length(object@mapPolarity)!=0){
print("mapPolarity:")
print(paste(object@mapPolarity))
}
if(length(object@speedScore)!=0){
print("speedScore:")
print(paste(object@speedScore))
}
if(length(object@speedRateSlope)!=0){
print("speedRateSlope:")
print(paste(object@speedRateSlope))
print("speedRateIntercept:")
print(paste(object@speedRateIntercept))
print("speedPValue:")
print(paste(object@speedPValue))
}
print(paste("nShufflings:",object@nShufflings))
print(paste("shuffled values:",length(object@infoScoreShuffle)))
})
#' Return the data to plot spikes on the path of the animal
#'
#' Only data that fall within the intervals of the SpikeTrain object
#' are used.
#'
#' @param sp SpatialProperties2d
#' @param st SpikeTrain object
#' @param pt Positrack object
#'
#' @return List containing xPath, yPath, xSpike, ySpike, cluSpike
#'
#' @docType methods
#' @rdname spikeOnPath-methods
setGeneric(name="spikeOnPath",
def=function(sp,st,pt)
{standardGeneric("spikeOnPath")}
)
#' @rdname spikeOnPath-methods
#' @aliases spikeOnPath,ANY,ANY-method
setMethod(f="spikeOnPath",
signature="SpatialProperties2d",
definition=function(sp,st,pt)
{
if(length(pt@x)==0)
stop(paste("pt@x has length of 0 in firingRateMap2d",st@session))
if(st@nSpikes==0)
stop(paste("st@nSpikes==0 in firingRateMap2d",st@session))
sp@cellList<-st@cellList
## set position data outside the SpikeTrain intervals to NA
pt<-setInvalidOutsideInterval(pt,s=st@startInterval,e=st@endInterval)
## move data closer to 0, like done for firing rate maps
if(sp@reduceSize==T){
x<-pt@x-min(pt@x,na.rm=T)+sp@cmPerBin
y<-pt@y-min(pt@y,na.rm=T)+sp@cmPerBin
}else{
x<-pt@x
y<-pt@y
}
## use -1 as invalid values in c functions
x[is.na(x)]<- -1.0
y[is.na(y)]<- -1.0
## get spike position
results<-.Call("spike_position_cwrap",
x,
y,
length(x),
as.integer(st@res),
as.integer(st@nSpikes),
as.integer(pt@resSamplesPerWhlSample),
as.integer(st@startInterval),
as.integer(st@endInterval),
length(st@startInterval))
sp@xSpikes<-results[1,]
sp@ySpikes<-results[2,]
## return all the data
xPath<-x[which(x!=-1.0)]
yPath<-y[which(y!=-1.0)]
xSpike<-sp@xSpikes[which(sp@xSpikes!=-1.0&st@clu%in%st@cellList)]
ySpike<-sp@ySpikes[which(sp@xSpikes!=-1.0&st@clu%in%st@cellList)]
cluSpike<-st@clu[which(sp@xSpikes!=-1.0&st@clu%in%st@cellList)]
return(list(xPath=xPath,yPath=yPath,xSpike=xSpike,ySpike=ySpike,cluSpike=cluSpike))
}
)
#' Calculate the firing rate maps of neurons using a SpikeTrain and Positrack objects
#'
#' The occupancy map and the firing rate maps are smoothed with a Gaussian kernel.
#' The amount of smoothing is determined by slots smoothOccupancySd and smoothRateMapSd of sp.
#' You can force the function to create maps with arbitrary x and y sizes as long as the map fits into this arbitrary size.
#'
#' @param sp SpatialProperties1d object
#' @param st SpikeTrain object
#' @param pt Positrack object
#' @param nRowMap Numeric indicating the number of rows in the map. By default the maps has the smallest size possible given the
#' position data in the Positrack object. Use this argument to have maps of a fixed size. Needs to be as large as the minimal size of the map
#' @param nColMap Numeric indicating the number of columns in the map. By default the maps has the smallest size possible given the
#' position data in the Positrack object. Use this argument to have maps of a fixed size. Needs to be as large as the minimal size of the map
#' @return SpatialProperties2d object with the firing rate maps
#'
#' @docType methods
#' @rdname firingRateMap2d-methods
setGeneric(name="firingRateMap2d",
def=function(sp,st,pt,nRowMap=NA,nColMap=NA)
{standardGeneric("firingRateMap2d")}
)
#' @rdname firingRateMap2d-methods
#' @aliases firingRateMap2d,ANY,ANY-method
setMethod(f="firingRateMap2d",
signature="SpatialProperties2d",
definition=function(sp,st,pt,nRowMap=NA,nColMap=NA)
{
if(length(pt@x)==0)
stop(paste("pt@x has length of 0 in firingRateMap2d",st@session))
if(st@nSpikes==0)
stop(paste("st@nSpikes==0 in firingRateMap2d",st@session))
if(!is.na(nRowMap)|!is.na(nColMap)){
if(is.na(nRowMap))
stop("if you set nColMap, you need to also set nRowMap")
if(is.na(nColMap))
stop("if you set nRowMap, you need to also set nColMap")
}
sp@cellList<-st@cellList
## reduce the size of maps and map autocorrelation
if(sp@reduceSize==T){
x<-pt@x-min(pt@x,na.rm=T)+sp@cmPerBin
y<-pt@y-min(pt@y,na.rm=T)+sp@cmPerBin
}else{
x<-pt@x
y<-pt@y
}
## use -1 as invalid values in c functions
x[is.na(x)]<- -1.0
y[is.na(y)]<- -1.0
## get the dimensions of the map
sp@nRowMap=as.integer(((max(x)+1)/sp@cmPerBin)+1) # x in R is a row
sp@nColMap=as.integer(((max(y)+1)/sp@cmPerBin)+1) # y in R is a col
## user want a map of a given size
if(!is.na(nRowMap)){
if(nRowMap<sp@nRowMap)
stop(paste("nRowMap value",nRowMap,"is smaller than the minimal size of the map",sp@nRowMap))
if(nColMap<sp@nColMap)
stop(paste("nColMap value",nColMap,"is smaller than the minimal size of the map",sp@nColMap))
sp@nRowMap=as.integer(nRowMap)
sp@nColMap=as.integer(nColMap)
}
## get spike position
results<-.Call("spike_position_cwrap",
x,
y,
length(x),
as.integer(st@res),
as.integer(st@nSpikes),
as.integer(pt@resSamplesPerWhlSample),
as.integer(st@startInterval),
as.integer(st@endInterval),
length(st@startInterval))
sp@xSpikes<-results[1,]
sp@ySpikes<-results[2,]
#plot(head(sp@xSpikes[which(sp@xSpikes!=-1.0)],n=20000),head(sp@ySpikes[which(sp@xSpikes!=-1.0)],n=20000))
## make the occupancy map
sp@occupancy<-.Call("occupancy_map_cwrap",
sp@nRowMap,
sp@nColMap,
sp@cmPerBin,
sp@cmPerBin,
x,
y,
length(x),
pt@resSamplesPerWhlSample/pt@samplingRateDat*1000, ## ms per whl samples
as.integer(st@startInterval),
as.integer(st@endInterval),
length(st@startInterval),
as.integer(pt@resSamplesPerWhlSample))
#sp@occupancy
#image((sp@occupancy),zlim=c(0,max(sp@occupancy,na.rm=T)))
## smooth the occupancy map
sp@occupancy<- .Call("smooth_double_gaussian_2d_cwrap",
as.numeric(sp@occupancy),
sp@nColMap, # because C has a different way to order matrix as my c code
sp@nRowMap, #
sp@smoothOccupancySd/sp@cmPerBin,
-1.0)
#image((sp@occupancy),zlim=c(0,max(sp@occupancy,na.rm=T)))
## make the 2d maps
results<- .Call("firing_rate_map_2d_cwrap",
sp@nRowMap,
sp@nColMap,
sp@cmPerBin,
sp@cmPerBin,
sp@xSpikes,
sp@ySpikes,
as.integer(st@clu),
as.integer(st@nSpikes),
as.integer(sp@cellList),
length(sp@cellList),
as.numeric(sp@occupancy),
sp@smoothRateMapSd/sp@cmPerBin)
sp@maps<-array(data=results,dim=(c(sp@nRowMap,sp@nColMap,length(sp@cellList))))
return(sp)
}
)
#' Calculate the occupancy map with the data from a Positrack objects
#'
#' The occupancy map is smoothed with a Gaussian kernel.
#' The amount of smoothing is determined by slots smoothOccupancySd of sp.
#' You can force the function to create maps with arbitrary x and y sizes as long as the map fits into this arbitrary size.
#'
#' @param sp SpatialProperties2d object
#' @param st SpikeTrain object to get the intervals
#' @param pt Positrack object
#' @param nRowMap Numeric indicating the number of rows in the map. By default the maps has the smallest size possible given the
#' position data in the Positrack object. Use this argument to have maps of a fixed size. Needs to be as large as the minimal size of the map
#' @param nColMap Numeric indicating the number of columns in the map. By default the maps has the smallest size possible given the
#' position data in the Positrack object. Use this argument to have maps of a fixed size. Needs to be as large as the minimal size of the map
#' @return SpatialProperties2d object with the occupancy maps
#'
#' @docType methods
#' @rdname occupancyMap2d-methods
setGeneric(name="occupancyMap2d",
def=function(sp,st,pt,nRowMap=NA,nColMap=NA)
{standardGeneric("occupancyMap2d")}
)
#' @rdname occupancyMap2d-methods
#' @aliases occupancyMap2d,ANY,ANY-method
setMethod(f="occupancyMap2d",
signature="SpatialProperties2d",
definition=function(sp,st,pt,nRowMap=NA,nColMap=NA)
{
if(length(pt@x)==0)
stop(paste("pt@x has length of 0 in occupancyMap2d",st@session))
if(length(st@startInterval)==0)
stop(paste("length of st@startInterval == 0 in occupancyMap2d",st@session))
if(!is.na(nRowMap)|!is.na(nColMap)){
if(is.na(nRowMap))
stop("if you set nColMap, you need to also set nRowMap")
if(is.na(nColMap))
stop("if you set nRowMap, you need to also set nColMap")
}
sp@cellList<-st@cellList
## reduce the size of maps and map autocorrelation
if(sp@reduceSize==T){
x<-pt@x-min(pt@x,na.rm=T)+sp@cmPerBin
y<-pt@y-min(pt@y,na.rm=T)+sp@cmPerBin
}else{
x<-pt@x
y<-pt@y
}
## use -1 as invalid values in c functions
x[is.na(x)]<- -1.0
y[is.na(y)]<- -1.0
## get the dimensions of the map
sp@nRowMap=as.integer(((max(x)+1)/sp@cmPerBin)+1) # x in R is a row
sp@nColMap=as.integer(((max(y)+1)/sp@cmPerBin)+1) # y in R is a col
## user want a map of a given size
if(!is.na(nRowMap)){
if(nRowMap<sp@nRowMap)
stop(paste("nRowMap value",nRowMap,"is smaller than the minimal size of the map",sp@nRowMap))
if(nColMap<sp@nColMap)
stop(paste("nColMap value",nColMap,"is smaller than the minimal size of the map",sp@nColMap))
sp@nRowMap=as.integer(nRowMap)
sp@nColMap=as.integer(nColMap)
}
## make the occupancy map
sp@occupancy<-.Call("occupancy_map_cwrap",
sp@nRowMap,
sp@nColMap,
sp@cmPerBin,
sp@cmPerBin,
x,
y,
length(x),
pt@resSamplesPerWhlSample/pt@samplingRateDat*1000, ## ms per whl samples
as.integer(st@startInterval),
as.integer(st@endInterval),
length(st@startInterval),
as.integer(pt@resSamplesPerWhlSample))
## smooth the occupancy map
sp@occupancy<- .Call("smooth_double_gaussian_2d_cwrap",
as.numeric(sp@occupancy),
sp@nColMap, # because C has a different way to order matrix as my c code
sp@nRowMap, #
sp@smoothOccupancySd/sp@cmPerBin,
-1.0)
return(sp)
}
)
#' Calculate the spike-triggered firing rate maps of neurons using a SpikeTrain and Positrack objects
#'
#' Each spike is treated as a reference spike in turn. The map is constructed from the data
#' following the reference spikes by shifting the x and y coordinate so that the position of the
#' agent at the time of the reference spike is 0,0.
#'
#' The occupancy map and the firing rate maps are smoothed with a Gaussian kernel
#' The amount of smoothing is determined by slots smoothOccupancySd and smoothRateMapSd of sp
#'
#' You can set the temporal limit for the data used to construct the map with minIsiMs and maxIsiMs
#'
#'
#' @param sp SpatialProperties1d object
#' @param st SpikeTrain object
#' @param pt Positrack object
#' @param minIsiMs Minimal interspike interval to consider in ms
#' @param maxIsiMs Maximal interspike interval to consider in ms
#' @return SpatialProperties2d object with the spike-triggered firing rate maps
#'
#' @docType methods
#' @rdname spikeTriggeredFiringRateMap2d-methods
setGeneric(name="spikeTriggeredFiringRateMap2d",
def=function(sp,st,pt,minIsiMs,maxIsiMs)
{standardGeneric("spikeTriggeredFiringRateMap2d")}
)
#' @rdname spikeTriggeredFiringRateMap2d-methods
#' @aliases spikeTriggeredFiringRateMap2d,ANY,ANY-method
setMethod(f="spikeTriggeredFiringRateMap2d",
signature="SpatialProperties2d",
definition=function(sp,st,pt,minIsiMs,maxIsiMs)
{
if(length(pt@x)==0)
stop(paste("pt@x has length of 0 in firingRateMap2d",st@session))
if(st@nSpikes==0)
stop(paste("st@nSpikes==0 in firingRateMap2d",st@session))
if(minIsiMs<0)
stop(paste("minIsiMs should be 0 or larger than 0"))
if(maxIsiMs<0)
stop(paste("maxIsiMs should larger than 0"))
if(maxIsiMs<=minIsiMs)
stop(paste("maxIsiMs should be larger than minIsiMs"))
sp@cellList<-st@cellList
## reduce the size of maps and map autocorrelation
if(sp@reduceSize==T){
x<-pt@x-min(pt@x,na.rm=T)+sp@cmPerBin
y<-pt@y-min(pt@y,na.rm=T)+sp@cmPerBin
}else{
x<-pt@x
y<-pt@y
}
## use -1 as invalid values in c functions
x[is.na(x)]<- -1.0
y[is.na(y)]<- -1.0
## get the dimension of the map
## will have twice the x and y size than normal 2d maps
## the idea is that 0,0 is in the middel of the array
## at num_bins_x/2, num_bins_y/2
sp@nRowMap=as.integer(floor(((max(x)+1)/sp@cmPerBin+1))*2)
sp@nColMap=as.integer(floor(((max(y)+1)/sp@cmPerBin+1))*2)
results<- .Call("spike_triggered_firing_rate_maps_cwrap",
as.integer(sp@nRowMap),
as.integer(sp@nColMap),
sp@cmPerBin,
sp@cmPerBin,
as.integer(sp@nRowMap*sp@nColMap),
as.integer(sp@cellList),
length(sp@cellList),
x,
y,
length(x),
as.integer(st@res),
as.integer(st@clu),
st@nSpikes,
as.integer(st@startInterval),
as.integer(st@endInterval),
length(st@startInterval),
pt@resSamplesPerWhlSample/pt@samplingRateDat*1000,
as.integer(pt@resSamplesPerWhlSample),
sp@smoothOccupancySd,
sp@smoothRateMapSd,
minIsiMs,
maxIsiMs,
as.integer(st@samplingRate))
sp@maps<-array(data=results,dim=(c(sp@nRowMap,sp@nColMap,length(sp@cellList))))
return(sp)
}
)
#' Calculate spatial statistics of the firing rate maps of neurons
#'
#' @param sp SpatialProperties2d object
#' @param st SpikeTrain object
#' @param pt Positrack object
#' @param border Set how the border of the environment will be detected. Value can be set to "rectangular" or "circular".
#' Default value is "rectangular"
#' @param triggered Logical indicating whether to calculate a spike-triggered firing rate map instead of conventional map
#' Default value is FALSE
#' @param newMaps logical indicating if new maps are calculated when calling the function
#' @return SpatialProperties2d object with the stats in the following slots: peakRate, infoScore, sparsity, borderScore and gridScore
#'
#' @docType methods
#' @rdname getMapStats-methods
setGeneric(name="getMapStats",
def=function(sp,st,pt,border="rectangular",triggered=F,newMaps=T)
{standardGeneric("getMapStats")}
)
#' @rdname getMapStats-methods
#' @aliases getMapStats,ANY,ANY-method
setMethod(f="getMapStats",
signature="SpatialProperties2d",
definition=function(sp,st,pt,border="rectangular",triggered=FALSE,newMaps=TRUE)
{
if(border!="rectangular" & border!= "circular")
stop(paste("getMapstats, value of border can be \"rectangular\" or \"circular\" but is", border))
## make the maps
if(newMaps==TRUE){
if(triggered==FALSE){
sp<-firingRateMap2d(sp,st,pt)
} else{
sp<-spikeTriggeredFiringRateMap2d(sp,st,pt)
}
}
### get peak rates
sp@peakRate<-apply(sp@maps,3,max)
### get info scores
sp@infoScore<- .Call("information_score_cwrap",
length(sp@cellList),
as.numeric(sp@maps),
as.numeric(sp@occupancy),
as.integer(sp@nColMap*sp@nRowMap))
### get sparsity scores
sp@sparsity<- .Call("sparsity_score_cwrap",
length(sp@cellList),
as.numeric(sp@maps),
as.numeric(sp@occupancy),
as.integer(sp@nColMap*sp@nRowMap))
if(border=="rectangular"){
### get border score
results<-.Call("border_score_rectangular_environment_cwrap", ## if no field is detected, you get NaN values
as.integer(sp@cellList),
length(sp@cellList),
sp@nRowMap,
sp@nColMap,
sp@occupancy,
sp@maps,
sp@borderPercentageThresholdField,
as.integer(sp@borderMinBinsInField))
sp@borderScore<-results[1,]
sp@borderCM<-results[2,]
sp@borderDM<-results[3,]
sp@borderNumFieldsDetected<-results[4,]
}
if(border=="circular")
{
### get border score
results<-.Call("border_score_circular_environment_cwrap", ## if no field is detected, you get NaN values
as.integer(sp@cellList),
length(sp@cellList),
sp@nRowMap,
sp@nColMap,
sp@occupancy,
sp@maps,
sp@borderPercentageThresholdField,
as.integer(sp@borderMinBinsInField))
sp@borderScore<-results[1,]
sp@borderCM<-results[2,]
sp@borderDM<-results[3,]
sp@borderNumFieldsDetected<-results[4,]
sp@mapPolarity<-results[5,]
}
# make spatial autocorrelations
sp<-mapSpatialAutocorrelation(sp)
sp@gridScore<-.Call("grid_score_cwrap",
length(sp@cellList),
sp@autos,
sp@nRowAuto,
sp@nColAuto,
sp@cmPerBin,
as.integer(sp@gridScoreNumberFieldsToDetect),
as.integer(sp@gridScoreMinNumBinsPerField),
sp@gridScoreFieldThreshold,
-2.0) #invalid
sp@gridSpacing<- .Call("grid_spacing_cwrap",
length(sp@cellList),
sp@autos,
sp@nColAuto,
sp@nRowAuto,
sp@cmPerBin,
as.integer(sp@gridScoreNumberFieldsToDetect),
as.integer(sp@gridScoreMinNumBinsPerField),
sp@gridScoreFieldThreshold,
-2.0)
return(sp)
}
)
#' Calculate random spatial statistics of the firing rate maps of neurons
#'
#' The random values are obtained by shifting the position data.
#' The number of shufflings is nShufflings of the SpatialProperties2d object.
#'
#' @param sp SpatialProperties2d object
#' @param st SpikeTrain object
#' @param pt Positrack object
#' @param border Set how the border of the environment will be detected. Value can be set to "rectangular" or "circular.
#' Default value is rectangular
#' @param triggered Logical indicating whether to calculate a spike-triggered firing rate map instead of conventional map
#' Default value is FALSE
#' @return SpatialProperties2d object with the random stats in the following slots: peakRateShuffle, infoScoreShuffle,
#' sparsityShuffle, borderScoreShuffle and gridScoreShuffle, etc.
#'
#' @docType methods
#' @rdname getMapStatsShuffle-methods
setGeneric(name="getMapStatsShuffle",
def=function(sp,st,pt,border="rectangular",triggered=F)
{standardGeneric("getMapStatsShuffle")}
)
#' @rdname getMapStatsShuffle-methods
#' @aliases getMapStatsShuffle,ANY,ANY-method
setMethod(f="getMapStatsShuffle",
signature="SpatialProperties2d",
definition=function(sp,st,pt,border="rectangular",triggered=FALSE){
if(border!="rectangular" & border!= "circular")
stop(paste("getMapstatsShuffle, value of border can be \"rectangular\" or \"circular\" but is", border))
if(sp@nShufflings==0)
stop("sp@nShufflings==0")
sp@peakRateShuffle=numeric()
sp@infoScoreShuffle=numeric()
sp@sparsityShuffle=numeric()
sp@borderScoreShuffle=numeric()
sp@borderCMShuffle=numeric()
sp@borderDMShuffle=numeric()
sp@gridScoreShuffle=numeric()
for(i in 1:sp@nShufflings){
pts<-shiftPositionRandom(pt)
## make the maps
if(triggered==FALSE)
sp<-firingRateMap2d(sp,st,pts)
else
sp<-spikeTriggeredFiringRateMap2d(sp,st,pts)
sp@peakRateShuffle <- c(sp@peakRateShuffle,apply(sp@maps,3,max))
sp@infoScoreShuffle <- c(sp@infoScoreShuffle,
.Call("information_score_cwrap",
length(sp@cellList),
as.numeric(sp@maps),
as.numeric(sp@occupancy),
as.integer(sp@nColMap*sp@nRowMap)))
sp@sparsityShuffle <- c(sp@sparsityShuffle, .Call("sparsity_score_cwrap",
length(sp@cellList),
as.numeric(sp@maps),
as.numeric(sp@occupancy),
as.integer(sp@nColMap*sp@nRowMap)))
if(border=="rectangular"){
### get border score
results<-.Call("border_score_rectangular_environment_cwrap", # if no field is detected, you get NaN values
as.integer(sp@cellList),
length(sp@cellList),
sp@nRowMap,
sp@nColMap,
sp@occupancy,
sp@maps,
sp@borderPercentageThresholdField,
as.integer(sp@borderMinBinsInField))
sp@borderScoreShuffle <-c(sp@borderScoreShuffle,results[1,])
sp@borderCMShuffle<-c(sp@borderCMShuffle,results[2,])
sp@borderDMShuffle<-c(sp@borderDMShuffle,results[3,])
sp@borderNumFieldsDetectedShuffle<-c(sp@borderNumFieldsDetectedShuffle,results[4,])
}
if(border=="circular")
{
### get border score
results<-.Call("border_score_circular_environment_cwrap", ## if no field is detected, you get NaN values
as.integer(sp@cellList),
length(sp@cellList),
sp@nRowMap,
sp@nColMap,
sp@occupancy,
sp@maps,
sp@borderPercentageThresholdField,
as.integer(sp@borderMinBinsInField))
sp@borderScoreShuffle<-c(sp@borderScoreShuffle,results[1,])
sp@borderCMShuffle<-c(sp@borderCMShuffle,results[2,])
sp@borderDMShuffle<-c(sp@borderDMShuffle,results[3,])
sp@borderNumFieldsDetectedShuffle<-c(sp@borderNumFieldsDetectedShuffle,results[4,])
sp@mapPolarityShuffle<-c(sp@mapPolarityShuffle,results[5,])
}
# make spatial autocorrelations
sp<-mapSpatialAutocorrelation(sp)
sp@gridScoreShuffle<-c(sp@gridScoreShuffle,
.Call("grid_score_cwrap",
length(sp@cellList),
sp@autos,
sp@nRowAuto,
sp@nColAuto,
sp@cmPerBin,
as.integer(sp@gridScoreNumberFieldsToDetect),
as.integer(sp@gridScoreMinNumBinsPerField),
sp@gridScoreFieldThreshold,
-2.0))
}
return(sp)
})
#' Calculate the spatial autocorrelation of the firing rate maps of neurons
#'
#' The autocorrelation is performed on the firing rate maps of the SpatialProperties2d object
#'
#' @param sp SpatialProperties2d object
#' @return SpatialProperties2d object with the spatial autocorrelation in slot auto
#'
#' @docType methods
#' @rdname mapSpatialAutocorrelation-methods
setGeneric(name="mapSpatialAutocorrelation",
def=function(sp)
{standardGeneric("mapSpatialAutocorrelation")}
)
#' @rdname mapSpatialAutocorrelation-methods
#' @aliases mapSpatialAutocorrelation,ANY,ANY-method
setMethod(f="mapSpatialAutocorrelation",
signature="SpatialProperties2d",
definition=function(sp)
{
if(length(sp@maps)==0)
stop("Need to call firingRateMap2d first to run getMapStats")
sp@nColAuto = as.integer((sp@nColMap*2)+1)
sp@nRowAuto = as.integer((sp@nRowMap*2)+1)
results<- .Call("map_autocorrelation_cwrap",
length(sp@cellList),
as.numeric(sp@maps),
sp@nRowMap,
sp@nColMap,
sp@nRowAuto,
sp@nColAuto,
as.integer(sp@minValidBinsAuto))
sp@autos<-array(data=results,dim=(c(sp@nRowAuto,sp@nColAuto,length(sp@cellList))))
return(sp)
}
)
#' Calculate the spatial autocorrelation of the firing rate maps of neurons and removed the detected fields
#'
#' The autocorrelation is performed on the firing rate maps of the SpatialProperties2d object.
#' Fields are detected as when one wants to calculate grid scores
#' Useful to test the different steps of the calculation of the grid score.
#'
#' @param sp SpatialProperties2d object
#' @return SpatialProperties2d object with the spatial autocorrelation in slot autosDetect
#'
#' @docType methods
#' @rdname autocorrelationNoFields-methods
setGeneric(name="autocorrelationNoFields",
def=function(sp)
{standardGeneric("autocorrelationNoFields")}
)
#' @rdname autocorrelationNoFields-methods
#' @aliases autocorrelationNoFields,ANY,ANY-method
setMethod(f="autocorrelationNoFields",
signature="SpatialProperties2d",
definition=function(sp)
{
if(length(sp@autos)==0)
stop("Need to call mapSpatialAutocorrelation first to run gridScore()")
results<-.Call("detect_and_remove_field_cwrap",
as.integer(sp@cellList),
length(sp@cellList),
sp@autos,
sp@nRowAuto,
sp@nColAuto,
as.integer(sp@gridScoreNumberFieldsToDetect),
as.integer(sp@gridScoreMinNumBinsPerField),
sp@gridScoreFieldThreshold,
-2.0) #invalid
sp@autosDetect<-array(data=results,dim=(c(sp@nRowAuto,sp@nColAuto,length(sp@cellList))))
return(sp)
}
)
#' Calculate the spatial autocorrelation of the firing rate maps of neurons and get the region use for grid score calculation
#' Autocorrelation is performed on the firing rate maps of the SpatialProperties2d object.
#' Fields are detected as when one wants to calculate grid scores.
#' Then the region with the 6 fields surrounding the center is kept
#' Useful to test the different steps of the calculation of the grid score.
#'
#' @param sp SpatialProperties2d object
#' @return SpatialProperties2d object with the spatial autocorrelation in slot autosDoughnut
#'
#' @docType methods
#' @rdname autocorrelationDoughnut-methods
setGeneric(name="autocorrelationDoughnut",
def=function(sp)
{standardGeneric("autocorrelationDoughnut")}
)
#' @rdname autocorrelationDoughnut-methods
#' @aliases autocorrelationDoughnut,ANY,ANY-method
setMethod(f="autocorrelationDoughnut",
signature="SpatialProperties2d",
definition=function(sp)
{
if(length(sp@autos)==0)
stop("Need to call mapSpatialAutocorrelation first to run gridScore()")
results<-.Call("autocorrelation_doughnut_cwrap",
as.integer(sp@cellList),
length(sp@cellList),
sp@autos,
sp@nRowAuto,
sp@nColAuto,
as.integer(sp@gridScoreNumberFieldsToDetect),
as.integer(sp@gridScoreMinNumBinsPerField),
sp@gridScoreFieldThreshold,
sp@cmPerBin,
-2.0) #invalid
sp@autosDoughnut<-array(data=results,dim=(c(sp@nRowAuto,sp@nColAuto,length(sp@cellList))))
return(sp)
}
)
#' Get the rotated region of the spatial autocorrelation of the firing rate maps when calculating grid scores
#' Autocorrelation is performed on the firing rate maps of the SpatialProperties2d object.
#' Fields are detected as when one wants to calculate grid scores.
#' Then the region with the 6 fields surrounding the center is kept
#' The rotated copy of the region are stored in slot autosDoughnutRotate
#' Useful to test the different steps of the calculation of the grid score.
#'
#' @param sp SpatialProperties2d object
#' @return SpatialProperties2d object with the spatial autocorrelation in slot autosDoughnutRotate
#'
#' @docType methods
#' @rdname autocorrelationDoughnutRotate-methods
setGeneric(name="autocorrelationDoughnutRotate",
def=function(sp)
{standardGeneric("autocorrelationDoughnutRotate")}
)
#' @rdname autocorrelationDoughnutRotate-methods
#' @aliases autocorrelationDoughnutRotate,ANY,ANY-method
setMethod(f="autocorrelationDoughnutRotate",
signature="SpatialProperties2d",
definition=function(sp)
{
if(length(sp@autos)==0)
stop("Need to call mapSpatialAutocorrelation first to run gridScore()")
results<-.Call("autocorrelation_doughnut_rotate_cwrap",
as.integer(sp@cellList),
length(sp@cellList),
sp@autos,
sp@nRowAuto,
sp@nColAuto,
as.integer(sp@gridScoreNumberFieldsToDetect),
as.integer(sp@gridScoreMinNumBinsPerField),
sp@gridScoreFieldThreshold,
sp@cmPerBin,
sp@nAutoRotations,
sp@AutoRotationDegree,
-2.0) #invalid
sp@autosDoughnutRotate<-array(data=results,dim=(c(sp@nRowAuto,sp@nColAuto,length(sp@cellList)*sp@nAutoRotations)))
return(sp)
}
)
#' Calculate the grid scores from the spatial autocorrelations
#'
#'
#' @param sp SpatialProperties2d object
#' @return SpatialProperties2d object with the grid scores in the slot gridScore
#'
#' @docType methods
#' @rdname gridScore-methods
setGeneric(name="gridScore",
def=function(sp)
{standardGeneric("gridScore")}
)
#' @rdname gridScore-methods
#' @aliases gridScore,ANY,ANY-method
setMethod(f="gridScore",
signature="SpatialProperties2d",
definition=function(sp)
{
if(length(sp@autos)==0)
stop("Need to call mapSpatialAutocorrelation first to run gridScore()")
sp@gridScore<-.Call("grid_score_cwrap",
length(sp@cellList),
sp@autos,
sp@nRowAuto,
sp@nColAuto,
sp@cmPerBin,
as.integer(sp@gridScoreNumberFieldsToDetect),
as.integer(sp@gridScoreMinNumBinsPerField),
sp@gridScoreFieldThreshold,
-2.0) #invalid
return(sp)
}
)
#' Calculate the grid spacing from the spatial autocorrelations
#'
#' @param sp SpatialProperties2d object
#' @return SpatialProperties2d object with the grid spacing in the slot gridSpacing
#'
#' @docType methods
#' @rdname gridSpacing-methods
setGeneric(name="gridSpacing",
def=function(sp)
{standardGeneric("gridSpacing")}
)
#' @rdname gridSpacing-methods
#' @aliases gridSpacing,ANY,ANY-method
setMethod(f="gridSpacing",
signature="SpatialProperties2d",
definition=function(sp)
{
if(length(sp@autos)==0)
stop("Need to call mapSpatialAutocorrelation first to run gridSpacing()")
sp@gridSpacing<- .Call("grid_spacing_cwrap",
length(sp@cellList),
sp@autos,
sp@nColAuto,
sp@nRowAuto,
sp@cmPerBin,
as.integer(sp@gridScoreNumberFieldsToDetect),
as.integer(sp@gridScoreMinNumBinsPerField),
sp@gridScoreFieldThreshold,
-2.0)
return(sp)
}
)
# setGeneric(name="gridOrientation",
# def=function(sp)
# {standardGeneric("gridOrientation")}
# )
# setMethod(f="gridOrientation",
# signature="SpatialProperties2d",
# definition=function(sp)
# {
# if(length(sp@autos)==0)
# stop("Need to call mapSpatialAutocorrelation first to run gridOrientation()")
#
# #
# # sp@gridOrientation<- .Call("grid_orientation_cwrap",
# # as.integer(sp@cellList),
# # length(sp@cellList),
# # sp@autos,
# # sp@nColAuto,
# # sp@nRowAuto,
# # sp@cmPerBin,
# # as.integer(sp@gridScoreNumberFieldsToDetect),
# # as.integer(sp@gridScoreMinNumBinsPerField),
# # sp@gridScoreFieldThreshold,
# # -2.0)
# return(sp)
# }
# )
#' Calculate border scores from the firing maps
#'
#'
#' @param sp SpatialProperties2d object
#' @param border Set to rectangular or circular depending on the shape of environment
#' @return SpatialProperties2d object with the border scores in the slot borderScore
#'
#' @docType methods
#' @rdname borderScore-methods
setGeneric(name="borderScore",
def=function(sp,border="rectangular")
{standardGeneric("borderScore")}
)
#' @rdname borderScore-methods
#' @aliases borderScore,ANY,ANY-method
setMethod(f="borderScore",
signature="SpatialProperties2d",
definition=function(sp,border="rectangular")
{
if(border!="rectangular"&border!="circular")
stop("only border = rectangular or circular is supported")
if(length(sp@maps)==0)
stop("Need to call firingRateMap2d first to run borderScore()")
if(border=="rectangular"){
results<-.Call("border_score_rectangular_environment_cwrap",
as.integer(sp@cellList),
length(sp@cellList),
sp@nRowMap,
sp@nColMap,
sp@occupancy,
sp@maps,
sp@borderPercentageThresholdField,
as.integer(sp@borderMinBinsInField))
sp@borderScore<-results[1,]
sp@borderCM<-results[2,]
sp@borderDM<-results[3,]
sp@borderNumFieldsDetected<-results[4,]
}
if(border=="circular"){
### get border score
results<-.Call("border_score_circular_environment_cwrap", ## if no field is detected, you get NaN values
as.integer(sp@cellList),
length(sp@cellList),
sp@nRowMap,
sp@nColMap,
sp@occupancy,
sp@maps,
sp@borderPercentageThresholdField,
as.integer(sp@borderMinBinsInField))
sp@borderScore<-results[1,]
sp@borderCM<-results[2,]
sp@borderDM<-results[3,]
sp@borderNumFieldsDetected<-results[4,]
sp@mapPolarity<-results[5,]
}
return(sp)
}
)
#' Detect the border pixels in an environment and return
#' a matrix with border pixels identified.
#'
#' Call firingRateMap2d() to construct the map first before calling this function
#'
#' @param sp SpatialProperties2d object
#' @param border Set to rectangular or circular depending of the type of environment
#' Default value is rectangular
#' @return A matrix with the border pixels set to non negative positive values
#'
#' @docType methods
#' @rdname borderDetection-methods
setGeneric(name="borderDetection",
def=function(sp,border="rectangular")
{standardGeneric("borderDetection")}
)
#' @rdname borderDetection-methods
#' @aliases borderDetection,ANY,ANY-method
setMethod(f="borderDetection",
signature="SpatialProperties2d",
definition=function(sp,border="rectangular")
{
if(border!="rectangular" & border!= "circular")
stop(paste("borderDetection, value of border can be \"rectangular\" or \"circular\" but is", border))
if(length(sp@maps)==0)
stop("Need to call firingRateMap2d first to run borderDetection()")
if(border=="rectangular"){
results<-.Call("border_detection_rectangular_environment_cwrap",
sp@nRowMap,
sp@nColMap,
sp@occupancy)
}
if(border=="circular"){
results<-.Call("border_detection_circular_environment_cwrap",
sp@nRowMap,
sp@nColMap,
sp@occupancy)
}
return(results)
})
#' Speed scores from spike train and positrack
#'
#'
#' @param sp SpatialProperties2d object
#' @param st SpikeTrain object with ifr
#' @param pt Positrack object
#' @param minSpeed Minimal speed to be considered
#' @param maxSpeed Maximal speed to be considered
#' @param angular Logical, if TRUE will use the angular velocity of the head instead of running speed
#' @param runLm Logical, if TRUE a linear model will be build and slope and intercept calculated
#' @return SpatialProperties2d object with the speed scores in the slots SpeedScore.
#' If runLm argument was TRUE, then speedRateSlope and peedRateIntercept will also be filled
#' The analysis only use period withing the intervals of the SpikeTrain object
#' The function ifr() should be called prior to calling speedScore()
#'
#' @docType methods
#' @rdname speedScore-methods
setGeneric(name="speedScore",
def=function(sp,st,pt,minSpeed,maxSpeed,angular=F,runLm=F)
{standardGeneric("speedScore")}
)
#' @rdname speedScore-methods
#' @aliases speedScore,ANY,ANY-method
setMethod(f="speedScore",
signature="SpatialProperties2d",
definition=function(sp,st,pt,minSpeed=2,maxSpeed=100,angular=F,runLm=F)
{
if(length(pt@speed)==0)
stop("pt@speed has length of 0")
if(dim(st@ifr)[1]!=length(st@cellList))
stop(paste("ifr dim",dim(st@ifr)[1], "is not equal to number of cells", length(sp@cellList)))
sp@cellList<-st@cellList
## get the ifr and ifrTime inside st@interval
resTime<-st@ifrTime*st@samplingRate
index<-as.logical(.Call("resWithinIntervals",
length(st@startInterval),
as.integer(st@startInterval),
as.integer(st@endInterval),
length(resTime),
as.integer(resTime)))
ifrSel<-matrix(st@ifr[,index],nrow=length(st@cellList))
resTime<-resTime[index]
## get the speed for the res values
if(angular==FALSE){
speed<-getSpeedAtResValues(pt,resTime)
}
else{
print("angular")
speed<-getSpeedAtResValues(pt,resTime,angular=TRUE)
}
## speed filter
index<-which(speed>minSpeed&speed<maxSpeed)
ifrSel<-matrix(ifrSel[,index],nrow=length(st@cellList))
speed<-speed[index]
## do the correlation
sp@speedScore<-apply(ifrSel,1,cor,speed)
if(runLm){
c<-apply(ifrSel,1,function(x,y){lm(x~y)$coefficients},speed)
c.p_value<-apply(ifrSel,1,function(x,y){summary(lm(x~y))$coefficients[2, 4]},speed)
sp@speedPValue=c.p_value
sp@speedRateSlope=c[2,]
sp@speedRateIntercept=c[1,]
}
return(sp)
}
)
#' Random speed scores from spike train and positrack
#'
#'
#' @param sp SpatialProperties2d object
#' @param st SpikeTrain object with ifr
#' @param pt Positrack object
#' @param minSpeed Minimal speed to be considered
#' @param maxSpeed Maximal speed to be considered
#' @return SpatialProperties2d object with the random speed scores in the slots SpeedScoreShuffle
#'
#' @docType methods
#' @rdname speedScoreShuffle-methods
setGeneric(name="speedScoreShuffle",
def=function(sp,st,pt,minSpeed,maxSpeed)
{standardGeneric("speedScoreShuffle")}
)
#' @rdname speedScoreShuffle-methods
#' @aliases speedScoreShuffle,ANY,ANY-method
setMethod(f="speedScoreShuffle",
signature="SpatialProperties2d",
definition=function(sp,st,pt,minSpeed=3,maxSpeed=100)
{
if(length(pt@speed)==0)
stop("pt@speed has length of 0")
if(dim(st@ifr)[1]!=length(sp@cellList))
stop(paste("ifr dim",dim(st@ifr)[1], "is not equal to number of cells", length(sp@cellList)))
if(sp@nShufflings==0)
stop("sp@nShufflings==0")
sp@cellList<-st@cellList
## get the ifr and ifrTime inside st@interval
resTime<-st@ifrTime*st@samplingRate
index<-as.logical(.Call("resWithinIntervals",
length(st@startInterval),
as.integer(st@startInterval),
as.integer(st@endInterval),
length(resTime),
as.integer(resTime)))
ifrSel<-matrix(st@ifr[,index],nrow=length(st@cellList))
resTime<-resTime[index]
#clear from previous data
sp@speedScoreShuffle<-numeric()
for(i in 1:sp@nShufflings){
ifrSels<-ifrSel
resTimes<-resTime
# shuffle speed
pts<-shiftSpeedRandom(pt)
## get the speed for the res values
speed<-getSpeedAtResValues(pts,resTimes)
## speed filter
index<-which(speed>minSpeed&speed<maxSpeed)
ifrSels<-matrix(ifrSels[,index],nrow=length(st@cellList))
speed<-speed[index]
## do the correlation
sp@speedScoreShuffle<-c(sp@speedScoreShuffle,
apply(ifrSels,1,cor,speed))
}
return(sp)
}
)
#' Speed-rate tuning curve from spike train and positrack
#'
#' Uses the ifr of cells to calculate the mean firing rate of neurons
#' for different speed bands. You need to call ifr() on the st object first.
#'
#' @param sp SpatialProperties2d object
#' @param st SpikeTrain object with ifr
#' @param pt Positrack object
#' @param minSpeed Minimal speed to be considered
#' @param maxSpeed Maximal speed to be considered
#' @param angular Logical, if TRUE then angular velocity is used instead of running speed
#' @param binSize Size of the bins in the tuning curve
#' @param maxBin Maximum speed in the tuning curve
#' @return data.frame with the speed-rate tuning curves
#'
#' @docType methods
#' @rdname speedRateTuningCurve-methods
setGeneric(name="speedRateTuningCurve",
def=function(sp,st,pt,minSpeed,maxSpeed,angular=FALSE,binSize=5,maxBin=30)
{standardGeneric("speedRateTuningCurve")}
)
#' @rdname speedRateTuningCurve-methods
#' @aliases speedRateTuningCurve,ANY,ANY-method
setMethod(f="speedRateTuningCurve",
signature="SpatialProperties2d",
definition=function(sp,st,pt,minSpeed=2,maxSpeed=100,angular=FALSE,binSize=5,maxBin=30)
{
if(length(pt@speed)==0)
stop("pt@speed has length of 0")
if(dim(st@ifr)[1]!=length(st@cellList))
stop(paste("ifr dim",dim(st@ifr)[1], "is not equal to number of cells", length(sp@cellList)))
sp@cellList<-st@cellList
## get the ifr and ifrTime inside st@interval
resTime<-st@ifrTime*st@samplingRate
index<-as.logical(.Call("resWithinIntervals",
length(st@startInterval),
as.integer(st@startInterval),
as.integer(st@endInterval),
length(resTime),
as.integer(resTime)))
ifrSel<-matrix(st@ifr[,index],nrow=length(st@cellList))
resTime<-resTime[index]
## get the speed for the res values
if(angular==FALSE)
speed<-getSpeedAtResValues(pt,resTime)
else
speed<-getSpeedAtResValues(pt,resTime,angular=TRUE)
## speed filter
index<-which(speed>minSpeed&speed<maxSpeed)
ifrSel<-matrix(ifrSel[,index],nrow=length(st@cellList))
speed<-speed[index]
## speed bands
m<-matrix(ncol=2,c(seq(0,maxBin-binSize,binSize),seq(binSize,maxBin,binSize)))
## function to get the mean firing rate at different speed bands
fn<-function(ifr,speed,m){
v<-numeric(length=nrow(m))
for(i in 1:nrow(m)){
v[i]<-mean(ifr[which(speed>m[i,1]&speed<=m[i,2])],na.rm=T)
}
return(v)
}
## apply to each neuron,
rate<-as.numeric(apply(ifrSel,1,fn,speed,m))
## create a data.frame
df<-data.frame(clu=rep(st@cellList,each=nrow(m)),
min.speed=m[,1],
max.speed=m[,2],
mid=m[,1]+(m[,2]-m[,1])/2,
rate=rate)
return(df)
}
)
#' Calculate the center of mass of the firing rate maps in a SpatialProperties2d object
#'
#'
#' @param sp SpatialProperties2d object
#' @return matrix containing the center of mass of the firing rate maps
#'
#' @docType methods
#' @rdname firingRateMapCenterOfMass-methods
setGeneric(name="firingRateMapCenterOfMass",
def=function(sp)
{standardGeneric("firingRateMapCenterOfMass")}
)
#' @rdname firingRateMapCenterOfMass-methods
#' @aliases firingRateMapCenterOfMass,ANY,ANY-method
setMethod(f="firingRateMapCenterOfMass",
signature="SpatialProperties2d",
definition=function(sp)
{
if(length(sp@maps)==0)
stop("firingRateMapCenterOfMass: length(sp@maps)==0")
if(length(dim(sp@maps))!=3)
stop("firingRateMapCenterOfMass: length(dim(sp@maps))!=3")
sp@maps[which(sp@maps==-1.0)]<-NA
cm<-t(apply(sp@maps,3,centerOfMass))
colnames(cm)<-c("row","col")
return(cm)
}
)
#' Calculate a Pearson correlation coefficients between the firing rate maps of two SpatialProperties2d objects
#'
#' Used to estimate map stability between the firing rate maps in two conditions.
#'
#' The maps of 2 SpatialProperties2d are compared.
#' If the maps are made with different Positrack objects, make sure that the sp@reduceSize is set to FALSE.
#' Also make sure that the maps of the 2 SpatialProperties2d objects have the same dimensions by using the
#' arguments nRowMap and nColMap when calling firingRateMap2d().
#'
#'
#' @param sp1 SpatialProperties2d object
#' @param sp2 SpatialProperties2d object
#' @return Numeric containing the Pearson correlation coefficients
#'
#' @docType methods
#' @rdname firingRateMapCorrelation-methods
setGeneric(name="firingRateMapCorrelation",
def=function(sp1,sp2)
{standardGeneric("firingRateMapCorrelation")}
)
#' @rdname firingRateMapCorrelation-methods
#' @aliases firingRateMapCorrelation,ANY,ANY-method
setMethod(f="firingRateMapCorrelation",
signature="SpatialProperties2d",
definition=function(sp1,sp2)
{
if(class(sp1)!="SpatialProperties2d")
stop("firingRateMapCorrelation: sp1 is not a SpatialProperties2d object")
if(class(sp2)!="SpatialProperties2d")
stop("firingRateMapCorrelation: sp2 is not a SpatialProperties2d object")
if(length(sp1@maps)==0)
stop("firingRateMapCorrelation: length(sp1@maps)==0")
if(length(dim(sp1@maps))!=3)
stop("firingRateMapCorrelation: length(dim(sp1@maps))!=3")
if(!identical(dim(sp1@maps),dim(sp2@maps)))
stop("firingRateMapCorrelation: the dimensions of the maps arrays in sp1 and sp2 are not equal")
sp1@maps[which(sp1@maps==-1.0)]<-NA
sp2@maps[which(sp2@maps==-1.0)]<-NA
return(sapply(1:dim(sp1@maps)[3],
function(i) cor(as.numeric(sp1@maps[,,i]), as.numeric(sp2@maps[,,i]),use="pairwise.complete.obs")))
}
)
#' Return the firing rate maps in a data.frame
#'
#'
#' @param sp SpatialProperties2d object
#' @return data.frame with the firing rate maps. Column names are clu.id, x, y, rate
#'
#' @docType methods
#' @rdname mapsAsDataFrame-methods
setGeneric(name="mapsAsDataFrame",
def=function(sp)
{standardGeneric("mapsAsDataFrame")}
)
#' @rdname mapsAsDataFrame-methods
#' @aliases mapsAsDataFrame,ANY,ANY-method
setMethod(f="mapsAsDataFrame",
signature="SpatialProperties2d",
definition=function(sp)
{
if(length(sp@maps)==0)
stop("Need to call firingRateMap2d first to run mapsAsDataFrame()")
data.frame(clu.id=rep(paste(sp@session,sp@cellList,sep="_"),each=sp@nRowMap*sp@nColMap),
x=rep(1:sp@nRowMap,sp@nColMap), ## warning the x and y were swapped
y=rep(1:sp@nColMap,each=sp@nRowMap), ## warning the x and y were swapped
rate=as.numeric(sp@maps))
}
)
#' Return firing rate map statisitcs as a data.frame
#'
#'
#' @param sp SpatialProperties2d object
#' @param shuffle Logical, if TRUE returns the stats of the shuffling analysis
#' @return data.frame with the firing rate map stats.
#'
#' @docType methods
#' @rdname statsAsDataFrame-methods
setGeneric(name="statsAsDataFrame",
def=function(sp,shuffle)
{standardGeneric("statsAsDataFrame")}
)
#' @rdname statsAsDataFrame-methods
#' @aliases statsAsDataFrame,ANY,ANY-method
setMethod(f="statsAsDataFrame",
signature="SpatialProperties2d",
definition=function(sp,shuffle=FALSE)
{
if(shuffle==FALSE){
if(length(sp@maps)==0)
stop("Need to call getMapStats() before statsAsDataFrame")
if(length(sp@infoScore)==0)
stop("Need to call getMapStats() before statsAsDataFrame")
df<-data.frame(clu.id=paste(sp@session,sp@cellList,sep="_"),
peakRate=sp@peakRate,
infoScore=sp@infoScore,
sparsity=sp@sparsity,
borderScore=sp@borderScore,
borderCM=sp@borderCM,
borderDM=sp@borderDM,
gridScore=sp@gridScore,
gridSpacing=sp@gridSpacing)
}
if(shuffle==TRUE){
if(length(sp@maps)==0)
stop("Need to call getMapStatsShuffle() before statsAsDataFrame with shuffle=T")
if(length(sp@infoScoreShuffle)==0)
stop("Need to call getMapStatsShuffle() before statsAsDataFrame with shuffle=T")
df<-data.frame(clu.id=paste(sp@session,sp@cellList,sep="_"),
peakRate=sp@peakRateShuffle,
infoScore=sp@infoScoreShuffle,
sparsity=sp@sparsityShuffle,
borderScore=sp@borderScoreShuffle,
borderCM=sp@borderCMShuffle,
borderDM=sp@borderDMShuffle,
gridScore=sp@gridScoreShuffle)
}
return(df)
}
)
#' Calculate a Pearson correlation coefficients between maps of two SpatialProperties2d object (sp1 and sp2)
#' after rotating the maps of the second object
#'
#' Used to estimate whether the spatial representation rotated between two conditions
#'
#'
#' @param sp1 SpatialProperties2d object
#' @param sp2 SpatialProperties2d object
#' @param numRotations Number of rotation steps
#' @param rotationDegrees Number of degrees for each rotation step
#' @return Matrix containing the Pearson correlation coefficients after different rotations (cols), each map pair has its own row
#'
#' @docType methods
#' @rdname firingRateMapCorrelationRotation-methods
setGeneric(name="firingRateMapCorrelationRotation",
def=function(sp1,sp2,numRotations,rotationDegrees)
{standardGeneric("firingRateMapCorrelationRotation")}
)
#' @rdname firingRateMapCorrelationRotation-methods
#' @aliases firingRateMapCorrelationRotation,ANY,ANY-method
setMethod(f="firingRateMapCorrelationRotation",
signature="SpatialProperties2d",
definition=function(sp1,sp2,numRotations=36,rotationDegrees=10)
{
if(class(sp1)!="SpatialProperties2d")
stop("firingRateMapCorrelationRotation: sp1 is not a SpatialProperties2d object")
if(class(sp2)!="SpatialProperties2d")
stop("firingRateMapCorrelationRotation: sp2 is not a SpatialProperties2d object")
if(length(sp1@maps)==0)
stop("firingRateMapCorrelationRotation: length(sp1@maps)==0")
if(length(dim(sp1@maps))!=3)
stop("firingRateMapCorrelationRotation: length(dim(sp1@maps))!=3")
if(!identical(dim(sp1@maps),dim(sp2@maps)))
stop("firingRateMapCorrelationRotation: the dimensions of the maps arrays in sp1 and sp2 are not equal")
if(numRotations<1)
stop("firingRateMapCorrelationRotation: numRotations<1")
rotations<-seq(from=0,by=rotationDegrees,length=numRotations)%%360
results<-matrix(ncol=length(rotations),nrow=length(sp1@cellList),dimnames=list(sp1@cellList,rotations))
for(i in 1:length(rotations)){
spr<-firingRateMapsRotation(sp2,rotations[i])
results[,i]<-firingRateMapCorrelation(sp1,spr)
}
return(results)
}
)
#' Rotate the firing rate maps of a SpatialProperties2d object
#'
#'
#' @param sp SpatialProperties2d object
#' @param rotationDegrees Degrees of the rotation
#' @return SpatialProperties2d object with the rotated maps
#'
#' @docType methods
#' @rdname firingRateMapsRotation-methods
setGeneric(name="firingRateMapsRotation",
def=function(sp,rotationDegrees)
{standardGeneric("firingRateMapsRotation")}
)
#' @rdname firingRateMapsRotation-methods
#' @aliases firingRateMapsRotation,ANY,ANY-method
setMethod(f="firingRateMapsRotation",
signature="SpatialProperties2d",
definition=function(sp,rotationDegrees=10)
{
if(length(sp@maps)==0)
stop("firingRateMapsRotation: length(sp@maps)==0")
if(rotationDegrees<0)
stop("firingRateMapsRotation: rotationDegree<0")
if(rotationDegrees>360)
stop("firingRateMapsRotation: rotationDegree>360")
results<-.Call("maps_rotate_cwrap",
as.numeric(sp@maps),
sp@nRowMap,
sp@nColMap,
length(sp@cellList),
rotationDegrees)
sp@maps<-array(data=results,dim=(c(sp@nRowMap,sp@nColMap,length(sp@cellList))))
return(sp)
}
)
#' Get a given percentile from the firing rate maps of a SpatialProperties2d object
#'
#' @param sp SpatialProperties2d object
#' @param percentile Given percentile to be retrieved
#' @return Numeric vector with the xth percentile of the firing rate maps
#'
#' @docType methods
#' @rdname percentileFiringRateMaps-methods
setGeneric(name="percentileFiringRateMaps",
def=function(sp,percentile)
{standardGeneric("percentileFiringRateMaps")}
)
#' @rdname percentileFiringRateMaps-methods
#' @aliases percentileFiringRateMaps,ANY,ANY-method
setMethod(f="percentileFiringRateMaps",
signature="SpatialProperties2d",
definition=function(sp,percentile=75)
{
if(length(sp@maps)==0)
stop("percentileFiringRateMaps: length(sp@map)==0")
if(percentile<0|percentile>100)
stop("percentileFiringRateMaps: percentile is out of 0-100 range")
sp@maps[which(sp@maps==-1.0)]<-NA
return(apply(sp@maps,3,quantile,na.rm=T,probs=percentile/100))
})
#' Detect firing fields in the firing rate maps of a SpatialProperties2d object
#'
#' @param sp SpatialProperties2d object
#' @param minAreaCm2 Minimal area of the field in cm squared
#' @param rateThresholds Numeric vector containing the firing rate threshold to be part of a firing field.
#' Should have the same length than the number of cells in the SpatialProperties2d object
#' @return matrix with the field information (clu, xcom, ycom, peak.rate, area in cm^2)
#'
#' @docType methods
#' @rdname detectFiringFields-methods
setGeneric(name="detectFiringFields",
def=function(sp,minAreaCm2,rateThresholds)
{standardGeneric("detectFiringFields")}
)
#' @rdname detectFiringFields-methods
#' @aliases detectFiringFields,ANY,ANY-method
setMethod(f="detectFiringFields",
signature="SpatialProperties2d",
definition=function(sp,minAreaCm2=20,rateThresholds)
{
if(length(sp@maps)==0)
stop("detectFiringFields: length(sp@map)==0")
if(minAreaCm2<0)
stop("detectFiringFields: minAreaCm2<0")
if(length(rateThresholds)!=length(sp@cellList))
stop("detectFiringFields: length(rateThresholds)!=length(sp@cellList)")
results<-t(.Call("detect_firing_fields_cwrap",
as.numeric(sp@maps),
sp@nRowMap,
sp@nColMap,
length(sp@cellList),
as.integer(sp@cellList),
as.integer(minAreaCm2/(sp@cmPerBin^2)),
rateThresholds))
colnames(results)<-c("clu","xcom","ycom","peak.rate","area")
results[,"area"]<-results[,"area"]*sp@cmPerBin*sp@cmPerBin
results[,"xcom"]<-results[,"xcom"]*sp@cmPerBin
results[,"ycom"]<-results[,"ycom"]*sp@cmPerBin
return(results)
})
#' Get the spike distance metric of the spikes
#'
#' Score based on Hardcastle et al. 2015 Neuron
#'
#'
#' @param sp SpatialProperties2d object with valid firing rate maps
#' @param st SpikeTime object
#' @param pt Positrack object
#' @param startIntervals Numeric with time in sample values for time 0
#' @param endIntervals Numeric with time in sample values for end of intervals
#' @param percentileField Numeric with the percentile of bins in the maps to act as a threshold to be part of a field
#' @return Matrix with clu.id, interval.no, time.sec, distance.from.field.center
#'
#' @docType methods
#' @rdname spikeDistanceMetric-methods
setGeneric(name="spikeDistanceMetric",
def=function(sp,st,pt,startIntervals,endIntervals,percentileField=75)
{standardGeneric("spikeDistanceMetric")}
)
#' @rdname spikeDistanceMetric-methods
#' @aliases spikeDistanceMetric,ANY,ANY-method
setMethod(f="spikeDistanceMetric",
signature="SpatialProperties2d",
definition=function(sp,st,pt,startIntervals,endIntervals,percentileField=75)
{
if(length(sp@maps)==0)
stop("spikeDistanceMetric: length(sp@map)==0")
if(length(startIntervals)!=length(endIntervals))
stop("spikeDistanceMetric: length(startIntervals)!=length(endIntervals)")
if(any(startIntervals>=endIntervals))
stop("spikeDistanceMetric: startIntervals>=endIntervals")
## get the threshold for fields
fieldThresholds<-percentileFiringRateMaps(sp,percentileField)
## get the firing fields and their xcom and ycom
fields<-detectFiringFields(sp,minAreaCm2 = 20,rateThresholds = fieldThresholds)
if(any(!st@cellList %in% unique(fields[,"clu"]))){
print(paste("spikeDistanceMetric, a cell has no field",sp@session))
print(paste("old cell list:",st@cellList))
st@cellList<-st@cellList[st@cellList %in% unique(fields[,"clu"])]
print(paste("new cell list:",st@cellList))
if(length(st@cellList)==0)
{
return(NA)
}
}
## reduce the size of maps and map autocorrelation
if(sp@reduceSize==T){
x<-pt@x-min(pt@x,na.rm=T)+sp@cmPerBin
y<-pt@y-min(pt@y,na.rm=T)+sp@cmPerBin
}else{
x<-pt@x
y<-pt@y
}
## use -1 as invalid values in c functions
x[is.na(x)]<- -1.0
y[is.na(y)]<- -1.0
## get spike position
results<-.Call("spike_position_cwrap",
x,
y,
length(x),
as.integer(st@res),
as.integer(st@nSpikes),
as.integer(pt@resSamplesPerWhlSample),
as.integer(startIntervals),
as.integer(endIntervals),
length(startIntervals))
sp@xSpikes<-results[1,]
sp@ySpikes<-results[2,]
if(length(sp@xSpikes)!=length(st@res))
stop("spikeDistanceMetric, length(xSpikes)!=length(st@res)")
results<-t(.Call("spike_distance_metric_cwrap",
sp@xSpikes,
sp@ySpikes,
as.integer(st@res),
as.integer(st@clu),
as.integer(st@nSpikes),
as.integer(fields[,"clu"]),
fields[,"xcom"],
fields[,"ycom"],
length(fields[,1]),
as.integer(startIntervals),
as.integer(endIntervals),
length(startIntervals),
as.integer(st@cellList),
length(st@cellList)))
r<-data.frame(clu=results[,1],
trial=results[,2],
time=results[,3]/st@samplingRate, # in sec
distance=results[,4])
meanFieldArea<-as.numeric(by(fields[,5],list(fields[,1]),mean))
meanRadius<-sqrt(meanFieldArea/pi) ## the radius of a circle with same area
## divide distance from com by mean radius of the fields
x<-data.frame(clu=st@cellList,radius=meanRadius)
r<-merge(r,x)
r$sdm<-r$distance/r$radius
return(r)
})
#' Calculate the crosscorrelation between the firing rate maps of the SpatialProperties2d object
#'
#' A list of cell pair is generated from the cellList slot.
#' For each pair, the spatial crosscorrelation between the firing rate maps is calculated
#' The results is saved in the ccMaps slot of the SpatialProperties2d object.
#' The cellPairList slot gives you the cluster associated with each crosscorrelation maps
#'
#' @param sp SpatialProperties2d object
#' @param radiusCm radius in cm to keep in the matrix
#' @param removeEmptyRowCol logical indicating whether to remove the empty rows and colums in the crosscorrelation
#' @return SpatialProperties2d object with the spatial crosscorrelation in ccMaps and the cell pairs in cellPairList
#'
#' @docType methods
#' @rdname mapSpatialCrosscorrelation-methods
setGeneric(name="mapSpatialCrosscorrelation",
def=function(sp,radiusCm=30,removeEmptyRowCol=TRUE)
{standardGeneric("mapSpatialCrosscorrelation")}
)
#' @rdname mapSpatialCrosscorrelation-methods
#' @aliases mapSpatialCrosscorrelation,ANY,ANY-method
setMethod(f="mapSpatialCrosscorrelation",
signature="SpatialProperties2d",
definition=function(sp,radiusCm=30,removeEmptyRowCol=TRUE)
{
if(length(sp@maps)==0)
stop("Need to call firingRateMap2d first to run mapSpatialCrosscorrelation")
sp@cellPairList<-as.matrix(makePairs(sp@cellList,sp@cellList))
colnames(sp@cellPairList)<-c("clu1","clu2")
sp@nColCross = as.integer((sp@nColMap*2)+1)
sp@nRowCross = as.integer((sp@nRowMap*2)+1)
results<- .Call("map_spatial_crosscorrelation_cwrap",
length(sp@cellList),
as.integer(sp@cellList),
length(sp@cellPairList[,1]),
as.integer(sp@cellPairList[,1]),
as.integer(sp@cellPairList[,2]),
as.numeric(sp@maps),
sp@nRowMap,
sp@nColMap,
sp@nRowCross,
sp@nColCross,
as.integer(sp@minValidBinsCross))
sp@ccMaps<-array(data=results,dim=(c(sp@nRowCross,sp@nColCross,length(sp@cellPairList[,1]))))
## now set to -2 the values that are further away from the center than radiusCm
x<-matrix(data=rep(1:sp@nColCross,each=sp@nRowCross),nrow =sp@nRowCross,ncol=sp@nColCross)
y<-matrix(data=rep(1:sp@nRowCross,sp@nColCross),nrow=sp@nRowCross,ncol=sp@nColCross)
centerX<-ceiling(sp@nColCross/2)
centerY<-ceiling(sp@nRowCross/2)
d<-sqrt((x-centerX)*(x-centerX)+(y-centerY)*(y-centerY))*sp@cmPerBin
## function to remove what is too distant
f<-function(m,d,radiusCm){m[which(d>radiusCm)]<- -2.0
return(m)
}
a<-aperm(plyr::aaply(sp@ccMaps,3,f,d,radiusCm))
if(removeEmptyRowCol){
sp@ccMaps<-a[apply(a,1,function(x){any(x!=-2)}),
apply(a,2,function(x){any(x!=-2)}),]
sp@nColCross = dim(sp@ccMaps)[2]
sp@nRowCross = dim(sp@ccMaps)[1]
}
return(sp)
}
)
#' Calculate a 3D occupancy matrix with x and y postion and head-direction.
#' It uses the data from a Positrack objects
#'
#' The occupancy3D array is not smoothed.
#' All data are kept even if very little time was spent in one bin.
#' Unvisited x-y-hd bins are set to 0
#'
#' @param sp SpatialProperties2d object
#' @param st SpikeTrain object to get the intervals
#' @param pt Positrack object
#' @param nRowMap Numeric indicating the number of rows in the map. By default the maps has the smallest size possible given the
#' position data in the Positrack object. Use this argument to have maps of a fixed size. Needs to be as large as the minimal size of the map
#' @param nColMap Numeric indicating the number of columns in the map. By default the maps has the smallest size possible given the
#' position data in the Positrack object. Use this argument to have maps of a fixed size. Needs to be as large as the minimal size of the map
#' @param degPerBin Give the number of degrees per bin for the head-direction data
#' @return SpatialProperties2d object with occupancy3D array, the dimensions are x, y and hd.
#'
#' @docType methods
#' @rdname occupancyMap3d-methods
setGeneric(name="occupancyMap3d",
def=function(sp,st,pt,nRowMap=NA,nColMap=NA,degPerBin=10)
{standardGeneric("occupancyMap3d")}
)
#' @rdname occupancyMap3d-methods
#' @aliases occupancyMap3d,ANY,ANY-method
setMethod(f="occupancyMap3d",
signature="SpatialProperties2d",
definition=function(sp,st,pt,nRowMap=NA,nColMap=NA,degPerBin=10)
{
if(length(pt@x)==0)
stop(paste("pt@x has length of 0 in occupancyMap2d",st@session))
if(length(st@startInterval)==0)
stop(paste("length of st@startInterval == 0 in occupancyMap2d",st@session))
if(!is.na(nRowMap)|!is.na(nColMap)){
if(is.na(nRowMap))
stop("if you set nColMap, you need to also set nRowMap")
if(is.na(nColMap))
stop("if you set nRowMap, you need to also set nColMap")
}
if(degPerBin<=0)
stop("degPerBin should be larger than 0")
sp@cellList<-st@cellList
## reduce the size of maps and map autocorrelation
if(sp@reduceSize==T){
x<-pt@x-min(pt@x,na.rm=T)+sp@cmPerBin
y<-pt@y-min(pt@y,na.rm=T)+sp@cmPerBin
}else{
x<-pt@x
y<-pt@y
}
hd<-pt@hd
## use -1 as invalid values in c functions
x[is.na(x)]<- -1.0
y[is.na(y)]<- -1.0
hd[is.na(hd)]<- -1.0
## get the dimensions of the map
sp@nRowMap=as.integer(((max(x)+1)/sp@cmPerBin)+1) # x in R is a row
sp@nColMap=as.integer(((max(y)+1)/sp@cmPerBin)+1) # y in R is a col
## user want a map of a given size
if(!is.na(nRowMap)){
if(nRowMap<sp@nRowMap)
stop(paste("nRowMap value",nRowMap,"is smaller than the minimal size of the map",sp@nRowMap))
if(nColMap<sp@nColMap)
stop(paste("nColMap value",nColMap,"is smaller than the minimal size of the map",sp@nColMap))
sp@nRowMap=as.integer(nRowMap)
sp@nColMap=as.integer(nColMap)
}
nHdBins=as.integer(ceiling(360/degPerBin))
## make the occupancy map
res<-.Call("occupancy3D_cwrap",
sp@nRowMap,
sp@nColMap,
nHdBins,
sp@cmPerBin,
sp@cmPerBin,
degPerBin,
x,
y,
hd,
length(x),
pt@resSamplesPerWhlSample/pt@samplingRateDat*1000, ## ms per whl samples
as.integer(st@startInterval),
as.integer(st@endInterval),
length(st@startInterval),
as.integer(pt@resSamplesPerWhlSample))
# change vector to array and transpose so that it dimensions are [x,y,hd]
sp@occupancy3D<-aperm(array(res,dim = c(nHdBins,sp@nColMap,sp@nRowMap)),c(3,2,1))
return(sp)
}
)
#' Calculate a predicted HD tuning curve assuming the null hypothesis that a cell is only modulated by location.
#'
#' The method assumes that the only influence of head direction arises from a sampling bias of different directions in some location of the maze
#' This hypothesis is called the distributive hypothesis and was introduced by
#' Muller, Bostock, Taube and Kubie (1994) On the directional firing properties of hippocampal place cells. J Neurosci.
#' and used by
#' Cacucci, Lever, Wills, Burgess and O'Keefe (2004) Theta-modulated place-by-direction cells in the hippocampal formation in the rat. J Neurosci
#' This function uses formula provided by Cacucci et al.
#'
#' @param sp SpatialProperties2d object
#' @param st SpikeTrain object to get the intervals
#' @param pt Positrack object
#' @param hd HeadDirection object
#' @param nRowMap Numeric indicating the number of rows in the map. By default the maps has the smallest size possible given the
#' position data in the Positrack object. Use this argument to have maps of a fixed size. Needs to be as large as the minimal size of the map
#' @param nColMap Numeric indicating the number of columns in the map. By default the maps has the smallest size possible given the
#' position data in the Positrack object. Use this argument to have maps of a fixed size. Needs to be as large as the minimal size of the map
#' @return Array with the predicted HD tuning curves
#'
#' @docType methods
#' @rdname distributiveHypothesisHdHisto-methods
setGeneric(name="distributiveHypothesisHdHisto",
def=function(sp,st,pt,hd,nRowMap=NA,nColMap=NA)
{standardGeneric("distributiveHypothesisHdHisto")}
)
#' @rdname distributiveHypothesisHdHisto-methods
#' @aliases distributiveHypothesisHdHisto,ANY,ANY-method
setMethod(f="distributiveHypothesisHdHisto",
signature="SpatialProperties2d",
definition=function(sp,st,pt,hd,nRowMap=NA,nColMap=NA)
{
if(length(pt@x)==0)
stop(paste("pt@x has length of 0 in occupancyMap2d",st@session))
if(length(pt@x)==0)
stop(paste("pt@x has length of 0 in occupancyMap2d",st@session))
if(length(st@startInterval)==0)
stop(paste("length of st@startInterval == 0 in occupancyMap2d",st@session))
if(!is.na(nRowMap)|!is.na(nColMap)){
if(is.na(nRowMap))
stop("if you set nColMap, you need to also set nRowMap")
if(is.na(nColMap))
stop("if you set nRowMap, you need to also set nColMap")
}
if(hd@degPerBin<=0)
stop("hd@degPerBin should be larger than 0")
## get the occupancy3D array
sp<-occupancyMap3d(sp,st,pt,degPerBin=hd@degPerBin)
## get the firing rate map
sp<-firingRateMap2d(sp,st,pt)
if(dim(sp@occupancy3D)[1]!=dim(sp@maps)[1]|dim(sp@occupancy3D)[1]!=dim(sp@maps)[1])
stop("dimensions of occupancy3D are not the same as maps dimensions")
## in sp@occupancy3D, unvisited bins are set to 0.
## these bins have no influence on the distributedHypothesis tuning curves as we multiply by 0
#function to run on one map
distributedHypothesisHdHistoOneCell<-function(map,occ3D){
apply(sp@occupancy3D,3,function(occ,map){sum(map*occ)/sum(occ)},map)## apply the function to all hd bins
}
## for each firing rate map, get the tuning curve
tc<-apply(sp@maps,3,distributedHypothesisHdHistoOneCell,sp@occupancy3D)
return(tc)
})
#' Calculate a directional distributive ratio (DR) to test the null hypothesis that a cell is only modulated by location
#' and any apparent HD selectivity is due to spatial selectivity and biased directional sampling
#'
#' The method assumes that the apparent influence of head direction arises from a sampling bias of different directions in some location of the maze .
#' This hypothesis is called the distributive hypothesis and was introduced by
#' Muller, Bostock, Taube and Kubie (1994) On the directional firing properties of hippocampal place cells. J Neurosci.
#' and used by
#' Cacucci, Lever, Wills, Burgess and O'Keefe (2004) Theta-modulated place-by-direction cells in the hippocampal formation in the rat. J Neurosci
#' This function uses formula provided by Cacucci et al.
#'
#' @param sp SpatialProperties2d object
#' @param st SpikeTrain object to get the intervals
#' @param pt Positrack object
#' @param hd HeadDirection object
#' @param nRowMap Numeric indicating the number of rows in the map. By default the maps has the smallest size possible given the
#' position data in the Positrack object. Use this argument to have maps of a fixed size. Needs to be as large as the minimal size of the map
#' @param nColMap Numeric indicating the number of columns in the map. By default the maps has the smallest size possible given the
#' position data in the Positrack object. Use this argument to have maps of a fixed size. Needs to be as large as the minimal size of the map
#' @return SpatialProperties2d object with occupancy3D array, the dimensions are x, y and hd.
#'
#' @docType methods
#' @rdname directionalDistributiveRatioFromHdHisto-methods
setGeneric(name="directionalDistributiveRatioFromHdHisto",
def=function(sp,st,pt,hd,nRowMap=NA,nColMap=NA)
{standardGeneric("directionalDistributiveRatioFromHdHisto")}
)
#' @rdname directionalDistributiveRatioFromHdHisto-methods
#' @aliases directionalDistributiveRatioFromHdHisto,ANY,ANY-method
setMethod(f="directionalDistributiveRatioFromHdHisto",
signature="SpatialProperties2d",
definition=function(sp,st,pt,hd,nRowMap=NA,nColMap=NA)
{
dtc<-distributiveHypothesisHdHisto(sp,st,pt,hd,nRowMap,nColMap) # predicted histo
hd<-headDirectionHisto(hd,st,pt) # observed histo
if(!all(dim(dtc)==dim(hd@histo)))
stop("Problem with the dimensions of distributive HD histo and HD histo")
## merge the observed and predicted histograms in an array, to use apply
a<-array(c(hd@histo,dtc),dim=c(dim(dtc),2))
ratio<-function(x){ # x is a 2d array hd,obs/pred
obs<-as.numeric(x[,1])# observed histo is linearized
pred<-as.numeric(x[,2]) # predicted histo is linearized
obs[which(obs==-1.0)]<-NA # change -1.0 in histo to NA
m<-matrix(c(obs,pred),nrow = length(obs)) # create a 2d matrix with 2 columns (obs,pred)
m<-m[apply(m,1,function(x)all(is.finite(x))),] # only keep rows with valid rate
sum(abs(log((1+m[,1])/(1+m[,2]))))/length(m[,1]) # calculate the ratio
}
## get the distributive ratio
DR<-apply(a,2,ratio)
return(DR)
})
#' Calculate a predicted firing rate map assuming the null hypothesis that a cell is only modulated by head direction.
#'
#' The method assumes that the influence of spatial selectivity arises from a sampling bias of different location in some directiontion
#' This hypothesis is called the distributive hypothesis and was introduced by
#' Muller, Bostock, Taube and Kubie (1994) On the directional firing properties of hippocampal place cells. J Neurosci.
#' and used by
#' Cacucci, Lever, Wills, Burgess and O'Keefe (2004) Theta-modulated place-by-direction cells in the hippocampal formation in the rat. J Neurosci
#' This function uses formula provided by Cacucci et al.
#'
#' @param sp SpatialProperties2d object
#' @param st SpikeTrain object to get the intervals
#' @param pt Positrack object
#' @param hd HeadDirection object
#' @param nRowMap Numeric indicating the number of rows in the map. By default the maps has the smallest size possible given the
#' position data in the Positrack object. Use this argument to have maps of a fixed size. Needs to be as large as the minimal size of the map
#' @param nColMap Numeric indicating the number of columns in the map. By default the maps has the smallest size possible given the
#' position data in the Positrack object. Use this argument to have maps of a fixed size. Needs to be as large as the minimal size of the map
#' @return Array with the predicted HD tuning curves
#'
#' @docType methods
#' @rdname distributiveHypothesisMap-methods
setGeneric(name="distributiveHypothesisMap",
def=function(sp,st,pt,hd,nRowMap=NA,nColMap=NA)
{standardGeneric("distributiveHypothesisMap")}
)
#' @rdname distributiveHypothesisMap-methods
#' @aliases distributiveHypothesisMap,ANY,ANY-method
setMethod(f="distributiveHypothesisMap",
signature="SpatialProperties2d",
definition=function(sp,st,pt,hd,nRowMap=NA,nColMap=NA)
{
if(length(pt@x)==0)
stop(paste("pt@x has length of 0 in occupancyMap2d",st@session))
if(length(pt@x)==0)
stop(paste("pt@x has length of 0 in occupancyMap2d",st@session))
if(length(st@startInterval)==0)
stop(paste("length of st@startInterval == 0 in occupancyMap2d",st@session))
if(!is.na(nRowMap)|!is.na(nColMap)){
if(is.na(nRowMap))
stop("if you set nColMap, you need to also set nRowMap")
if(is.na(nColMap))
stop("if you set nRowMap, you need to also set nColMap")
}
if(hd@degPerBin<=0)
stop("hd@degPerBin should be larger than 0")
## get the occupancy3D array
sp<-occupancyMap3d(sp,st,pt,degPerBin = hd@degPerBin)
## get the hd histogram
hd<-headDirectionHisto(hd,st,pt)
if(dim(sp@occupancy3D)[3]!=dim(hd@histo)[1])
stop("dimensions of occupancy3D are not the same as hd@histo")
## in sp@occupancy3D, unvisited bins are set to 0.
## these bins have no influence on the distributedHypothesis tuning curves as we multiply by 0
#function to predict the firing rate in each bin of the firing map based on hd tuning curve and hd occupancy data
distributedHypothesisMapOneCell<-function(hdHisto,occ3D){
apply(sp@occupancy3D,c(1,2),function(occ3D,hdHisto){sum(hdHisto*occ3D)/sum(occ3D)},hdHisto)## apply the function to all map bin
}
## for each hd histo, get a firing rate map of the predict firing rate
b<-apply(hd@histo,2,distributedHypothesisMapOneCell,sp@occupancy3D)
# somehow output is a matrix instead of array, need to convert to an array here
maps<-array(b,c(dim(sp@occupancy3D)[c(1,2)],dim(hd@histo)[2]))
return(maps)
})
#' Calculate a locational distributive ratio (DR) to test the null hypothesis that a cell is only modulated by head direction
#' and any apparent spatial selectivity is due to head direction tuning and biased spatial sampling
#'
#' The method assumes that the apparent influence of position arises from a sampling bias of spatial data for some head direction.
#' This hypothesis is called the distributive hypothesis and was introduced by
#' Muller, Bostock, Taube and Kubie (1994) On the directional firing properties of hippocampal place cells. J Neurosci.
#' and used by
#' Cacucci, Lever, Wills, Burgess and O'Keefe (2004) Theta-modulated place-by-direction cells in the hippocampal formation in the rat. J Neurosci
#' This function uses formula provided by Cacucci et al.
#'
#' @param sp SpatialProperties2d object
#' @param st SpikeTrain object to get the intervals
#' @param pt Positrack object
#' @param hd HeadDirection object
#' @param nRowMap Numeric indicating the number of rows in the map. By default the maps has the smallest size possible given the
#' position data in the Positrack object. Use this argument to have maps of a fixed size. Needs to be as large as the minimal size of the map
#' @param nColMap Numeric indicating the number of columns in the map. By default the maps has the smallest size possible given the
#' position data in the Positrack object. Use this argument to have maps of a fixed size. Needs to be as large as the minimal size of the map
#' @return SpatialProperties2d object with occupancy3D array, the dimensions are x, y and hd.
#'
#' @docType methods
#' @rdname locationalDistributiveRatioFromMap-methods
setGeneric(name="locationalDistributiveRatioFromMap",
def=function(sp,st,pt,hd,nRowMap=NA,nColMap=NA)
{standardGeneric("locationalDistributiveRatioFromMap")}
)
#' @rdname locationalDistributiveRatioFromMap-methods
#' @aliases locationalDistributiveRatioFromMap,ANY,ANY-method
setMethod(f="locationalDistributiveRatioFromMap",
signature="SpatialProperties2d",
definition=function(sp,st,pt,hd,nRowMap=NA,nColMap=NA)
{
dmap<-distributiveHypothesisMap(sp,st,pt,hd,nRowMap,nColMap) # predicted maps
sp<-firingRateMap2d(sp,st,pt,nRowMap,nColMap) # observed maps
if(!all(dim(dmap)==dim(sp@maps)))
stop("Problem with the dimensions of distributive maps and sp@maps")
## merge the observed and predicted histograms in an array, to use apply
a<-array(c(sp@maps,dmap),dim=c(dim(dmap),2))
ratio<-function(x){ # x is a 3d array x,y,obs/pred
obs<-as.numeric(x[,,1])# observed map is linearized
pred<-as.numeric(x[,,2]) # predicted map is linearized
obs[which(obs==-1.0)]<-NA # change -1.0 in map to NA
m<-matrix(c(obs,pred),nrow = length(obs)) # create a 2d matrix with 2 columns (obs,pred)
m<-m[apply(m,1,function(x)all(is.finite(x))),] # only keep rows with valid rate
sum(abs(log((1+m[,1])/(1+m[,2]))))/length(m[,1]) # calculate the ratio
}
## get the distributive ratio
DR<-apply(a,3,ratio)
return(DR)
})
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.