Tabulate tracking task data & kinematics

[1]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
import json
from datetime import date

import aopy
import aopy.data.bmi3d as bmi3d
import aopy.data.db as db
plt.rcParams['font.size'] = '16'
/home/aolab/miniconda3/envs/katherine-analysis/lib/python3.9/site-packages/pandas/core/computation/expressions.py:21: UserWarning: Pandas requires version '2.8.4' or newer of 'numexpr' (version '2.8.1' currently installed).
  from pandas.core.computation.check import NUMEXPR_INSTALLED
/home/aolab/miniconda3/envs/katherine-analysis/lib/python3.9/site-packages/pandas/core/arrays/masked.py:60: UserWarning: Pandas requires version '1.3.6' or newer of 'bottleneck' (version '1.3.4' currently installed).
  from pandas.core import (
/home/aolab/miniconda3/envs/katherine-analysis/lib/python3.9/site-packages/one/alf/files.py:10: FutureWarning: `one.alf.files` will be removed in version 3.0. Use `one.alf.path` instead.
  warnings.warn(
[2]:
preproc_dir = '/data/preprocessed/'
db.BMI3D_DBNAME = 'booth' # for in-cage data, use 'tablet'

# load task codes
task_codes = aopy.data.bmi3d.load_bmi3d_task_codes()

# set constants
samplerate = 120

Tabulate (newer) experiment data

Newer tracking task data contains unique event codes and event times for the start and end of ramp periods.

This does not effect the usability of the tabulated dataframe (all columns will be present), simply the behind-the-scenes way the dataframe columns are filled in.

[3]:
# set subject and range of dates to look up
subject = 'churro'
task_date = (date.fromisoformat('2025-03-03'), date.fromisoformat('2025-03-13'))

# look up sessions (aka entries) of subject within date range
entries = db.lookup_sessions(subject=subject, date=task_date, task_desc='tracking task')
display(entries)

# sort entries into lists of important metadata
subjects, te_ids, te_dates = db.list_entry_details(entries)
[2025-03-03 16:06:16.137089: churro on tracking task, id=19367,
 2025-03-04 16:29:38.509324: churro on tracking task, id=19381,
 2025-03-05 14:59:10.425884: churro on tracking task, id=19390,
 2025-03-05 15:23:08.784877: churro on tracking task, id=19392,
 2025-03-05 15:34:52.672716: churro on tracking task, id=19394,
 2025-03-06 14:21:12.534646: churro on tracking task, id=19401,
 2025-03-06 14:40:30.642826: churro on tracking task, id=19402,
 2025-03-06 15:19:44.195229: churro on tracking task, id=19403,
 2025-03-11 14:46:48.316095: churro on tracking task, id=19423,
 2025-03-11 15:12:35.944435: churro on tracking task, id=19424,
 2025-03-11 15:41:40.775817: churro on tracking task, id=19425]
[4]:
# tabulate data across entries into a df where rows = trials
metadata = [
    'features', 'scale', 'rotation', 'exp_rotation', 'offset',
    'trajectory_amplitude', 'disturbance_amplitude', 'cursor_radius', 'target_radius',
    'tracking_out_time'
]
df = bmi3d.tabulate_behavior_data_tracking_task(preproc_dir, subjects, te_ids, te_dates, metadata)
df.head()
[4]:
subject te_id date event_codes event_times reward penalty features scale rotation ... tracking_start_time trajectory_start_time trajectory_end_time tracking_end_time reward_start_time penalty_start_time penalty_event pause_start_time pause_event trial_end_time
0 churro 19367 2025-03-03 [16, 2, 64, 239] [6.02204, 6.457, 6.60116, 7.93624] False True [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 zyx ... NaN NaN NaN NaN NaN 6.60116 64.0 NaN NaN 7.93624
1 churro 19367 2025-03-03 [16, 2, 81, 97, 81, 97, 81, 80, 96, 80, 96, 80... [8.11332, 8.52728, 8.93504, 9.3966, 9.72716, 9... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 zyx ... 8.93504 10.48168 30.83520 32.36324 32.36324 NaN NaN NaN NaN 33.00908
2 churro 19367 2025-03-03 [16, 2, 64, 239] [33.17244, 33.29592, 33.31952, 34.65868] False True [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 zyx ... NaN NaN NaN NaN NaN 33.31952 64.0 NaN NaN 34.65868
3 churro 19367 2025-03-03 [16, 2, 81, 97, 81, 97, 96, 80, 96, 80, 96, 80... [34.83156, 35.18776, 35.5954, 36.00936, 36.585... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 zyx ... 35.59540 37.09492 57.57612 59.13304 59.13304 NaN NaN NaN NaN 59.75772
4 churro 19367 2025-03-03 [16, 2, 81, 97, 81, 97, 81, 97, 81, 80, 96, 80... [59.93952, 59.9478, 60.35448, 60.7454, 61.0116... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 zyx ... 60.35448 61.91224 82.83836 84.39132 84.39132 NaN NaN NaN NaN 85.03296

5 rows × 35 columns

Tabulate (older) experiment data

Older tracking task data does not contain unique ramp events; the start and end of ramp periods are estimated using the sequence generator params.

This does not effect the usability of the tabulated dataframe (all columns will be present), simply the behind-the-scenes way the dataframe columns are filled in.

[5]:
# set subject and range of dates to look up
subject = 'beignet'
task_date = (date.fromisoformat('2023-06-13'), date.fromisoformat('2023-06-24'))

# look up sessions (aka entries) of subject within date range
entries = db.lookup_sessions(subject=subject, date=task_date, task_desc='tracking task')
display(entries)

# sort entries into lists of important metadata
subjects, te_ids, te_dates = db.list_entry_details(entries)
[2023-06-13 14:29:37.570000: beignet on tracking task, id=9701,
 2023-06-13 14:49:06.959000: beignet on tracking task, id=9702,
 2023-06-13 14:55:33.866000: beignet on tracking task, id=9703,
 2023-06-14 13:39:04.384000: beignet on tracking task, id=9717,
 2023-06-14 13:50:40.366000: beignet on tracking task, id=9718,
 2023-06-14 13:53:52.918000: beignet on tracking task, id=9719,
 2023-06-15 13:08:24.838000: beignet on tracking task, id=9723,
 2023-06-15 13:19:57.469000: beignet on tracking task, id=9724,
 2023-06-15 13:31:18.419000: beignet on tracking task, id=9725,
 2023-06-16 13:53:07.923000: beignet on tracking task, id=9728,
 2023-06-16 14:05:50.244000: beignet on tracking task, id=9729,
 2023-06-16 14:08:57.072000: beignet on tracking task, id=9730,
 2023-06-17 13:40:35.067000: beignet on tracking task, id=9734,
 2023-06-17 13:52:16.907000: beignet on tracking task, id=9735,
 2023-06-17 13:55:26.072000: beignet on tracking task, id=9736,
 2023-06-18 13:34:55.077000: beignet on tracking task, id=9740,
 2023-06-18 13:47:16.040000: beignet on tracking task, id=9741,
 2023-06-18 13:50:46.566000: beignet on tracking task, id=9742,
 2023-06-19 13:36:31.520000: beignet on tracking task, id=9746,
 2023-06-19 13:47:54.395000: beignet on tracking task, id=9747,
 2023-06-19 13:51:05.324000: beignet on tracking task, id=9748,
 2023-06-20 13:19:59.844000: beignet on tracking task, id=9757,
 2023-06-20 13:30:50.048000: beignet on tracking task, id=9758,
 2023-06-20 13:33:48.643000: beignet on tracking task, id=9759,
 2023-06-21 14:34:55.897000: beignet on tracking task, id=9763,
 2023-06-21 14:46:56.112000: beignet on tracking task, id=9764,
 2023-06-21 14:49:53.405000: beignet on tracking task, id=9765,
 2023-06-22 11:34:31.724000: beignet on tracking task, id=9775,
 2023-06-22 11:45:52.258000: beignet on tracking task, id=9776,
 2023-06-22 11:49:13.957000: beignet on tracking task, id=9777,
 2023-06-23 12:54:29.746000: beignet on tracking task, id=9785,
 2023-06-23 13:05:42.598000: beignet on tracking task, id=9786,
 2023-06-23 13:08:54.243000: beignet on tracking task, id=9787]
[7]:
# tabulate data across entries into a df where rows = trials
metadata = [
    'features', 'scale', 'rotation', 'exp_rotation', 'offset',
    'trajectory_amplitude', 'disturbance_amplitude', 'cursor_radius', 'target_radius',
    'tracking_out_time'
]
df = bmi3d.tabulate_behavior_data_tracking_task(preproc_dir, subjects, te_ids, te_dates, metadata)
df.head()
[7]:
subject te_id date event_codes event_times reward penalty features scale rotation ... tracking_start_time trajectory_start_time trajectory_end_time tracking_end_time reward_start_time penalty_start_time penalty_event pause_start_time pause_event trial_end_time
0 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [6.79096, 7.24928, 7.45828, 11.7332, 12.1748, ... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... 7.45828 8.95828 28.97444 30.47444 30.47444 NaN NaN NaN NaN 31.39560
1 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [33.091, 33.09912, 33.30768, 40.55796, 41.2824... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... 33.30768 34.80768 54.84080 56.34080 56.34080 NaN NaN NaN NaN 57.26276
2 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [58.95804, 59.224, 59.433, 60.63292, 61.01628,... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... 59.43300 60.93300 80.98240 82.48240 82.48240 NaN NaN NaN NaN 83.40760
3 beignet 9701 2023-06-13 [16, 2, 64, 239] [85.09884, 85.44052, 85.62388, 86.94212] False True [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN NaN NaN NaN NaN 85.62388 64.0 NaN NaN 86.94212
4 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [88.64056, 89.09056, 89.29948, 91.6656, 91.916... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... 89.29948 90.79948 110.85728 112.35728 112.35728 NaN NaN NaN NaN 113.28256

5 rows × 35 columns

[8]:
# add additional metadata related to visuomotor perturbations (note: non-perturbation sessions will have NaN values for these params)
param_names = ['pertubation_rotation', 'init_rotation_y', 'final_rotation_y', 'delta_rotation_y', 'trials_per_increment']
param_funcs = [lambda x: x.get_task_param(name) for name in param_names]

db.add_metadata_columns(df, entries, param_names, param_funcs)
df.head()
[8]:
subject te_id date event_codes event_times reward penalty features scale rotation ... penalty_start_time penalty_event pause_start_time pause_event trial_end_time pertubation_rotation init_rotation_y final_rotation_y delta_rotation_y trials_per_increment
0 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [6.79096, 7.24928, 7.45828, 11.7332, 12.1748, ... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN NaN NaN NaN 31.39560 6.0 6.0 6.0 6.0 6.0
1 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [33.091, 33.09912, 33.30768, 40.55796, 41.2824... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN NaN NaN NaN 57.26276 6.0 6.0 6.0 6.0 6.0
2 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [58.95804, 59.224, 59.433, 60.63292, 61.01628,... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN NaN NaN NaN 83.40760 6.0 6.0 6.0 6.0 6.0
3 beignet 9701 2023-06-13 [16, 2, 64, 239] [85.09884, 85.44052, 85.62388, 86.94212] False True [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... 85.62388 64.0 NaN NaN 86.94212 6.0 6.0 6.0 6.0 6.0
4 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [88.64056, 89.09056, 89.29948, 91.6656, 91.916... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN NaN NaN NaN 113.28256 6.0 6.0 6.0 6.0 6.0

5 rows × 40 columns

[9]:
# add even more metadata by extracting info from the 'sequence_params' column
df['sequence_params'] = [json.loads(df.iloc[i]['sequence_params']) for i in range(len(df))] # convert 'sequence_params' from string to dict so it's easier to use

time_length = [df.sequence_params[i]['time_length'] for i in range(len(df))]
ramp = [df.sequence_params[i]['ramp'] for i in range(len(df))]
ramp_down = [df.sequence_params[i]['ramp_down'] for i in range(len(df))]

df['time_length'] = time_length
df['ramp'] = ramp
df['ramp_down'] = ramp_down
df.head()
[9]:
subject te_id date event_codes event_times reward penalty features scale rotation ... pause_event trial_end_time pertubation_rotation init_rotation_y final_rotation_y delta_rotation_y trials_per_increment time_length ramp ramp_down
0 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [6.79096, 7.24928, 7.45828, 11.7332, 12.1748, ... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN 31.39560 6.0 6.0 6.0 6.0 6.0 20 1.5 1.5
1 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [33.091, 33.09912, 33.30768, 40.55796, 41.2824... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN 57.26276 6.0 6.0 6.0 6.0 6.0 20 1.5 1.5
2 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [58.95804, 59.224, 59.433, 60.63292, 61.01628,... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN 83.40760 6.0 6.0 6.0 6.0 6.0 20 1.5 1.5
3 beignet 9701 2023-06-13 [16, 2, 64, 239] [85.09884, 85.44052, 85.62388, 86.94212] False True [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN 86.94212 6.0 6.0 6.0 6.0 6.0 20 1.5 1.5
4 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [88.64056, 89.09056, 89.29948, 91.6656, 91.916... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN 113.28256 6.0 6.0 6.0 6.0 6.0 20 1.5 1.5

5 rows × 43 columns

Filter tabulated data

[10]:
# make new df containing only rewarded trials
reward_df = df[df['reward']==True]
reward_df.head()
[10]:
subject te_id date event_codes event_times reward penalty features scale rotation ... pause_event trial_end_time pertubation_rotation init_rotation_y final_rotation_y delta_rotation_y trials_per_increment time_length ramp ramp_down
0 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [6.79096, 7.24928, 7.45828, 11.7332, 12.1748, ... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN 31.39560 6.0 6.0 6.0 6.0 6.0 20 1.5 1.5
1 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [33.091, 33.09912, 33.30768, 40.55796, 41.2824... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN 57.26276 6.0 6.0 6.0 6.0 6.0 20 1.5 1.5
2 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [58.95804, 59.224, 59.433, 60.63292, 61.01628,... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN 83.40760 6.0 6.0 6.0 6.0 6.0 20 1.5 1.5
4 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [88.64056, 89.09056, 89.29948, 91.6656, 91.916... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN 113.28256 6.0 6.0 6.0 6.0 6.0 20 1.5 1.5
5 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [114.97368, 114.98204, 115.19068, 116.4076, 11... True False [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN 139.13532 6.0 6.0 6.0 6.0 6.0 20 1.5 1.5

5 rows × 43 columns

[11]:
# make new df containing only trials that ended in a tracking out penalty
tracking_out_df = df[df.penalty_event == task_codes['OTHER_PENALTY']]
tracking_out_df.head()
[11]:
subject te_id date event_codes event_times reward penalty features scale rotation ... pause_event trial_end_time pertubation_rotation init_rotation_y final_rotation_y delta_rotation_y trials_per_increment time_length ramp ramp_down
10 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [199.44816, 199.73984, 199.94872, 200.18216, 2... False True [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN 207.64756 6.0 6.0 6.0 6.0 6.0 20 1.5 1.5
15 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [313.88392, 314.15588, 314.36472, 315.73988, 3... False True [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN 334.70596 6.0 6.0 6.0 6.0 6.0 20 1.5 1.5
17 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [339.66408, 339.88904, 340.09812, 341.16664, 3... False True [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN 358.44252 6.0 6.0 6.0 6.0 6.0 20 1.5 1.5
21 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 80, 96, 80, 96, 80, 96... [393.9138, 393.98052, 394.1894, 394.56444, 394... False True [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN 404.47412 6.0 6.0 6.0 6.0 6.0 20 1.5 1.5
22 beignet 9701 2023-06-13 [16, 2, 80, 96, 80, 96, 79, 239] [406.14708, 409.5304, 409.73932, 411.52264, 41... False True [b'optitrack', b'reward_system', b'saveHDF', b... 1.0 yzx ... NaN 416.54836 6.0 6.0 6.0 6.0 6.0 20 1.5 1.5

5 rows × 43 columns

Get reward trial kinematics using tabulated data

[12]:
# specify columns from the df to use for segmenting kinematics
start_event = 'trajectory_start_time'
end_event = 'trajectory_end_time'

# get cursor & target kinematics for rewarded trials
cursor_segments_reward = bmi3d.tabulate_kinematic_data(
    preproc_dir, reward_df['subject'], reward_df['te_id'], reward_df['date'],
    reward_df[start_event], reward_df[end_event],
    datatype='cursor', samplerate=samplerate, upsamplerate=samplerate
)

target_segments_reward = bmi3d.tabulate_kinematic_data(
    preproc_dir, reward_df['subject'], reward_df['te_id'], reward_df['date'],
    reward_df[start_event], reward_df[end_event],
    datatype='reference', samplerate=samplerate, upsamplerate=samplerate
)
[13]:
# plot kinematics for an example rewarded trial
y_idx = 1 # tracking task signals are 1D, so only have non-zero y-values

for trial_idx in range(5):
    plt.figure(figsize=(10,6))
    plt.title(f'reward trial {trial_idx}', fontsize=18)
    t = np.linspace(0, target_segments_reward[trial_idx].shape[0]/samplerate, target_segments_reward[trial_idx].shape[0])
    plt.plot(t, target_segments_reward[trial_idx][:,y_idx], color='darkorange', label='target')
    plt.plot(t, cursor_segments_reward[trial_idx][:,y_idx], color='blueviolet', label='cursor')
    plt.fill_between(t, target_segments_reward[trial_idx][:,y_idx] - reward_df.target_radius.values[trial_idx],
                        target_segments_reward[trial_idx][:,y_idx] + reward_df.target_radius.values[trial_idx], color='darkorange', alpha=.25)
    plt.fill_between(t, cursor_segments_reward[trial_idx][:,y_idx] - reward_df.cursor_radius.values[trial_idx],
                        cursor_segments_reward[trial_idx][:,y_idx] + reward_df.cursor_radius.values[trial_idx], color='blueviolet', alpha=.25)
    plt.xlabel('time (s)')
    plt.ylabel('position (cm)')
    plt.legend(loc='upper right');
../_images/examples_tabulate_tracking_task_example_18_0.png
../_images/examples_tabulate_tracking_task_example_18_1.png
../_images/examples_tabulate_tracking_task_example_18_2.png
../_images/examples_tabulate_tracking_task_example_18_3.png
../_images/examples_tabulate_tracking_task_example_18_4.png

Get penalty trial kinematics using tabulated data

[14]:
# specify columns from the df to use for segmenting kinematics
start_event = 'trajectory_start_time'
end_event = 'trajectory_end_time' # equivalent to using 'penalty_start_time'

# get cursor & target kinematics for trials that ended in a tracking out penalty
cursor_segments_trackout = bmi3d.tabulate_kinematic_data(
    preproc_dir, tracking_out_df['subject'], tracking_out_df['te_id'], tracking_out_df['date'],
    tracking_out_df[start_event], tracking_out_df[end_event],
    datatype='cursor', samplerate=samplerate, upsamplerate=samplerate
)

target_segments_trackout = bmi3d.tabulate_kinematic_data(
    preproc_dir, tracking_out_df['subject'], tracking_out_df['te_id'], tracking_out_df['date'],
    tracking_out_df[start_event], tracking_out_df[end_event],
    datatype='reference', samplerate=samplerate, upsamplerate=samplerate
)
[15]:
# plot kinematics for an example penalty trial
y_idx = 1 # tracking task signals are 1D, so only have non-zero y-values

for trial_idx in range(5):
    plt.figure(figsize=(10,6))
    plt.title(f'tracking out trial {trial_idx}', fontsize=18)
    t = np.linspace(0, target_segments_trackout[trial_idx].shape[0]/samplerate, target_segments_trackout[trial_idx].shape[0])
    plt.plot(t, target_segments_trackout[trial_idx][:,y_idx], color='darkorange', label='target')
    plt.plot(t, cursor_segments_trackout[trial_idx][:,y_idx], color='blueviolet', label='cursor')
    plt.fill_between(t, target_segments_trackout[trial_idx][:,y_idx] - tracking_out_df.target_radius.values[trial_idx],
                        target_segments_trackout[trial_idx][:,y_idx] + tracking_out_df.target_radius.values[trial_idx], color='darkorange', alpha=.25)
    plt.fill_between(t, cursor_segments_trackout[trial_idx][:,y_idx] - tracking_out_df.cursor_radius.values[trial_idx],
                        cursor_segments_trackout[trial_idx][:,y_idx] + tracking_out_df.cursor_radius.values[trial_idx], color='blueviolet', alpha=.25)
    plt.xlabel('time (s)')
    plt.ylabel('position (cm)')
    plt.legend(loc='upper right');
../_images/examples_tabulate_tracking_task_example_21_0.png
../_images/examples_tabulate_tracking_task_example_21_1.png
../_images/examples_tabulate_tracking_task_example_21_2.png
../_images/examples_tabulate_tracking_task_example_21_3.png
../_images/examples_tabulate_tracking_task_example_21_4.png

Get inter-trial kinematics using tabulated data

[16]:
# specify columns from the df to use for segmenting kinematics
start_event = 'prev_trial_end_time'
end_event = 'target_on_time'

# get cursor & target kinematics between tracking out penalty and start of next trial
cursor_segments_intertrial = bmi3d.tabulate_kinematic_data(
    preproc_dir, tracking_out_df['subject'], tracking_out_df['te_id'], tracking_out_df['date'],
    tracking_out_df[start_event], tracking_out_df[end_event],
    datatype='cursor', samplerate=samplerate, upsamplerate=samplerate
)

target_segments_intertrial = bmi3d.tabulate_kinematic_data(
    preproc_dir, tracking_out_df['subject'], tracking_out_df['te_id'], tracking_out_df['date'],
    tracking_out_df[start_event], tracking_out_df[end_event],
    datatype='reference', samplerate=samplerate, upsamplerate=samplerate
)
[17]:
# plot kinematics for an example inter-trial interval
y_idx = 1 # tracking task signals are 1D, so only have non-zero y-values

for trial_idx in range(5):
    plt.figure(figsize=(10,6))
    plt.title(f'inter-trial {trial_idx}', fontsize=18)
    t = np.linspace(0, target_segments_intertrial[trial_idx].shape[0]/samplerate, target_segments_intertrial[trial_idx].shape[0])
    plt.plot(t, target_segments_intertrial[trial_idx][:,y_idx], color='darkorange', label='target')
    plt.plot(t, cursor_segments_intertrial[trial_idx][:,y_idx], color='blueviolet', label='cursor')
    plt.fill_between(t, target_segments_intertrial[trial_idx][:,y_idx] - reward_df.target_radius.values[trial_idx],
                        target_segments_intertrial[trial_idx][:,y_idx] + reward_df.target_radius.values[trial_idx], color='darkorange', alpha=.25)
    plt.fill_between(t, cursor_segments_intertrial[trial_idx][:,y_idx] - reward_df.cursor_radius.values[trial_idx],
                        cursor_segments_intertrial[trial_idx][:,y_idx] + reward_df.cursor_radius.values[trial_idx], color='blueviolet', alpha=.25)
    plt.xlabel('time (s)')
    plt.ylabel('position (cm)')
    plt.legend(loc='upper right');
../_images/examples_tabulate_tracking_task_example_24_0.png
../_images/examples_tabulate_tracking_task_example_24_1.png
../_images/examples_tabulate_tracking_task_example_24_2.png
../_images/examples_tabulate_tracking_task_example_24_3.png
../_images/examples_tabulate_tracking_task_example_24_4.png