README for Kagaya_Patek_2015JEB_dataset_for_dryad.rdata To load the dataset and functions in R environment used for the analyses in the paper, "Feed-forward motor control of ultrafast, ballistic movements" by Katsushi Kagaya and S. N. Patek, type > load("Kagaya_Patek_2015JEB_dataset_for_dryad.rdata ") then all dataset and R functions required for the analysis will be loaded to your work space. Dataset The data are contained as R objects named for each animal ID. The IDs are, “nb18, nb30, nb500, nb504, nb520, nb55”. Each object is a nested object. If you want to see the name of sub-contents, type > names(nb30) [1] "phys" "spike.df" "contraction" "strike" "fps.contraction" "fps.strike" [8] "strike.type" "prop.len" "gender" "mmperpix" "merus.len" "id" then the names for the sub-contents will be returned. The sub-contents are summarized in the table below. names phys spike.df contraction strike fps.contraction fps.strike id strike.type prop.len gender mmperpix merus.len sub-contents EMG data spike timing data digitized data of spring compression digitized data of strike movement frames per second for contraction frames per second for strike animal ID single strike or double strike (This information is not used in the current study.) propodus length gender of the animal mm per pixel value for the high speed images merus length Some of these sub-contents are further nested. For example, if you want strike kinematics data of the propodus rotation of the first strike of nb18, type > first.strike.p.kinematics.nb18 <- nb18$strike$output.propodus.rotation[[1]] then you can store the data in the object “first.strike.p.kinematics.nb18”. On the other hand, if you want strike kinematics data of the meral-V rotation of the second strike of nb30, type > second.strike.m.kinematics.nb30 <- nb30$strike$output.meralV.rotation[[2]] then you can store the data in the object “second.strike.m.kinematics.nb30”. The results in the paper can be reproduced by using the functions introduced in the next section. Functions The functions are written in three separate scripts; these are available from three URLs and are also included in this Dryad depository. script name Kagaya_Patek_kinem_control_emg.R Kagaya_Patek_kinem_control_hsv.R Kagaya_Patek_kinem_control_stat.R URL https://gist.github.com/kagaya/36145a370a1da0f4ba0d https://gist.github.com/kagaya/540596f6d1191c157cb7 https://gist.github.com/kagaya/cbafe0a332e6d6766168 For example, to reproduce the analyses in the study, type > source(“Kagaya_Patek_kinem_control_stat.R”) then you are ready for the analyses and visualization using the functions summarized in the three tables. 1. Kagaya_Patek_kinem_control_emg.R Functions for preprocessing and spike detection from EMG data. function name function read.phys read the physiological data from the text file—exported file from chart software print.phys show the length of the read phys data plot.phys plot phys data plot.phys.aligned plot the aligned phys data which is available by align.phys function align.phys return time aligned phys data truncate.phys truncate the phys data nessary for the analysis get.spike.timing return the time stamps of the peaks of spikes in EMG detect.peak detect the spike peaks find.peak return peak values from one time series data my.theme customize ggplot for publication, usage: q + my.theme() 2. Kagaya_Patek_kinem_control_hsv.R Functions for preprocessing and obtaining kinematic data. function name function read.one.mdf read mdf — output of MTrackJ which is a plugin of ImageJ used for digitizing get.rotation return rotation data with the current two lines method get.rotation2 return rotation data with the previous pivot point method check.rotation compare the two methods plot.track plot digitized points my.theme customize ggplot for publication, usage: q + my.theme() 3. Kagaya_Patek_kinem_control_stat.R Functions for visualization and statistical modeling of the dataset. function name function generate.data.for.model.selection generate dataset (stat.df) for statistical modeling aggregate.stats return descriptive statistics of stat.df model.selection perform model selection and output the AICs and coefficients fit.input.rotation.vs.number.of.spike visualize the relationship of input spring rotation and # spikes fit.strike.velocity.vs.number.of.spike visualize the relationship of strike velocity and # spikes plot.time.diff.acc plot time intervals between maximum acceleration of propodus and meral-V plot.emg.contraction plot input meral-V rotation and EMG of the extensor fit.3.kinematics fit the 10th order polynomial and GAM of 3 strikes fit.kinematics my.theme fit the 10th order polynomial and GAM of a single strike customize ggplot for publication, usage: q + my.theme() Usage Before using these functions, please load the following packages required for the analyses: > library(ggplot2); library(plyr); library(mgcv); library(nlme) read.phys Type, > phys <- read.phys(“folder_name_containing_text_files_exported_from_labchart_software”) then EMG data will be stored in the object, phys. Each text file looks like the screen shot below. The left-most column contains the timestamp, and following columns contain voltage time series data. In this case, four columns are contained in a text file named ’01.txt’. The folder is expected to contained several text files in this format and the files are expected to be named as ’01.txt’, ’02.txt’… print.phys Type, > print.phys(phys) 24 physiological data. or just type, > phys 24 physiological data. The class of the phys is phys, so you do not have to type ‘phys’ for print, and plot functions. plot.phys Type, > plot(phys) then a plot like below will be returned. It looks the time stamps are aligned. However, the time stamps of the data phys are actually not aligned (see the x axis of the plot below). > plot(phys[[1]]$phys$V1, phys[[1]]$phys$V2, type="l") plot.phys.aligned align.phys To align, type, > phys.aligned <- align.phys(phys) > plot.phys.aligned(phys.aligned, ch=1) The ‘align.phys’ will align the time series, and ‘plot.phys.aligned’ will return the plot of the time series data of the channel set by 'ch'. truncate.phys > phys.truncated <- truncate.phys(phys.aligned, my.lim = c(-0.75, 0.01)) > plot.phys.aligned(phys.truncated) This function truncates phys data by setting the time range. In this case, phys.truncated will be the object containing the data from -0.75 sec to 0.01 sec. get.spike.timing detect.peak find.peak To detect the spikes of the EMG data and store the spike timing data, type > spike.timings <- get.spike.timing(phys.truncated) then this will give the next plot, such as: then you are expected to click two points which determine a time range for detecting spikes. If you click the two points, then a zoomed-in window will appear, and you are expected to select a threshold. The window will show red open circles to indicate the detected points. The functions "detect.peak" and "find.peak" are used in the function "get.spike.timing". After showing the detected points, the function will show three options, 1: next 2: redo 3: no_spikes Selection: Select one of these three options to continue. If you repeat this process as many as the number of the strikes, then the spike timing data will be stored in ' spike.timings '. read.one.mdf plot.track > my.track <- read.one.mdf('file_name_of_digitized_data_using_MTrackJ.mdf') > plot.track(my.track) It should be noted that this plot is upside down. The points 1, 2 are on the propodus, and the points 3, 4 are on the meral-V, and the points 5-8 are on the merus. The point 1 is the trajectory of the tip of propodus. get.rotation To get rotation of the two lines (uses the two line method described in the online Supplementary Materials of Kagaya&Patek). > my.rotation <- get.rotation(my.track, target.v=nb504$strike$chosen.point$propodus[[1]]$target, ref.v=nb504$strike$chosen.point$propodus[[1]]$reference) > plot(my.rotation) Thus, the function get.rotation will give angular rotation of the propodus. get.rotation2 To get rotation of a point using previous method (pivot point method), this function can be used. > my.rotation2 <- get.rotation2(my.track, 212.18, 1) > plot(my.rotation2) Since the number of points of my.rotation2 is one less than my.rotation2, > my.rotation2 <- c(0, my.rotation2) and, the sign of my.rotation is minus. > plot(-my.rotation) > points(my.rotation2, pch=3) check.rotation > check.rotation(animal = nb504, strike = 10, focal.p.m = 3, vlines = c(1, 21, 36, 46, 55)) arm.length = 212.18, focal.p = 1, arm.length.m = 80.63, generate.data.for.model.selection > stat.df <- generate.data.for.model.selection(ang=20, time.lim=c(-10,0), nb18, nb30, nb55, nb500, nb504, nb520) > names(stat.df) [1] "id" "strike.num" "input.rotation" [4] "input.rotation2" "rotational.vel.m.in" "max.acc.m.in" [7] "velocity.meralV" "max.acc.meralV" "time.at.max.acc.meralV" [10] "time.diff.max.acc" "time.at.limit" "time.at.max.linear.vel" [13] "time.at.max.acc" "max.linear.vel" "linear.vel.at.limit" [16] "max.linear.acc" "linear.acc.at.limit" "max.vel" [19] "vel.at.limit" "max.acc" "acc.at.limit" [22] "spike.num" "spike.num.init100" "spike.num.last100" [25] "spike.num.100.with.silent" "duration" "duration2" [28] "silent" "spike.num.over.duration" "spike.num.over.duration2" column's name id strike.num input.rotation input.rotation2 rotational.vel.m.in contents animal ID strike event number input rotation of meral-V (degrees) input rotation of meral-V offset by the value when the extensor is on (degrees) maximum angular velocity of loading movement of meral-V (rad/s) max.acc.m.in velocity.meralV max.acc.meralV time.at.max.acc.meralV time.diff.max.acc time.at.limit time.at.max.linear.vel time.at.max.acc max.linear.vel linear.vel.at.limit max.linear.acc linear.acc.at.limit max.vel vel.at.limit max.acc acc.at.limit spike.num spike.num.init100 spike.num.last100 spike.num.100.with.silent duration duration2 silent spike.num.over.duration spike.num.over.duration2 maximum angular acceleration of loading movement of meral-V (rad/s2) maximum angular velocity of unloading movement of meral-V (rad/s) maximum angular acceleration of unloading movement of meral-V (rad/s2) duration until maximum acceleration of meral-V (ms) time difference between the max. acc. of meral-V and propodus (ms) time at the limit set by 'ang' (ms) time at maximum linear velocity of propodus (ms) time at maximum acceleration of propodus (ms) maximum linear velocity of propodus (m/s) linear velocity when the angular position of propodus is at the limit set by 'ang' (m/s) maximum linear acceleration of propodus movement (m/s2) linear acceleration when the angular position of propodus is at the limit set by 'ang' (m/s) maximum angular velocity of propodus movement (rad/s) angular velocity of propodus movement when the angular position of propodus is at the limit set by 'ang' (rad/s) maximum angular acceleration of proposed movement (rad/s2) angular acceleration of propodus when the angular position of propodus is at the limit set by 'ang' (rad/s2) number of extensor spikes during co-activation (samples) number of extensor spikes of initial 100 ms of co-activation (samples) number of extensor spikes of last 100 ms of co-activation (samples) number of extensor spikes of 100 ms before initiation including silent phase (samples) duration of co-activation phase (s) duration of co-activation + silent phase (s) duration of silent phase (s) number of spikes divided by duration (samples/s) number of spikes divided by duration2 (samples/s) aggregate.stats > aggregate.stats(stat.df, variable="max.linear.vel", my.times=1, my.round=1) nb18 , 18.8 x 1 +- 3 x 1 nb18 , 11.8 x 1 to 21.6 x 1 nb30 , 11.1 x 1 +- 2.8 x 1 nb30 , 7.9 x 1 to 17.1 x 1 nb55 , 12.3 x 1 +- 2.3 x 1 nb55 , 6.5 x 1 to 16 x 1 nb500 , 5.4 x 1 +- 1.9 x 1 nb500 , 2.8 x 1 to 8.3 x 1 nb504 , 9 x 1 +- 1.3 x 1 nb504 , 6.9 x 1 to 11.2 x 1 Thus, it gives descriptive statistics. model.selection > model.selection(stat.df) > model.selection(stat.df) explanatory.var AIC.REML AIC.ML intercept intercept.se slope slope.se (Intercept) spike.num 463.1411 461.2025 4.206958 1.070857 0.14150328 0.06191333 (Intercept)1 spike.num.init100 485.8613 486.9569 6.465246 2.408308 0.17325476 0.13632150 (Intercept)2 spike.num.last100 480.8463 481.9937 4.629519 1.994123 0.33616079 0.17723701 (Intercept)3 spike.num.100.with.silent 473.7170 475.2103 4.881967 1.425891 0.46282135 0.27857630 (Intercept)4 duration 457.7649 465.4289 3.424693 1.171189 15.30112691 7.19606425 (Intercept)5 silent 464.2328 477.3489 9.830163 4.417432 -45.67679915 81.36731397 (Intercept)6 spike.num.over.duration 485.1200 482.0156 5.005784 1.914403 0.03424507 0.02499605 (Intercept)7 spike.num.over.duration2 480.2285 477.2019 4.144641 1.751028 0.04954904 0.02957959 (Intercept)8 no.random.effects 558.6284 NA 9.487753 1.110818 -0.01942053 0.02624676 (Intercept)9 null 479.6567 482.9029 8.137653 2.141671 0.00000000 NA pval (Intercept) 0.02486472 (Intercept)1 0.20734810 (Intercept)2 0.06139211 (Intercept)3 0.10045585 (Intercept)4 0.03648363 (Intercept)5 0.57607942 (Intercept)6 0.17442044 (Intercept)7 0.09772263 (Intercept)8 0.46136296 (Intercept)9 NA explanatory.var AIC.REML AIC.ML intercept intercept.se slope slope.se (Intercept) input.rotation 1252.971 1272.510 1219.572 220.6624 40.287600 15.332805 (Intercept)1 spike.num 1247.850 1265.841 1190.065 235.8550 14.958630 6.292672 (Intercept)2 spike.num.init100 1275.570 1295.804 1627.593 345.8714 -3.395432 13.042795 (Intercept)3 spike.num.last100 1256.628 1278.451 1202.264 359.3402 45.353524 32.317438 (Intercept)4 spike.num.100.with.silent 1253.960 1275.454 1087.739 288.7316 73.569795 32.281425 (Intercept)5 duration 1237.108 1263.766 1035.774 180.5474 1695.300122 616.642632 (Intercept)6 silent 1258.791 1291.468 1798.548 450.6945 -6944.531247 7094.235567 (Intercept)7 spike.num.over.duration 1275.991 1292.390 1334.574 323.0438 2.665808 2.030232 (Intercept)8 spike.num.over.duration2 1272.768 1289.356 1217.518 322.0429 4.263625 2.217593 (Intercept)9 no.random.effects 1410.643 NA 1610.524 157.3805 -1.086799 3.718638 (Intercept)10 null 1276.580 1289.950 1585.320 338.2133 0.000000 NA pval (Intercept) 0.010263001 (Intercept)1 0.019775421 (Intercept)2 0.795261786 (Intercept)3 0.164280208 (Intercept)4 0.025265655 (Intercept)5 0.007345858 (Intercept)6 0.330509761 (Intercept)7 0.192826840 (Intercept)8 0.057997769 (Intercept)9 0.770793883 (Intercept)10 NA fit.input.rotation.vs.number.of.spike > fit.input.rotation.vs.number.of.spike(stat.df) * The summary of the constant model: Linear mixed-effects model fit by REML Data: ddf AIC BIC logLik 479.6567 487.0544 -236.8283 Random effects: Formula: ~1 | id (Intercept) Residual StdDev: 4.718831 3.305341 Fixed effects: input.rotation ~ 1 Value Std.Error DF t-value p-value (Intercept) 8.137653 2.141671 83 3.799675 3e-04 Standardized Within-Group Residuals: Min Q1 Med Q3 -2.36873405 -0.58618144 0.01086766 0.56482019 Max 3.64202228 Number of Observations: 88 Number of Groups: 5 * The summary of the correlative model: Linear mixed-effects model fit by REML Data: ddf AIC BIC logLik 463.1411 477.8672 -225.5705 Random effects: Formula: ~1 + spike.num | id Structure: General positive-definite, Log-Cholesky parametrization StdDev Corr (Intercept) 0.9576121 (Intr) spike.num 0.1169618 0.9 Residual 2.8136329 Fixed effects: input.rotation ~ spike.num Value Std.Error DF t-value p-value (Intercept) 4.206958 1.0708566 82 3.928592 0.0002 spike.num 0.141503 0.0619133 82 2.285506 0.0249 Correlation: (Intr) spike.num -0.155 Standardized Within-Group Residuals: Min Q1 Med Q3 Max -2.46906304 -0.60694381 -0.02264248 0.61118779 3.24939812 Number of Observations: 88 Number of Groups: 5 fit.strike.velocity.vs.number.of.spike > fit.strike.velocity.vs.number.of.spike(stat.df) * The summary of the constant model: Linear mixed-effects model fit by REML Data: df AIC BIC logLik 1276.58 1283.978 -635.2901 Random effects: Formula: ~1 | id (Intercept) Residual StdDev: 752.2518 314.9232 Fixed effects: vel.at.limit ~ 1 Value Std.Error DF t-value p-value (Intercept) 1585.32 338.2133 83 4.68734 0 Standardized Within-Group Residuals: Min Q1 Med Q3 -3.47926010 -0.68526185 0.05504432 0.57435227 Max 2.51601954 Number of Observations: 88 Number of Groups: 5 * The summary of the correlative model: Linear mixed-effects model fit by REML Data: df AIC BIC logLik 1247.85 1262.576 -617.9249 Random effects: Formula: ~1 + spike.num | id Structure: General positive-definite, Log-Cholesky parametrization StdDev Corr (Intercept) 469.73339 (Intr) spike.num 11.86145 0.443 Residual 259.32414 Fixed effects: vel.at.limit ~ spike.num Value Std.Error DF t-value p-value (Intercept) 1190.0646 235.85500 82 5.045747 0.0000 spike.num 14.9586 Correlation: (Intr) spike.num 0.111 6.29267 82 2.377151 0.0198 Standardized Within-Group Residuals: Min Q1 Med Q3 -3.44417658 -0.59393938 0.02721824 0.47277176 Number of Observations: 88 Number of Groups: 5 plot.time.diff.acc > plot.time.diff.acc(stat.df) Max 2.76723871 plot.emg.contraction > plot.emg.contraction(nb504, emg.ch=1, strike.num=2) fit.3.kinematics > fit.3.kinematics(nb30, my.strikes=c(1,2,3)) fit.kinematics If you want to see kinematics of the first strike movement by the animal nb18, type > fit.kinematics(nb30, 1)