Demo 7 (PE): Analyzing Keypoint Movement and Kinematics in Pose Estimation Data

Written and last updated September 18, 2025 by Sedona Ewbank, snewbank@stanford.edu

The purpose of this demo is to demonstrate how to measure and plot keypoint travel and ego-centered kinematics in pose estimation data types typically associated with animal experiments (DLC, SLEAP) in MARIPoSA.

import os
import sys
import numpy as np
import pandas as pd
import importlib
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation, FFMpegWriter
from IPython.display import HTML

#This block only important for running as script
#script_dir = os.path.dirname(os.path.abspath(__file__))
mariposa_dir = "/Users/snewbank/PycharmProjects/MARIPoSA"
utils_dir = os.path.join(mariposa_dir, 'utils')
sys.path.append(utils_dir)
sys.path.append(mariposa_dir)

#import utils
from utils import metadata, analyze, plot, simulate

importlib.reload(metadata)
importlib.reload(analyze)
importlib.reload(plot)
importlib.reload(simulate)
<module 'utils.simulate' from '/Users/snewbank/PycharmProjects/MARIPoSA/utils/simulate.py'>
demo_dir="/Users/snewbank/Behavior/MARIPoSA_demo_data/"
config=metadata.load_project(demo_dir+"250910_test_PE/config_PE.yaml")
save=True
save_path = demo_dir+"demo_figures/"
if not os.path.exists(save_path):
    os.makedirs(save_path)

7.1: Measuring and plotting keypoint travel in animals

The purpose of this mode of analysis is to measure how much a keypoint has travelled during a recording, with or without time binning. This could reflect locomotion (e.g., in an open field recording approach) or overall movement of a keypoint in a confined assay (e.g., a reaching task). We obtain this with analyze.get_keypoint_travel, which will return a KeypointTravel object that we can then use for further analysis. Note the threshold parameter of this function, which optionally “cleans” the data; it can be disabled by setting it to None.

help(analyze.get_keypoint_travel)
Help on function get_keypoint_travel in module utils.analyze:

get_keypoint_travel(PE_config, keypoint, start, end, binsize=None, thresh=70, selected_subgroups='all', return_as_df=True)
    Measure keypodint distance travelled across group of subjects
    
    :param PE_config: pose estimation config - used for FPS only
    :param keypoint: name of keypoint
    :param start: start time in seconds
    :param end: end time in seconds
    :param binsize: if binned output is desired, size of timebins (default is None for no binning)
    :param thresh: threshold for when distance 'jump' is too large and should be excluded; default 70
    :param selected_subgroups: subgroups to analyze as defined in config; default "all"
    :param return_as_df: return as a dataframe or return as KeypointTravel class (for embedding, classification)
    :return:
kp_travel = analyze.get_keypoint_travel(config,
                                        "tailbase",
                                        0,1200,
                                        selected_subgroups=["sal","k1","k5","k10"],
                                        return_as_df=False)

kp_travel_df = kp_travel.to_df()
display(kp_travel_df)
plot.plot_keypoint_travel(kp_travel,cmap="viridis_r",plottype="bar")

kp_travel = analyze.get_keypoint_travel(config,
                                        "tailbase",
                                        0,1200,
                                        binsize=60,
                                        selected_subgroups=["sal","k1","k5","k10"],
                                        return_as_df=False)

kp_travel_df = kp_travel.to_df()
display(kp_travel_df)
plot.plot_keypoint_travel(kp_travel,cmap="viridis_r")
0.0 group
2023-06-15_21-28-12_bf04_salivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 76585.673549 sal
2023-06-15_22-01-50_bf03_salivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 77601.985038 sal
2023-06-20_15-41-01_bm03_salivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 21844.904953 sal
2023-06-20_16-13-48_bm04_salivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 46279.020517 sal
2023-06-20_17-21-51_bf02_salivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 69155.547166 sal
2023-07-03_16-15-21_bm02_salivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 60179.625151 sal
2023-07-03_19-20-49_bf01_salivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 74034.204420 sal
2023-06-20_19-10-38_bf04_k1ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 88946.845156 k1
2023-06-26_15-35-45_bf01_k1ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 85649.979937 k1
2023-06-26_19-00-05_bm02_k1ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 92955.598550 k1
2023-06-27_10-47-10_bm04_k1ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 22429.103784 k1
2023-07-08_14-42-24_bf02_k1ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 101134.822510 k1
2023-07-08_15-47-29_bm01_k1ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 70294.549563 k1
2023-06-15_17-16-18_bm01_k5ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 99097.355864 k5
2023-06-26_16-15-58_bf02_k5ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 117117.595734 k5
2023-07-03_20-27-25_bf03_k5ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 174943.106781 k5
2023-07-08_16-19-27_bm02_k5ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 154678.418900 k5
2023-07-14_16-25-47_bf01_k5ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 147305.238791 k5
2023-07-18_19-35-14_bm03_k5ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 121408.153258 k5
2023-05-15_16-47-52_bf03_k10ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 178874.593599 k10
2023-05-15_18-18-22_bf04_k10ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 163772.740151 k10
2023-05-25_14-30-54_bm02_k10ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 146356.555059 k10
2023-05-31_17-08-06_bf01_k10ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 256202.052488 k10
2023-07-13_15-45-26_bm03_k10ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 162133.395129 k10
2023-07-14_13-56-09_bm01_k10ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 140856.419448 k10
2023-07-18_18-19-50_bm04_k10ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 156764.561502 k10
0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 ... 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 group
2023-06-15_21-28-12_bf04_salivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 8880.331395 3351.100258 1587.052168 2750.742715 6399.252095 3460.315582 1016.990993 2570.881432 5468.918087 6059.387430 ... 1104.964260 1500.049482 3898.298124 6544.453556 4779.386395 3759.273735 2990.124814 1878.317699 6675.841174 sal
2023-06-15_22-01-50_bf03_salivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 8071.237357 6181.385243 7522.841487 3543.876735 3620.211232 5351.320313 6009.554638 3952.848740 5135.739960 2218.138347 ... 2122.939566 2672.667907 4010.858900 3015.353875 1996.782716 2114.914108 2257.268367 2296.118505 2669.904618 sal
2023-06-20_15-41-01_bm03_salivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 2660.048643 1383.006506 1059.346533 1503.847833 1535.395087 1002.214252 1054.906797 1108.866013 998.275640 1074.835503 ... 929.709634 910.307825 925.516701 768.447314 859.197324 803.552190 912.919929 705.072034 681.108369 sal
2023-06-20_16-13-48_bm04_salivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 5485.073029 1319.469199 3407.583833 3389.185426 2014.415106 2541.982760 2881.827623 2246.780758 3373.222174 1624.433605 ... 2263.354499 2712.941823 1787.785253 2965.370830 1906.642921 792.458697 1058.076463 1420.924233 1579.055731 sal
2023-06-20_17-21-51_bf02_salivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 7938.195203 6110.713672 5439.324565 4607.947223 3910.104986 3904.234479 3953.831284 4420.781394 2795.601820 2183.251945 ... 1217.639745 1699.270851 1403.652257 5089.822149 4489.189921 1268.897918 1459.432966 1557.146247 3414.117895 sal
2023-07-03_16-15-21_bm02_salivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 7048.375661 5818.277145 5278.729134 6021.514273 4386.361838 4524.004535 2291.139233 1999.318602 4098.064644 1537.187124 ... 3163.314795 1602.690430 1330.173334 1244.696870 1295.699150 1398.134903 1399.431639 1282.853249 1105.019397 sal
2023-07-03_19-20-49_bf01_salivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 8241.668408 4963.881674 5521.215270 3499.214739 3161.170596 5903.910838 3711.337363 2840.721863 4470.272583 4842.918753 ... 2164.682111 2000.644887 1889.692155 3446.230399 3064.598552 2689.005167 2111.795295 2750.955661 1497.574769 sal
2023-06-20_19-10-38_bf04_k1ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 8845.988403 7188.888682 8933.440687 7737.247830 7132.427277 5154.259105 6499.133309 4838.891810 4362.034290 7835.191761 ... 1406.535741 1084.069936 1088.998064 1959.846857 1817.340613 1521.430826 1497.603369 3469.641220 2591.058581 k1
2023-06-26_15-35-45_bf01_k1ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 11838.689804 8031.028128 8091.465003 4083.950757 6227.053949 8353.814333 6247.864234 4675.471830 1606.425168 3986.807139 ... 1737.062475 2822.933304 2976.058980 3062.887893 2957.368912 1436.065023 1788.045139 1733.746204 1242.606624 k1
2023-06-26_19-00-05_bm02_k1ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 20724.839634 10159.438676 6694.307894 5773.045308 5343.981806 3339.687841 2991.561963 4323.656226 4578.547308 1474.808605 ... 1333.393782 2728.749019 1169.581962 2477.143336 4004.994848 2404.780803 3424.785899 4033.969786 4708.006492 k1
2023-06-27_10-47-10_bm04_k1ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 766.578810 5014.383998 2207.256298 1714.152071 574.814660 541.741661 554.249522 614.487656 553.554121 541.568858 ... 528.902945 550.459444 640.761410 1896.750790 1001.235900 1147.860654 1184.954039 1046.978826 828.924569 k1
2023-07-08_14-42-24_bf02_k1ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 9507.050556 7019.303165 5778.522216 5275.720184 4803.188901 5795.852212 5318.264303 5055.511504 5197.850531 6794.038094 ... 4213.263493 3866.977180 5637.361996 3318.199933 2946.646448 5080.904185 2982.917208 4375.070269 3004.641604 k1
2023-07-08_15-47-29_bm01_k1ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 11255.983932 9795.427056 6615.669842 5953.572622 3314.193903 1333.949901 3547.871827 1639.513297 3787.331547 3724.773400 ... 4079.358745 2304.345840 1021.318289 987.000722 818.526623 749.524446 924.008889 3083.640126 2089.798243 k1
2023-06-15_17-16-18_bm01_k5ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 1016.139535 5548.329124 11221.154090 15577.294241 15856.303259 10550.382890 9436.532736 5250.601216 3021.957967 2782.403189 ... 1332.521919 5352.297650 1572.111102 982.383516 938.435036 1005.328986 1043.020094 1489.830168 3975.452915 k5
2023-06-26_16-15-58_bf02_k5ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 14258.394307 12114.657011 9102.119472 11990.647150 9855.363540 7376.524999 7655.121246 7192.705189 5686.557703 4683.214772 ... 2190.526606 3091.146136 1633.545223 1219.133549 2513.966949 3797.668741 5044.993972 822.887972 1084.662132 k5
2023-07-03_20-27-25_bf03_k5ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 18483.803942 19748.594069 13540.708424 13838.397015 13980.386907 12579.660954 11937.358990 10741.868081 10516.169960 6999.421026 ... 4401.776608 6663.845579 4579.081683 6278.084118 4725.643728 2439.890701 2259.074760 1876.957914 1750.146493 k5
2023-07-08_16-19-27_bm02_k5ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 16611.210569 19548.541040 19658.044198 17814.095713 15492.194395 10945.722452 7707.970097 6223.306978 4704.248748 1756.926066 ... 2511.241484 4233.279650 4207.197429 2686.937615 3080.327208 2562.255531 2879.748576 4858.737555 3996.036978 k5
2023-07-14_16-25-47_bf01_k5ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 16263.232968 17443.119375 22803.236161 12280.329026 11375.300372 9175.033094 7663.504950 6023.044032 3156.044494 5384.563438 ... 4284.413765 4759.089020 3107.118211 2045.449778 1671.686457 3081.160518 2499.152466 4010.878733 3482.302331 k5
2023-07-18_19-35-14_bm03_k5ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 3141.386064 8431.135309 11445.512440 15631.544467 13700.836614 11320.928608 10849.105647 9040.392641 6958.009582 9139.713930 ... 1920.523942 2250.660670 3657.893139 1732.058743 1268.457942 1951.751149 979.272468 891.367322 1445.491110 k5
2023-05-15_16-47-52_bf03_k10ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 4540.862776 5761.342404 5972.096308 9212.996149 11265.014433 12343.530581 13109.885729 14265.232545 12622.386646 14532.162772 ... 9880.460142 9848.548262 8212.565308 6903.801921 5536.163377 4185.685251 5688.052096 6323.492953 4504.421440 k10
2023-05-15_18-18-22_bf04_k10ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 1384.384759 1561.798339 5305.881105 6953.295808 11953.262476 15763.644870 17379.785409 16629.811314 16709.346509 15048.740676 ... 7703.680453 5275.320585 7444.790561 5507.504817 1817.069497 1802.525010 2997.591737 5776.201930 2934.882903 k10
2023-05-25_14-30-54_bm02_k10ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 1667.979021 1379.497290 1663.627196 3107.784112 4318.061746 7187.792082 9062.464735 10455.246228 14283.478704 11130.539709 ... 13852.193039 14019.179637 11930.154609 6806.428533 3768.340484 6354.961249 5443.839714 2999.410224 1938.707364 k10
2023-05-31_17-08-06_bf01_k10ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 11082.830746 15343.322621 18279.907887 17224.223559 23570.051169 18951.071018 16536.410407 14840.127977 15261.406690 16009.304633 ... 11787.589473 10134.122145 8567.505371 10117.137028 8937.780320 6922.975225 5773.762636 5015.415782 7104.784164 k10
2023-07-13_15-45-26_bm03_k10ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 3318.773423 4584.927267 6942.028511 10606.361780 13405.089466 14297.165685 19119.371373 18978.367552 17304.071745 11322.589042 ... 8377.923276 4126.843001 7022.605035 3799.446167 6055.594219 3491.997983 1980.469115 1970.568348 1237.739002 k10
2023-07-14_13-56-09_bm01_k10ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 1094.347692 821.769350 947.428873 995.894179 878.307377 850.194499 800.261095 2530.965888 3584.427386 6113.109571 ... 13742.433401 9811.396039 15422.909725 16949.204517 9715.071936 8837.586482 8954.368748 12788.630333 10803.153588 k10
2023-07-18_18-19-50_bm04_k10ivDLC_resnet101_DevRats2Jul21shuffle1_500000.csv 977.064278 792.373003 2147.504600 4129.242798 5808.968030 7137.626090 11072.575732 9573.996864 10198.357200 11214.199924 ... 15963.185520 12328.413572 11840.693869 10454.337927 9633.147720 8050.751995 3756.558807 3974.695994 4693.074698 k10

26 rows × 21 columns

../_images/386a06529ba2abad61a5376c45c2f73027f38da2a93fcf82630669dc8964272f.png ../_images/c8a8c4992bef3d9b28f08bdce8e3a6ec1b7c7725fd63dba1bc2ef31487333a33.png ../_images/386a06529ba2abad61a5376c45c2f73027f38da2a93fcf82630669dc8964272f.png

7.2 Using keypoint travel in animals for embedding and classification

The KeypointTravel class has many of the same attributes and use cases as pose segmentation data object classes (ModuleUsage and ModuleTransitions), and thus we can use the same embedding, classification, and regression functions as we do for pose segmentation data. It is worth noting that travel of a single keypoint only has sufficient dimensionality for some methods (e.g., PCA) if time binning is applied, otherwise it is a point value rather than a time series. We can start by looking at embedding the data with PCA or LDA.

pca_emb = analyze.embed(kp_travel,method="pca")
plot.plot_embeddings(kp_travel,pca_emb)
lda_emb = analyze.embed(kp_travel,method="lda")
plot.plot_embeddings(kp_travel,lda_emb)
Ellipse only drawn for embeddings_object of class LDA, not <class 'sklearn.decomposition._pca.PCA'>
../_images/7f4f7c35e664d4aac79b371e737c670ad55c87026ba71d05e08260fba9100087.png ../_images/30f8933b91af6eb6359e6d83781500963a110c1e9a61bc857558b36336238762.png ../_images/7f4f7c35e664d4aac79b371e737c670ad55c87026ba71d05e08260fba9100087.png

Next, we can look at classification with logistic regression - here shown with training a classifier on the full dataset and leave-one-out cross-validation to test performance on a subset of samples.

clf = analyze.classify(kp_travel,method="LogisticRegression")
accuracy, conf_mat = analyze.loocv(kp_travel,method="LogisticRegression")
plt.figure(figsize=(3,3),dpi=300)
plt.imshow(conf_mat,cmap="Greens")
plt.xticks([0,1,2,3],labels=kp_travel.group_dict.keys())
plt.yticks([0,1,2,3],labels=kp_travel.group_dict.keys())
plt.title("Confusion Matrix")
plt.colorbar()
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
/opt/anaconda3/envs/mariposa/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
<matplotlib.colorbar.Colorbar at 0x7fc448b17490>
../_images/19c1dd9a9f83478cb3b154fba9a4a62d9f9fd34d0886866c4db28518e3c35aa9.png

And now we can take a look at using regression on a KeypointTravel class object.

# Training regression model and doing LOOCV
dose_dict={"sal":0,"k1":1,"k5":5,"k10":10}
reg, dose_labels = analyze.regress(kp_travel,dose_dict)
y_preds, sq_err = analyze.loocv_regression(kp_travel,dose_dict)

# Plotting results for held-out samples
plt.figure(figsize=(3,3),dpi=300)
cmap = "viridis_r"
cmap = plt.get_cmap(cmap)
colors = [cmap([i/4]) for i in range(4)]
for d,dose in enumerate(dose_dict.keys()):
    x = dose_dict[dose]
    mask = np.array(kp_travel.group_labels)==kp_travel.group_dict[dose]
    m = np.mean(y_preds[mask])
    sd = np.std(y_preds[mask])
    plt.errorbar(x,m,yerr=sd,linestyle="none",marker="o",color=colors[d],capsize=2)
plt.xlabel("Real dose (mg/kg)")
plt.ylabel("Predicted dose (mg/kg)")
Text(0, 0.5, 'Predicted dose (mg/kg)')
../_images/5e2680464b329eb1a417b3b2f5fc27351a5d8a6feacddf63e280f151f13f499b.png

7.3: Measuring and using ego-centered kinematics from pose estimation data

Travel of a single keypoint of interest can be useful for measuring overall movement of the animal or limb, depending on the experimental setup; however, additional insight can be gained by ego-centering the data (i.e., using two keypoints to establish the origin and axis along which to reorient all points to) and evaluating the angle, location, and movement of keypoints relative to the new orientation. Ego-centering is acheived in MARIPoSA with analyze.ego_center(), which may be useful to know if you want to save the ego-centered data for some reason. Below is the documentation for analyze.ego_center and a movie plotting the original and ego-centered data.

help(analyze.ego_center)
Help on function ego_center in module utils.analyze:

ego_center(config, data, keypoint_ego1, keypoint_ego2)
    Ego centering a single pose estimation dataframe
    
    :param config: config
    :param data: pose estimation pandas dataframe in DLC format
    :param keypoint_ego1: first keypoint to use for establishing egocentric alignment axis
    :param keypoint_ego2: second keypoint to use for establishing egocentric alignment axis
    :return: ego_centered pandas dataframe
data = pd.read_csv(config["data_directory"]+config["project_files"][0],header=[1,2])
data_reoriented = analyze.ego_center(config, data, "tailbase", "nose")

fig, ax = plt.subplots(figsize=(5, 5))

def update(fr):
    ax.clear()
    x = data.xs("x", level=1, axis=1).loc[fr]
    y = data.xs("y", level=1, axis=1).loc[fr]
    x_ego = data_reoriented.xs("x", level=1, axis=1).loc[fr]
    y_ego = data_reoriented.xs("y", level=1, axis=1).loc[fr]
    
    ax.scatter(x, y, color="gray", s=4, label="Original")
    ax.scatter(x_ego, y_ego, color="C0", s=4, label="Ego-centered")
    ax.set_xlim([-200, 1080])
    ax.set_ylim([-200, 1080])
    ax.set_title(f"Frame {fr}")
    ax.legend(loc="upper right")

nframes=300
ani = FuncAnimation(fig, update, frames=nframes, interval=50)
HTML(ani.to_jshtml())
../_images/706b371de01cd3ecec8abb99f215afb704ecb06403b3db2f47ba5a15e74ad2e8.png

Ego-centering is incorporated into the function analyze.get_keypoint_kinematics, which can get the angle, distance, or travel/movement of keypoints relative to the ego-center.

help(analyze.get_keypoint_kinematics)
Help on function get_keypoint_kinematics in module utils.analyze:

get_keypoint_kinematics(PE_config, keypoint_ego1, keypoint_ego2, start, end, binsize=None, metric='angle', thresh=70, selected_subgroups='all', return_as_df=True, verbose=False)
    Measure keypoint kinematics (egocentrically aligned angle, distance, and travel of keypoints) across group of subjects
    
    :param PE_config: pose estimation config - used for FPS only
    :param keypoint1: first keypoint to use for establishing egocentric alignment axis
    :param keypoint2: second keypoint to use for establishing egocentric alignment axis
    :param start: start time in seconds
    :param end: end time in seconds
    :param binsize: if binned output is desired, size of timebins (default is None for no binning)
    :param metric: metric to assess for all non-keypoint1/2 keypoints; options are "angle_m" (for mean angle), "angle_sd" (for std deviation of angle), "distance_m" (for mean distance of keypoints to ego-center), "distance_sd" (for std deviation of distance of keypoints to ego-center), or "travel" (for movement of keypoints relative to ego-center)
    :param thresh: threshold for when distance 'jump' is too large and should be excluded; default 70
    :param selected_subgroups: subgroups to analyze as defined in config; default "all"
    :param return_as_df: return as a dataframe or return as KeypointTravel class (for embedding, classification)
    :return:
# Get the kinematic data - travel of keypoints relative to egocentric axis
travel_kin = analyze.get_keypoint_kinematics(config,
                                             "tailbase",
                                             "nose",
                                             0, 1200,
                                             metric='travel',
                                             selected_subgroups=['sal', 'k1', 'k5', 'k10'],
                                             return_as_df=False)

# plot the ego-centered travel data
plot.plot_keypoint_kinematics(config,travel_kin, figH=5,figW=8,style="bar_error",cmap="viridis_r")
plt.title("Ego-centered Keypoint Travel")

# use the data for embedding and classification
emb = analyze.embed(travel_kin)
plot.plot_embeddings(travel_kin,emb)
plt.title("LDA of Ego-centered Keypoint Travel")
plt.figure(figsize=(2.5,2.5),dpi=300)
plt.title("Ego-centered Keypoint Travel\nConfusion Matrix")
acc, cm = analyze.loocv(travel_kin)
plt.imshow(cm,cmap="Greens")
ticklabels=['sal','k1','k5','k10']
plt.xticks(range(len(ticklabels)),labels=ticklabels,rotation=90)
plt.yticks(range(len(ticklabels)),labels=ticklabels)
print(f"Accuracy: {acc}")
Accuracy: 0.23076923076923078
../_images/989699f359570a91a5ca60fc3f8c4a779f9c0d454cf2745c16840721ac5f5cb3.png ../_images/051f38f20dce1cf86ee7dd47a07d5bdc443919b50b3c9b9c4eb5b7ddfff52709.png ../_images/1c31b2b49ef727391d4aee4ac02ed848bd55ee7a9ce17c9bf512864a1be5e068.png
# Get the kinematic data - angle of keypoints relative to egocentric axis
angle_kin = analyze.get_keypoint_kinematics(config,
                                             "tailbase",
                                             "nose",
                                             0, 1200,
                                             metric='angle_sd',
                                             selected_subgroups=['sal', 'k1', 'k5', 'k10'],
                                             return_as_df=False)

# plot the kinematic data
plot.plot_keypoint_kinematics(config,angle_kin, figH=5,figW=8,style="bar_error",cmap="viridis_r")
plt.title("Ego-centered Keypoint Angle")

# use the data for embedding and classification
emb = analyze.embed(angle_kin)
plot.plot_embeddings(angle_kin,emb)
plt.title("LDA of Ego-centered Keypoint Angle")
plt.figure(figsize=(2.5,2.5),dpi=300)
plt.title("Ego-centered Keypoint Angle\nConfusion Matrix")
acc, cm = analyze.loocv(angle_kin)
plt.imshow(cm,cmap="Greens")
ticklabels=['sal','k1','k5','k10']
plt.xticks(range(len(ticklabels)),labels=ticklabels,rotation=90)
plt.yticks(range(len(ticklabels)),labels=ticklabels)
print(f"Accuracy: {acc}")
Accuracy: 0.2692307692307692
../_images/5f717415e10a00f29338599e43390a612df214ff6ec9a4a8470cc8e6218dd328.png ../_images/890d95baca84af001f5bdf843d2b6fc41dddcdb3d57c45536f933e4968fa9a79.png ../_images/46ca227758319a2974e1381666a6031467aa18587b9c02db507dbd4f017747d7.png