In [1100]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import string
import matplotlib.patches as mpatches
plt.ioff() # Turn interactive plotting off

#url = "https://raw.githubusercontent.com/jamcro/shootGaze/master/data/shootingNoviceGaze.csv"
#gaze_df = pd.read_csv(url, sep=',', skipinitialspace=True)
#url = "https://raw.githubusercontent.com/jamcro/shootGaze/master/data/shootingNoviceKinematics.csv"
#kin_df = pd.read_csv(url, sep=',', skipinitialspace=True)

# shot_df 0=miss, 1=limb, 2=torso, missing=no shot
gaze_df = pd.read_csv('/Users/jc/Documents/GitHub/shootGaze/data/shootingNoviceGaze.csv', sep=',', skipinitialspace=True)
kin_df = pd.read_csv('/Users/jc/Documents/GitHub/shootGaze/data/shootingNoviceKinematics.csv', sep=',', skipinitialspace=True)
shot_df = pd.read_csv('/Users/jc/Documents/GitHub/shootGaze/data/shootingNoviceShots.csv', sep=',', skipinitialspace=True)

In [1101]:
# remove trailing nans
first_idx = gaze_df.first_valid_index()
last_idx = gaze_df.last_valid_index()
print(first_idx, last_idx)
gaze_df = gaze_df.loc[first_idx:last_idx]

#gaze_df['fixOn'] = (gaze_df.startTime - gaze_df.Timer)
#gaze_df['fixOff'] = (gaze_df.finishTime - gaze_df.Timer)
#gaze_df['fixLen'] = (gaze_df.fixOff - gaze_df.fixOn)

# put them in order to make table easier to view
gaze_df.insert(7, 'tFixOn', (gaze_df.startTime - gaze_df.Timer))
gaze_df.insert(8, 'tFixOff', (gaze_df.finishTime - gaze_df.Timer))
gaze_df.insert(9, 'fixDur', (gaze_df.tFixOff - gaze_df.tFixOn))
gaze_df['shotFired'] = gaze_df['shotFired'].astype('int64')
gaze_df['fixID'] = pd.Categorical(gaze_df['fixID'])
gaze_df['fixIDcode'] = gaze_df.fixID.cat.codes
#gaze_df['fixID'].value_counts()
gaze_df.head(10)


0 877
Out[1101]:
Subject Intervention Day Trial Timer startTime finishTime tFixOn tFixOff fixDur fixID Position Distance shotFired fixIDcode
0 1 0 1 1 7.05 7.685 8.722 0.635 1.672 1.037 B 2 5 0 1
1 1 0 1 1 7.05 8.790 9.419 1.740 2.369 0.629 A 1 5 0 0
2 1 0 1 1 7.05 9.470 13.006 2.420 5.956 3.536 B 2 5 1 1
3 1 0 1 1 7.05 13.074 15.947 6.024 8.897 2.873 C 3 7 0 2
4 1 0 1 1 7.05 15.981 17.834 8.931 10.784 1.853 D 4 9 0 3
5 1 0 1 1 7.05 17.868 18.769 10.818 11.719 0.901 C 3 7 0 2
6 1 0 1 1 7.05 18.837 20.333 11.787 13.283 1.496 D 4 9 0 3
7 1 0 1 2 3.58 4.331 4.654 0.751 1.074 0.323 B 1 7 0 1
8 1 0 1 2 3.58 4.756 4.943 1.176 1.363 0.187 C 2 5 0 2
9 1 0 1 2 3.58 5.038 6.024 1.458 2.444 0.986 B 1 7 0 1

In [1102]:
shot_df['threat1'] = pd.Categorical(shot_df['threat1'], categories=['A','B','C','D'])
shot_df['threat2'] = pd.Categorical(shot_df['threat2'], categories=['A','B','C','D'])
shot_df['targetShot1'] = pd.Categorical(shot_df['targetShot1'], categories=['A','B','C','D'])
shot_df['targetShot2'] = pd.Categorical(shot_df['targetShot2'], categories=['A','B','C','D'])
shot_df['outcomeShot1'] = pd.Categorical(shot_df['outcomeShot1'])
shot_df['outcomeShot2'] = pd.Categorical(shot_df['outcomeShot2'])
shot_df['threat2'].value_counts()


Out[1102]:
D    2
C    0
B    0
A    0
Name: threat2, dtype: int64

In [1103]:
# remove trailing nans
first_idx = kin_df.first_valid_index()
last_idx = kin_df.last_valid_index()
print(first_idx, last_idx)
kin_df = kin_df.loc[first_idx:last_idx]

# convert dtypes
kin_df['Subject'] = kin_df['Subject'].astype('int64')
kin_df['Intervention'] = kin_df['Intervention'].astype('int64')
kin_df['Day'] = kin_df['Day'].astype('int64')
kin_df['Trial'] = kin_df['Trial'].astype('int64')

#subtract Timer (start of trial) from all time values
kin_df.loc[:, 'Timer':'tShot2'] = kin_df.loc[:, 'Timer':'tShot2'].sub(kin_df['Timer'], axis=0)
kin_df.head(3)


0 69
Out[1103]:
Subject Intervention Day Trial Timer tHandGun tDrawn tGunReady tOnTrigger tShot1 tOffTrigger tOnTrigger2 tShot2
0 1 0 1 1 0.0 1.739 2.428 3.149 3.428 5.421 11.596 NaN NaN
1 1 0 1 2 0.0 0.808 1.987 2.710 NaN NaN NaN NaN NaN
2 1 0 1 3 0.0 0.605 2.320 3.205 3.602 6.324 7.205 NaN NaN

In [1104]:
# combine data that has one row per trial
events_df = pd.merge(kin_df, shot_df, on=['Subject', 'Intervention', 'Day', 'Trial'])

# combine with gaze data (i.e. repeat trial based data for each fixation)
dat_df = pd.merge(gaze_df,events_df, on=['Subject', 'Intervention', 'Day', 'Trial'])
dat_df.head(10)


Out[1104]:
Subject Intervention Day Trial Timer_x startTime finishTime tFixOn tFixOff fixDur ... tShot1 tOffTrigger tOnTrigger2 tShot2 threat1 threat2 targetShot1 targetShot2 outcomeShot1 outcomeShot2
0 1 0 1 1 7.05 7.685 8.722 0.635 1.672 1.037 ... 5.421 11.596 NaN NaN B NaN B NaN Miss NaN
1 1 0 1 1 7.05 8.790 9.419 1.740 2.369 0.629 ... 5.421 11.596 NaN NaN B NaN B NaN Miss NaN
2 1 0 1 1 7.05 9.470 13.006 2.420 5.956 3.536 ... 5.421 11.596 NaN NaN B NaN B NaN Miss NaN
3 1 0 1 1 7.05 13.074 15.947 6.024 8.897 2.873 ... 5.421 11.596 NaN NaN B NaN B NaN Miss NaN
4 1 0 1 1 7.05 15.981 17.834 8.931 10.784 1.853 ... 5.421 11.596 NaN NaN B NaN B NaN Miss NaN
5 1 0 1 1 7.05 17.868 18.769 10.818 11.719 0.901 ... 5.421 11.596 NaN NaN B NaN B NaN Miss NaN
6 1 0 1 1 7.05 18.837 20.333 11.787 13.283 1.496 ... 5.421 11.596 NaN NaN B NaN B NaN Miss NaN
7 1 0 1 2 3.58 4.331 4.654 0.751 1.074 0.323 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
8 1 0 1 2 3.58 4.756 4.943 1.176 1.363 0.187 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
9 1 0 1 2 3.58 5.038 6.024 1.458 2.444 0.986 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

10 rows × 30 columns

Find gaze and events when fixation on valid threat and shot fired


In [1105]:
fired_df = dat_df.loc[(((dat_df['fixID']==dat_df['threat1']) | (dat_df['fixID']==dat_df['threat2'])) & 
           (dat_df['shotFired']==1)), 
           ['Subject', 'Intervention', 'Day', 'Trial', 'shotFired','tFixOn', 'fixID', 'threat1', 'threat2',
            'tOnTrigger', 'tShot1', 'tShot2', 'targetShot1', 'targetShot2', 'outcomeShot1', 'outcomeShot2']]
fired_df.head(10)


Out[1105]:
Subject Intervention Day Trial shotFired tFixOn fixID threat1 threat2 tOnTrigger tShot1 tShot2 targetShot1 targetShot2 outcomeShot1 outcomeShot2
2 1 0 1 1 1 2.420 B B NaN 3.428 5.421 NaN B NaN Miss NaN
23 1 0 1 3 1 5.366 A A NaN 3.602 6.324 NaN A NaN Torso NaN
29 1 0 1 4 1 6.034 D D NaN 2.452 5.080 7.726 C D Miss Miss
35 1 0 1 5 1 8.866 D C D 2.763 4.531 NaN C NaN Torso NaN
58 1 0 1 8 1 2.333 D D NaN 2.812 4.480 NaN D NaN Torso NaN
61 1 0 1 9 1 1.378 D A D 2.874 3.312 4.630 A D Torso Torso
63 1 0 1 10 1 0.425 C C NaN 2.331 2.840 NaN B NaN Torso NaN

In [1093]:
subj=1
day=1
tr=5
df=gaze_df
x = df.loc[(df.Subject == subj) & (df.Day == day) & (df.Trial == tr)]
x


Out[1093]:
Subject Intervention Day Trial Timer startTime finishTime tFixOn tFixOff fixDur fixID Position Distance shotFired fixIDcode
33 1 0 1 5 46.15 51.786 52.806 5.636 6.656 1.020 B 4 9 0 1
34 1 0 1 5 46.15 48.012 50.851 1.862 4.701 2.839 C 3 9 0 2
35 1 0 1 5 46.15 55.016 57.022 8.866 10.872 2.006 D 1 7 1 3

define function to plot gaze for each subject


In [834]:
def plotGazeTimeline(df, subj, day, tr, axId):
    # identify this trial
    x = df.loc[(df.Subject == subj) & (df.Day == day) & (df.Trial == tr)].tFixOn
    width = df.loc[(df.Subject == subj) & (df.Day == day) & (df.Trial == tr)].fixDur
    bottom = df.loc[(df.Subject == subj) & (df.Day == day) & (df.Trial == tr)].Position
    height = [0.5] * len(x)

    labels = ['Far Left','Left','Right','Far Right']
    catColor = df.loc[(df.Subject == subj) & (df.Day == day) & (df.Trial == tr)].catCode
    mycolors = ['#d7191c','#fdae61','#abdda4','#2b83ba']
    colorlist = [mycolors[x] for x in catColor-1]
    
    if axId.ndim == 1:
        axId[tr-1].bar(x, height, width, bottom, align='edge', color=colorlist, label=catColor)
    else:
        axId[tr-1, day-1].bar(x, height, width, bottom, align='edge', color=colorlist, label=catColor)
    
    ax=plt.gca()                            # get the axis
    ax.set_ylim([0.75,4.75])
    ax.set_ylim(ax.get_ylim()[::-1])        # invert the axis
    ax.yaxis.set_ticks([1.25,2.25,3.25,4.25]) # set y-ticks
    ax.yaxis.tick_left()                    # remove right y-Ticks
    ax.set_yticklabels(labels, fontdict=None, minor=False)
    xlims = ax.get_xlim()
    ax.set_xlim([0,xlims[1]])
    
    #for spine in plt.gca().spines.values():
     #   spine.set_visible(False)

    # remove all the ticks
    plt.tick_params(top='off', right='off', labelbottom='on')

In [835]:
def plotKinEvents(df, subj, day, tr, axId):
    # identify this trial
    thisTr = df.loc[(df.Subject == subj) & (df.Day == day) & (df.Trial == tr)]
    ax=plt.gca()
    ylims=ax.get_ylim()

    from itertools import cycle
    lines = ["-","--","-.",":"]
    linecycler = cycle(lines)

    #labels=df.loc[:,'handGun':'shot2'].columns.tolist()
    if thisTr.size > 0:
        xcoords = thisTr.loc[:,'tHandGun':'tShot2'].values
        for xc in xcoords[0]:
            if axId.ndim == 1:
                axId[tr-1].axvline(x=xc, linestyle=next(linecycler), color='k', lw=1)
            else:
                axId[tr-1, day-1].axvline(x=xc, linestyle=next(linecycler), color='k', lw=1)

    
#    #labels=df.loc[:,'handGun':'shot2'].columns.tolist()
#    if thisTr.size > 0:
#        xcoords = thisTr.loc[:,'handGun':'shot2'].values
#        if axId.ndim == 1:
#            axId[tr-1].vlines(x=xcoords, ymin=ylims[1], ymax=ylims[0], color='black', zorder=2, linestyle='--')
#        else:
#            axId[tr-1, day-1].vlines(x=xcoords, ymin=ylims[1], ymax=ylims[0], color='black', zorder=2, linestyle='--')

plot all trials for each day


In [836]:
# plotGazeTimeline(gaze_df, 1, 1, 2, 1)
width = 8
height = 12
sbj = 1

for dy in range(1,4):
    fig, ax = plt.subplots(nrows=10, ncols=1, sharex=True, sharey=True, figsize=(width, height))
    figName = '/Users/jc/Documents/GitHub/shootGaze/figs/s' + str(sbj) + 'day' + str(dy) + 'gaze.png'
    for tr in range(1,11):
        plotGazeTimeline(gaze_df, sbj, dy, tr, ax)
        plotKinEvents(kin_df, sbj, dy, tr, ax)
        
    ax[0].set_title('Day ' + str(dy))
    ax[9].set_xlabel('Time (s)')

    patch1 = mpatches.Patch(color='#d7191c', label='A')
    patch2 = mpatches.Patch(color='#fdae61', label='B')
    patch3 = mpatches.Patch(color='#abdda4', label='C')
    patch4 = mpatches.Patch(color='#2b83ba', label='D')

    plt.legend(handles=[patch1,patch2,patch3,patch4],
               bbox_to_anchor=(0.95, 0.95),
               loc=1, ncol=4,
               bbox_transform=plt.gcf().transFigure,
               fontsize=8)

    # # Fine-tune figure; hide x ticks for top plots and y ticks for right plots
    #plt.setp([a.get_xticklabels() for a in ax[0, :]], visible=False)
    #plt.setp([a.get_yticklabels() for a in ax[:, 1]], visible=False)

    # Tight layout often produces nice results but requires the title to be spaced accordingly
    fig.tight_layout()
    fig.subplots_adjust(top=0.88)

    plt.savefig(figName)
    #plt.show()
    plt.close(fig)

plot all trials for all days for one subject


In [842]:
# plotGazeTimeline(gaze_df, 1, 1, 2, 1)
width = 8
height = 12
fig, ax = plt.subplots(nrows=10, ncols=3, sharex=True, sharey=True, figsize=(width, height))

sbj = 1
figName = '/Users/jc/Documents/GitHub/shootGaze/figs/s' + str(sbj) + 'gaze.png'

for dy in range(1,4):
    for tr in range(1,11):
        plotGazeTimeline(gaze_df, sbj, dy, tr, ax)

ax[0, 0].set_title('Day 1')
ax[0, 1].set_title('Day 2')
ax[0, 2].set_title('Day 3')
ax[9, 0].set_xlabel('Time (s)')
ax[9, 1].set_xlabel('Time (s)')
ax[9, 2].set_xlabel('Time (s)')

patch1 = mpatches.Patch(color='#d7191c', label='A')
patch2 = mpatches.Patch(color='#fdae61', label='B')
patch3 = mpatches.Patch(color='#abdda4', label='C')
patch4 = mpatches.Patch(color='#2b83ba', label='D')

plt.legend(handles=[patch1,patch2,patch3,patch4],
           bbox_to_anchor=(0.95, 0.95),
           loc=1, ncol=4,
           bbox_transform=plt.gcf().transFigure,
           fontsize=8)

# # Fine-tune figure; hide x ticks for top plots and y ticks for right plots
#plt.setp([a.get_xticklabels() for a in ax[0, :]], visible=False)
#plt.setp([a.get_yticklabels() for a in ax[:, 1]], visible=False)

# Tight layout often produces nice results but requires the title to be spaced accordingly
fig.tight_layout()
fig.subplots_adjust(top=0.88)

plt.savefig(figName)
# plt.show()
plt.close(fig)

In [ ]:


In [ ]:


In [ ]: