analysis_functions.R 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. ###1. Preparation of Data###
  2. ###1.1 Converting .csv to dataframe.
  3. #'This is a modified function of fread from data.table package by adjusting to the format of DeepLabCut files
  4. #'
  5. #'@param [file]: file path of .csv file from the DLC-analyzed video.
  6. #'@return: data frame, has to be assigned to a variable.
  7. read.dlc = function(file){
  8. Table = fread(file, skip=2, data.table = FALSE)
  9. col = colnames(fread(file, skip=1, data.table = FALSE))
  10. col[1] = "seq"
  11. for(i in 2:length(col)){
  12. if(i %in% seq(2, length(col), 3)){
  13. col[i] = paste(col[i], "_x", sep="")
  14. }
  15. else if(i %in% seq(3, length(col), 3)){
  16. col[i] = paste(col[i], "_y", sep="")
  17. }
  18. else if(i %in% seq(4, length(col), 3)){
  19. col[i] = paste(col[i], "_likeli", sep="")
  20. }
  21. }
  22. colnames(Table) = col
  23. return(Table)
  24. }
  25. ###1.2 Inverting y axis###
  26. #'Inverts the Y axis.
  27. #'
  28. #'@param [Table]: dataframe after application of read.dlc.
  29. #'@return: dataframe, has to be assigned to a variable.
  30. y_invert<-function(Table){
  31. for(i in seq(3, ncol(Table)-3, 3)){
  32. for(j in 1:nrow(Table)){
  33. Table[j,i]=-(Table[j,i]-Table$height[1])
  34. }
  35. }
  36. return(Table)
  37. }
  38. ###1.3 Deleting Low Likelihood###
  39. #'Deletes X and Y coordinates under a given threshold. X and Y coordinates of a data point under the desired threshold turn into NAs.
  40. #'
  41. #'@param [Table]: dataframe after application of read.dlc and y_invert.
  42. #'@param [threshold]: value of desired minimum likelihood.
  43. #'@return: dataframe, has to be assigned to a variable.
  44. lowlikeli = function(Table, threshold = .90){
  45. likelirows = grep("likeli", names(Table))
  46. for(i in likelirows){
  47. for(j in 1:nrow(Table)){
  48. if(Table[j,i] < threshold){
  49. Table[j,i-1]<-NA
  50. Table[j,i-2]<-NA
  51. }
  52. }
  53. }
  54. return(Table)
  55. }
  56. ###1.4 Adding Video Resolution###
  57. #'Adds three columns with the Height, the Width and the FPS of the Video for easy access
  58. #'
  59. #'@param [Table]: dataframe after application of read.dlc, y_invert and lowlikeli.
  60. #'@param [Height]: Height of the analyzed video.
  61. #'@param [Width]: Width of the analyzed video.
  62. #'@param [FPS]: Frames per second of the analyzed video.
  63. #'@return: dataframe, has to be assigned to a variable.
  64. addreso = function(Table, Height, Width, FPS){
  65. Table$height = Height
  66. Table$width = Width
  67. Table$fps = FPS
  68. return(Table)
  69. }
  70. ###1.5 Combines all preparation functions###
  71. #'Combines all preparation functions. First reads DLC file, inverts the Y axis, removes data points under a given threshold and adds the columns Height, Width and FPS.
  72. #'
  73. #'@param [Path]: file path of .csv file from the DLC-analyzed video.
  74. #'@param [FPS]: Frames per second of the analyzed video.
  75. #'@param [Height]: Height of the analyzed video.
  76. #'@param [Width]: Width of the analyzed video.
  77. #'@param [Threshold]: value of desired minimum likelihood.
  78. #'@return: dataframe, has to be assigned to a variable.
  79. prep.dlc<-function(Path, FPS, Height, Width, Threshold=.90){
  80. Table=read.dlc(Path)
  81. Table=addreso(Table, Height, Width, FPS)
  82. Table=y_invert(Table)
  83. Table=lowlikeli(Table, Threshold)
  84. print("data process succeeded!")
  85. return(Table)
  86. }
  87. ###2. Conditions###
  88. ###2.1 Beam Left###
  89. #'Ignores data points on the left side of the left beam landmark.
  90. #'
  91. #'@param [Table]: dataframe after preparation of data. For preparation of data go to point 1.
  92. #'@param [x]: X coordinate of a data point which will be checked.
  93. #'@return: 0 or 1. If 0, data point is on the right side of the left beam landmark. If 1, the data point is on the left side of the left beam landmark.
  94. beam_left<-function(Table, x){
  95. left=0
  96. if(x<median(Table$'beam_left_top_x', na.rm=T)){
  97. left=1
  98. }
  99. return(left)
  100. }
  101. ###2.2 Beam Right###
  102. #'Ignores data points on the right side of the right beam landmark.
  103. #'
  104. #'@param [Table]: dataframe after preparation of data. For preparation of data go to point 1.
  105. #'@param [x]: X coordinate of a data point which will be checked.
  106. #'@return: 0 or 1. If 0, data point is on the left side of the right beam landmark. If 1, the data point is on the right side of the right beam landmark.
  107. beam_right<-function(Table, x){
  108. right=0
  109. if(x>median(Table$'beam_right_top_x', na.rm=T)){
  110. right=1
  111. }
  112. return(right)
  113. }
  114. ###2.3 Beam Top###
  115. #'Gives the corresponding Y coordinate of the upper beam edge to a X coordinate of a data point.
  116. #'
  117. #'@param [Table]: dataframe after preparation of data. For preparation of data go to point 1.
  118. #'@param [x]: X coordinate of a data point which will be checked.
  119. #'@return: Y coordinate of the upper beam edge at the given X coordinate. Will be compared to the Y coordinate of the given X coordinate.
  120. beam_top<-function(Table, x){
  121. m = (median(Table$'beam_left_top_y', na.rm=T) - median(Table$'beam_right_top_y', na.rm=T))/
  122. (median(Table$'beam_left_top_x', na.rm=T) - median(Table$'beam_right_top_x', na.rm=T))
  123. b = median(Table$'beam_right_top_y', na.rm=T) - m*median(Table$'beam_right_top_x', na.rm=T)
  124. beam_y=m*x+b
  125. return(beam_y)
  126. }
  127. ###2.4 Beam Bottom###
  128. #'Gives the corresponding Y coordinate of the lower beam edge to a X coordinate of a data point.
  129. #'
  130. #'@param [Table]: dataframe after preparation of data. For preparation of data go to point 1.
  131. #'@param [x]: X coordinate of a data point which will be checked.
  132. #'@return: Y coordinate of the lower beam edge at the given X coordinate. Will be compared to the Y coordinate of the given X coordinate.
  133. beam_bottom<-function(Table, x){
  134. m = (median(Table$'beam_left_bottom_y', na.rm=T) - median(Table$'beam_right_bottom_y', na.rm=T))/
  135. (median(Table$'beam_left_bottom_x', na.rm=T) - median(Table$'beam_right_bottom_x', na.rm=T))
  136. b = median(Table$'beam_right_bottom_y', na.rm=T) - m*median(Table$'beam_right_bottom_x', na.rm=T)
  137. beam_y=m*x+b
  138. return(beam_y)
  139. }
  140. ###3. Evaluation###
  141. ###3.1 Start###
  142. #'Determines the start point of the evaluation. Start point is set to the first time point, when both forepaw and hindpaw are close to the upper beam edge.
  143. #'
  144. #'@param [Table]: dataframe after preparation of data and the conditions. For preparation of data go to point 1. For conditions go to point 2.
  145. #'@return: frame number of the start of the evaluation.
  146. start<-function(Table){
  147. for(i in 1:nrow(Table)){
  148. if(is.na(Table$forepaw_right_x[i])==F && is.na(Table$forepaw_right_y[i])==F && is.na(Table$hindpaw_right_x[i])==F && is.na(Table$hindpaw_right_y[i])==F && Table$forepaw_right_y[i]<(beam_top(Table, Table$forepaw_right_x[i]+(Table$height[1]*0.01))) && Table$hindpaw_right_y[i]<(beam_top(Table, Table$hindpaw_right_x[i]+(Table$height[1]*0.01)))){
  149. return(Table$seq[i])
  150. break()
  151. }
  152. }
  153. }
  154. ###3.2 End###
  155. ###3.2.1 Turn###
  156. #'Determines whether the mouse turns around on the beam at a given X coordinate.
  157. #'
  158. #'@param [Table]: dataframe after preparation of data and the conditions. For preparation of data go to point 1. For conditions go to point 2.
  159. #'@param [x]: X coordinate of a data point which will be checked.
  160. #'@return: 0 or frame index of X coordinate. If 0 the mouse don't face in the wrong direction at the given X coordinate.
  161. turn<-function(Table, x){
  162. turn=0
  163. if(is.na(Table$snout_x[x]) || is.na(Table$tailbase_x[x])){
  164. return(turn)
  165. }
  166. if(Table$snout_x[x]<Table$tailbase_x[x]){
  167. turn=Table$seq[x]
  168. }
  169. return(turn)
  170. }
  171. ###3.2.2 Drop###
  172. #'Determines whether the mouse dropped of the beam at a given X coordinate.
  173. #'
  174. #'@param [Table]: dataframe after preparation of data and the conditions. For preparation of data go to point 1. For conditions go to point 2.
  175. #'@param [x]: X coordinate of a data point which will be checked.
  176. #'@return: 0 or frame index of X coordinate. If 0, the mouse is still on the beam at the given X coordinate.
  177. drop<-function(Table, x){
  178. drop=0
  179. if(is.na(Table$snout_y[x]) || is.na(Table$tailbase_y[x])){
  180. return(drop)
  181. }
  182. if(Table$snout_y[x]<(beam_bottom(Table, Table$snout_x[x])-Table$height[1]*0.01) && Table$tailbase_y[x]<(beam_bottom(Table, Table$tailbase_x[x])-Table$height[1]*0.01)){
  183. drop=Table$seq[x]
  184. }
  185. return(drop)
  186. }
  187. ###3.2.3 End of Beam###
  188. #'Determines whether to mouse reached the last 5 centimeter of the beam at a given X coordinate.
  189. #'
  190. #'@param [Table]: dataframe after preparation of data and the conditions. For preparation of data go to point 1. For conditions go to point 2.
  191. #'@param [x]: Frame number of a data point which will be checked.
  192. #'@return: 0 or frame index of X coordinate. If 0, the mouse isn't at the end of the beam at the given X coordinate.
  193. #'
  194. end_beam<-function(Table, x){
  195. end_beam=0
  196. if(is.na(Table$forepaw_right_x[x])){
  197. return(end_beam)
  198. }
  199. if(distance(Table, Table$forepaw_right_x[x])>115){
  200. end_beam=Table$seq[x]
  201. }
  202. return(end_beam)
  203. }
  204. ###3.2.4 Stop###
  205. #'Determines whether to mouse stops while running on the beam at a given X coordinate.
  206. #'
  207. #'@param [Table]: dataframe after preparation of data and the conditions. For preparation of data go to point 1. For conditions go to point 2.
  208. #'@param [x]: X coordinate of a data point which will be checked.
  209. #'@return: 0 or frame index of X coordinate. If 0, the mouse isn't stopping at the given X coordinate.
  210. stop<-function(Table, x){
  211. stop=0
  212. if(is.na(Table$forepaw_right_x[x]) || is.na(Table$hindpaw_right_x[x])){
  213. return(stop)
  214. }
  215. if(Table$seq[x]>start(Table) && Table$seq[i]>Table$fps[1] && Table$forepaw_right_x[x]<(Table$forepaw_right_x[x-Table$fps[1]]+Table$width[1]*0.05) && Table$hindpaw_right_x[x]<(Table$hindpaw_right_x[x-Table$fps[1]]+Table$width[1]*0.05)){
  216. stop=Table$seq[x]
  217. }
  218. return(stop)
  219. }
  220. ###3.2.5 End###
  221. #'Determines the end point of the evaluation. End point is set when the mouse either turns, drops, stops, reaches the end of the beam or 60 seconds past.
  222. #'
  223. #'@param [Table]: dataframe after preparation of data, the conditions and determination of the start point. For preparation of data go to point 1. For conditions go to point 2. For determination of start point go to point 3.1.
  224. #'@return: frame index of the end of the evaluation.
  225. end<-function(Table){
  226. for(i in 1:nrow(Table)){
  227. if(Table$seq[i]<start(Table)){
  228. next()
  229. }
  230. if(turn(Table, i)!=0){
  231. return(Table$seq[i])
  232. break()
  233. }
  234. if(drop(Table, i)!=0){
  235. return(Table$seq[i])
  236. break()
  237. }
  238. if(end_beam(Table, i)!=0){
  239. return(Table$seq[i])
  240. break()
  241. }
  242. if(stop(Table, i)!=0){
  243. return(Table$seq[i])
  244. break()
  245. }
  246. if(Table$seq[i]>=(Table$fps[1]*60)){
  247. return(Table$seq[i])
  248. break()
  249. }
  250. }
  251. }
  252. rfend<-function(Table){
  253. for(i in 1:nrow(Table)){
  254. if(Table$seq[i]<start(Table)){
  255. next()
  256. }
  257. if(turn(Table, i)!=0){
  258. return("turn")
  259. break()
  260. }
  261. if(drop(Table, i)!=0){
  262. return("drop")
  263. break()
  264. }
  265. if(end_beam(Table, i)!=0){
  266. return("end of beam")
  267. break()
  268. }
  269. if(stop(Table, i)!=0){
  270. return("stop")
  271. break()
  272. }
  273. if(Table$seq[i]>=(Table$fps[1]*60)){
  274. return("time over")
  275. break()
  276. }
  277. }
  278. }
  279. ###3.3 Speed###
  280. ###3.3.1 Distance###
  281. #'Calculates the traveled distance of the mouse at a given X coordinate.
  282. #'
  283. #'@param [Table]: dataframe after preparation of data and the conditions. For preparation of data go to point 1. For conditions go to point 2.
  284. #'@param [x]: X coordinate of a data point which will be checked.
  285. #'@return: traveled distance of the mouse at the given X coordinate in centimeter [cm]. Minimum 0 cm, Maximum 120 cm.
  286. distance<-function(Table, x){
  287. y<-((120/(median(Table$'beam_right_top_x', na.rm=T)-median(Table$'beam_left_top_x', na.rm=T)))*(x-median(Table$'beam_left_top_x', na.rm=T)))
  288. if(y>115){
  289. y=120
  290. }
  291. return(y)
  292. }
  293. ###3.3.2 Time###
  294. #'Determines the time from the start till the end of the evaluation.
  295. #'
  296. #'@param [Table]: dataframe after preparation of data and the conditions. For preparation of data go to point 1. For conditions go to point 2.
  297. #'@param [start]: either frame number of the start of the evaluation or function start with @param [Table], see 3.1.
  298. #'@param [end]: either frame number of the end of the evaluation or function end with @param [Table], see 3.2.5.
  299. #'@param [fps]: Frames per second of the analyzed video.
  300. #'@return: time of evaluation in seconds [s].
  301. time<-function(Table, start, end){
  302. time=(end-start)/Table$fps[1]
  303. return(time)
  304. }
  305. ###3.3.2 Speed###
  306. #'Determines the speed of the mouse during the evaluation.
  307. #'
  308. #'@param [Table]: dataframe after preparation of data, the conditions and determination of the start and the end point of the evaluation. For preparation of data go to point 1. For conditions go to point 2.
  309. #'@param [end]: either frame number of the end of the evaluation or function end with @param [Table], see 3.2.5.
  310. #'@param [time]: either time in seconds or function time with @param [Table], @param [start], @param [end] and @param [fps], see 3.3.2.
  311. #'@return: speed of the mouse during the evaluation in centimeter per second [cm/s].
  312. speed<-function(Table, end, time){
  313. speed=distance(Table, Table$forepaw_right_x[end+1])/time
  314. return(speed)
  315. }
  316. ###3.4 Hindlimb Drop###
  317. #'Detects right and left hindlimb drops at a given X coordinate.
  318. #'
  319. #'@param [Table]: dataframe after preparation of data and the conditions. For preparation of data got to point 1. For conditions go to point 2.
  320. #'@param [x]: X coordinate of the data point which will be checked.
  321. #'@return: frame number of hindlimb drop
  322. hdrop<-function(Table, Vec){
  323. for(i in 1:nrow(Table)){
  324. redropcount=0
  325. if(length(Vec)>0){
  326. for(j in (Vec[length(Vec)]+1):i){
  327. if(is.na(Table$hindpaw_right_x[j])==F && is.na(Table$hindpaw_right_y[j])==F && is.na(Table$hindpaw_left_x[j])==F && is.na(Table$hindpaw_left_y[j])==F && Table$hindpaw_right_y[j]>beam_bottom(Table, Table$hindpaw_right_x[j]) && Table$hindpaw_left_y[j]>beam_bottom(Table, Table$hindpaw_left_x[j])){
  328. redropcount=redropcount+1
  329. }
  330. }
  331. }
  332. if(length(Vec)>0 && redropcount==0){
  333. next()
  334. }
  335. if(is.na(Table$hindpaw_right_x[i])==F && is.na(Table$hindpaw_right_y[i])==F && Table$hindpaw_right_y[i]<beam_bottom(Table, Table$hindpaw_right_x[i]) && drop(Table, i)==0){
  336. Vec=cbind(Vec,Table$seq[i])
  337. }
  338. if(is.na(Table$hindpaw_left_x[i])==F && is.na(Table$hindpaw_left_y[i])==F && Table$hindpaw_left_y[i]<beam_bottom(Table, Table$hindpaw_left_x[i]) && drop(Table, i)==0){
  339. Vec=cbind(Vec,Table$seq[i])
  340. }
  341. }
  342. return(Vec)
  343. }
  344. ###4. Further Analysis###
  345. ###4.1 Angle###
  346. #'Calculates the angle between three bodyparts for all frames.
  347. #'
  348. #'@param [Table]: dataframe after preparation of data and the conditions. For preparation of data go to point 1. For conditions go to point 2.
  349. #'@param [Ray1, Vertex, Ray2]: columnnames of the desired bodyparts with quotation marks, e.g. angle(table, "snout", "neck", "tailbase").
  350. #'@param [Vertex]: chosen bodypart indicates the center (or vertex) of the calculated angles.
  351. #'@param [Ray1, Ray2]: chosen bodyparts indicate the outside points of the calculated angles.
  352. #'@return: dataframe with all angles between the given bodyparts, has to be assigned to a variable.
  353. angle<-function(Table, Ray1, Vertex, Ray2){
  354. vec=data.frame()
  355. grepRay1=grep(Ray1, names(Table))
  356. grepRay2=grep(Ray2, names(Table))
  357. grepVertex=grep(Vertex, names(Table))
  358. for(i in 1:nrow(Table)){
  359. if(is.na(Table[i, grepRay1[1]]) || is.na(Table[i, grepRay2[1]]) || is.na(Table[i, grepVertex[1]]) || is.na(Table[i, grepRay1[2]]) || is.na(Table[i, grepRay2[2]]) || is.na(Table[i, grepVertex[2]])){
  360. next()
  361. }
  362. else{
  363. R1<-c(Table[i, grepRay1[1]], Table[i, grepRay1[2]])
  364. R2<-c(Table[i, grepRay2[1]], Table[i, grepRay2[2]])
  365. V<-c(Table[i, grepVertex[1]], Table[i, grepVertex[2]])
  366. VR1=R1-V
  367. VR2=R2-V
  368. skalar=sum(VR1*VR2)
  369. BVR1=VR1[1]^2+VR1[2]^2
  370. BVR2=VR2[1]^2+VR2[2]^2
  371. rad<-acos(skalar/(sqrt(BVR1)*sqrt(BVR2)))
  372. deg<-180*rad/pi
  373. vec[Table$seq[i],1]=deg
  374. }
  375. }
  376. return(vec)
  377. }
  378. singleangle<-function(Table, j, Ray1, Vertex, Ray2){
  379. grepRay1=grep(Ray1, names(Table))
  380. grepRay2=grep(Ray2, names(Table))
  381. grepVertex=grep(Vertex, names(Table))
  382. if(is.na(Table[j, grepRay1[1]]) || is.na(Table[j, grepRay2[1]]) || is.na(Table[j, grepVertex[1]]) || is.na(Table[j, grepRay1[2]]) || is.na(Table[j, grepRay2[2]]) || is.na(Table[j, grepVertex[2]])){
  383. return(NA)
  384. }
  385. R1<-c(Table[j, grepRay1[1]], Table[j, grepRay1[2]])
  386. R2<-c(Table[j, grepRay2[1]], Table[j, grepRay2[2]])
  387. V<-c(Table[j, grepVertex[1]], Table[j, grepVertex[2]])
  388. VR1=R1-V
  389. VR2=R2-V
  390. skalar=sum(VR1*VR2)
  391. BVR1=VR1[1]^2+VR1[2]^2
  392. BVR2=VR2[1]^2+VR2[2]^2
  393. rad<-acos(skalar/(sqrt(BVR1)*sqrt(BVR2)))
  394. deg<-180*rad/pi
  395. return(deg)
  396. }