Working with trajectory data in python¶
This notebook shows some examples of loading and working with anTraX results in python. The notebook can be downloaded from https://github.com/Social-Evolution-and-Behavior/anTraX/blob/master/docs/analysis_nb.ipynb.
%load_ext autoreload
%autoreload 2
%matplotlib inline
from antrax import *
from matplotlib import pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
idx = pd.IndexSlice
Load data from a tracked experiment¶
We will use data from dataset J16. Replace the path to the correct path on your system. By default, data from all IDs ('ants') and all movies will be loaded, but it can be changed using the arguments.
expdir = '/Users/asaf/Box Sync/anTraX/datasets2upload/J16'
ex = axExperiment(expdir, session='antrax_demo')
ad = axAntData(ex, movlist=[1], antlist=['GO','PP'])
The data is loaded into the antdata object, and is stored in the pandas table antdata.data. By default, the values of the xy coordinates, the orientation angle, and the assignment type are loaded:
ad.data.head()
The 'ass_type' column indicates the type of assigment:
(1) single-animal tracklet assigned by the blob classifier
(2) single-animal tracklet assigned by the propagation algorithm
(3) multi-animal tracklet assigned by the propagation algorithm
(4) soft assigment (tracklet that is the only possible one for that ID, but was not assigned by the propagation)
(5) interpolation of coordinates, not corresponding to an actual blob
(NaN) no coordinates
See the anTraX publication for more details.
Plot the trajectories of two ants, with the background image as background¶
fig, ax = plt.subplots(1, 1, figsize=(10 ,10))
# load the background image
bg = ex.get_bg()
# get the scale parameter to convert real world units to pixels
scale = ex.prmtrs['geometry_rscale']
# show the image
ax.imshow(bg)
# plot trajectories on image
for ant in ['PP', 'GO']:
x = ad.data.loc[:, idx[ant, 'x']]/scale
y = ad.data.loc[:, idx[ant, 'y']]/scale
ax.plot(x, y, alpha=0.75)
# hide axis
ax.axis('off');
Plot the trajectories of all ants¶
# reload, this time with all ants
ad = axAntData(ex, movlist=[1])
# now plot (note the use the negative of y to have
# the same axis orientation as the images)
fig, axs = plt.subplots(4, 4, figsize=(16, 16))
axs = axs.flatten()
for i, ant in enumerate(ex.antlist):
axs[i].plot(ad.data.loc[:, idx[ant, 'x']], -ad.data.loc[:, idx[ant,'y']],
color='grey', linewidth=1)
axs[i].set_xlim((0.01, 0.08))
axs[i].set_ylim((-0.07, 0))
axs[i].axis('off')
Calculating some measures¶
Define the nest location as the median location of all ants. This is possible because in this experiment, the majority of the ants are in the nest for the majority of the time. To account for periods when many ants leave the nest for a short time, we will use median smoothing:
# calculating proxi for nest location
ad.data[('nest', 'x')] = \
ad.data.loc[:, idx[:, 'x']].interpolate(limit_area='inside').median(axis=1)
ad.data[('nest', 'y')] = \
ad.data.loc[:, idx[:, 'y']].interpolate(limit_area='inside').median(axis=1)
# temporal smoothing for consistency
window = 3001 # 5 minute in frames
ad.data[('nest', 'x')] = \
ad.data[('nest', 'x')].rolling(window, center=True).median()
ad.data[('nest', 'y')] = \
ad.data[('nest', 'y')].rolling(window, center=True).median()
# for each ant, add a column which is True if the ant is
# outside the nest (distance above threshold)
thresh = 0.01 # in meters
for ant in ad.antlist:
dx = ad.data[ant]['x'] - ad.data['nest']['x']
dy = ad.data[ant]['y'] - ad.data['nest']['y']
ad.data[(ant, 'dnest')] = np.sqrt(dx**2 + dy**2)
ad.data[(ant, 'outside')] = ad.data[(ant, 'dnest')] > thresh
Plot the nest trajectory on top of one frame from the experiment
fig, ax = plt.subplots(1, 1, figsize=(10 ,10))
# load the first frame in the movie
frame = ex.get_frame(1)
# get the scale parameter to convert real world units to pixels
scale = ex.prmtrs['geometry_rscale']
# show the image
ax.imshow(frame)
# plot nest trajectory on image
x = ad.data.loc[:,idx['nest','x']]/scale
y = ad.data.loc[:,idx['nest','y']]/scale
ax.plot(x, y, alpha=0.75, color='magenta', linewidth=3)
# hide axis
ax.axis('off');
Add velocity column to data table
for ant in ad.antlist:
dx = ad.data[ant]['x'].diff()
dy = ad.data[ant]['y'].diff()
ad.data[(ant, 'v')] = np.sqrt(dx**2 + dy**2)
Plot velocity histogram when outside of the nest
fig, ax = plt.subplots(1, 1, figsize=(10,6))
for ant in ['PP', 'GO']:
v = ad.data[ant]['v']
v = v[ad.data[ant]['outside']]
v = v[~np.isnan(v)]
sns.distplot(1000*v, label=ant)
ax.set_xlim((0, 2))
ax.set_ylim((0, 5));
ax.set_xlabel('Velocity (mm/s)', fontsize=20)
ax.set_ylabel('pdf', fontsize=20)
ax.legend(fontsize=20);
Loading JAABA scores¶
This is an example of loading tracks together with JAABA classifications (in case JAABA analysis was done using the anTraX interface). We will use the A36 dataset, which comes with some JAABA classifications (see anTraX publication figure 5).
expdir = '/Users/asaf/Box Sync/anTraX/datasets/A36'
ex = axExperiment(expdir, session='antrax')
ant = 'YY'
ad = axAntData(ex, movlist=[1], antlist=[ant])
ad.set_jaaba(behaviors=['WalkWithLarva'])
ad.data.head()
fig, ax = plt.subplots(1, 1, figsize=(10 ,10))
# load the background image
bg = ex.get_bg()
# get the scale parameter to convert real world units to pixels
scale = ex.prmtrs['geometry_rscale']
# show the image
ax.imshow(bg)
# plot trajectories on image
x = ad.data.loc[:, idx['YY', 'x']]/scale
y = ad.data.loc[:, idx['YY', 'y']]/scale
ax.plot(x, y, alpha=0.5, color='dimgrey')
# plot the part of the trajectory classified as WalkWithLarva
ix = ad.data.loc[:, idx['YY', 'scores_WalkWithLarva']] <= 0
x[ix] = np.nan
y[ix] = np.nan
ax.plot(x, y, alpha=0.5, color='indianred', linewidth=2)
# hide axis
ax.axis('off');
Loading DeepLabCut tracking results¶
This is an example of loading tracks together with DeepLabCut pose tracking results (see the corresponding section). We will use the A36 dataset, which comes with some DLC tracking results (see anTraX publication figure 5). Loading DLC results might take a bit of time.
expdir = '/Users/asaf/Box Sync/anTraX/datasets/A36'
ant = 'BG'
ex = axExperiment(expdir, session='antrax')
ad = axAntData(ex, movlist=[1], antlist=[ant])
ad.set_dlc(dlcproject='ant_walk')
ad.data.head()
# DLC was run only on frames inw which the ant was assigned to single-ant tracklets
# all the rest were asssigned NaN, so we need first to get a list of these frames
dlc_frames = ad.data.index[~np.isnan(ad.data[ant]['Head_x'])]
# now lets just pick one
f = dlc_frames[100]
# get the ant image
# note that this will work only for frames in which the tracklet is single ant
image = ad.get_image(ant, f)
# get the correct row
row = ad.data.loc[f,idx[ant,:]].droplevel(axis=0,level=0)
fig, ax = plt.subplots(1, 1, figsize=(10 ,10))
# show image
plt.imshow(image, origin='lower')
# plot body axis
ax.plot([row['Head_x'],row['Neck_x'],row['ThxAbd_x'],row['Tail_x']],
[row['Head_y'],row['Neck_y'],row['ThxAbd_y'],row['Tail_y']],
'-', linewidth=10, alpha=0.5, color='deeppink')
# plot antennae
ax.plot([row['L_ant_root_x'],row['L_ant_tip_x']],
[row['L_ant_root_y'],row['L_ant_tip_y']],
'-o', linewidth=10, alpha=0.5, color='coral')
ax.plot([row['R_ant_root_x'],row['R_ant_tip_x']],
[row['R_ant_root_y'],row['R_ant_tip_y']],
'-o', linewidth=10, alpha=0.5, color='coral')
ax.axis('off');