{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "\n", "from pathlib import Path\n", "from collections import namedtuple\n", "import numpy as np\n", "import scipy.stats as sstats\n", "import matplotlib.pyplot as plt\n", "import h5py" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "datafile = Path(\"analyzed-data.h5\")" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "logfile = \"stats.md\"\n", "\n", "with open(logfile, \"w\") as out:\n", " print(f\"# Statistics\\n\", file=out)\n", "\n", "def log(msg):\n", " with open(logfile, \"a\") as out:\n", " print(msg, file=out)\n", " print(msg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Parse single video results" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "data = h5py.File(str(datafile), \"r\")" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['001',\n", " '002',\n", " '003',\n", " '004',\n", " '005',\n", " '006',\n", " '007',\n", " '008',\n", " '009',\n", " '010',\n", " '011',\n", " '012',\n", " '013',\n", " '014',\n", " '015',\n", " '016',\n", " '017',\n", " '018',\n", " '019',\n", " '020',\n", " '021',\n", " '022',\n", " '023',\n", " '024',\n", " '025',\n", " '026',\n", " '027',\n", " '028',\n", " '029',\n", " '030']" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[k for k in data.keys()]" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "video = data[\"001\"]" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['conditional', 'densities', 'evaluation', 'pose']" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[k for k in video.keys()]" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "['expression=coords.Tip2 < 300',\n", " 'px_per_mm=8.4',\n", " 'run=151428',\n", " 'session=2020-09-11-test',\n", " 'subject=MLA-041630']" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[f\"{k}={v}\" for k, v in video.attrs.items()]" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "cond = video[\"conditional\"]" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['positions', 'probability', 'sigmoid']" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[k for k in cond.keys()]" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['cutoff_density_count=3',\n", " \"description='conditional probability of the real-time trigger at the given post-hoc position-values.'\",\n", " 'original_target_position_px=300']" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[f\"{k}={repr(v)}\" for k, v in cond.attrs.items()]" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[\"description='the results of the sigmoid fitting to the conditional probability distribution.'\",\n", " 'mean=307.90606875565913',\n", " 'sign=-1',\n", " 'std=18.217868298583625']" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sig = cond[\"sigmoid\"]\n", "[f\"{k}={repr(v)}\" for k, v in sig.attrs.items()]" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "diff = np.array(video[\"evaluation/realtime\"]) - np.array(video[\"evaluation/posthoc\"])" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(11.651634052017078, 32.435388738211955)" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.abs(diff).mean(), diff.std(ddof=1)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Tip1', 'Tip2', 'Tip3', 'description', 'threshold_mm', 'unit']" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[k for k in video[\"pose/realtime/jump\"].attrs.keys()]" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "class TargetStats(namedtuple(\"Stats\", (\"original\", \"pos_error\", \"sign\", \"mean\", \"std\",\n", " \"subject\", \"session\", \n", " \"Tip1diff\", \"Tip2diff\", \"Tip3diff\",\n", " \"Tip1jump\", \"Tip2jump\", \"Tip3jump\"))):\n", " @classmethod\n", " def from_video(cls, video):\n", " target = video[\"conditional\"].attrs[\"original_target_position_px\"]\n", " error = np.abs(np.array(video[\"evaluation/realtime\"]) - np.array(video[\"evaluation/posthoc\"])).mean()\n", " sig = video[\"conditional/sigmoid\"]\n", " mean = sig.attrs[\"mean\"]\n", " std = sig.attrs[\"std\"]\n", " sign = sig.attrs[\"sign\"]\n", " scale = video.attrs[\"px_per_mm\"]\n", " \n", " info = [video.attrs[\"subject\"], video.attrs[\"session\"]]\n", " diffs = []\n", " jumps = []\n", " for attr in (\"Tip1\", \"Tip2\", \"Tip3\"):\n", " diff = np.abs(np.array(video[f\"pose/realtime/{attr}\"]) - np.array(video[f\"pose/posthoc/{attr}\"])).mean()\n", " diffs.append(diff / scale)\n", " jumps.append(video[f\"pose/realtime/jump\"].attrs[attr])\n", " return cls(target/scale, error/scale, sign, mean/scale, std/scale, *(info + diffs + jumps))\n", " \n", " @classmethod\n", " def merge(cls, values):\n", " return cls(*[np.array([getattr(v, attr) for v in values]) for attr in cls._fields])" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "TargetStats(original=35.714285714285715, pos_error=1.387099291906795, sign=-1, mean=36.6554843756737, std=2.1687938450694793, subject='MLA-041630', session='2020-09-11-test', Tip1diff=1.0850558756616173, Tip2diff=1.387099291906795, Tip3diff=1.6605526471279732, Tip1jump=0.887719298245614, Tip2jump=0.9374800637958532, Tip3jump=0.8590111642743221)" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "TargetStats.from_video(video)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "data.close()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Summarize all videos" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "with h5py.File(str(datafile), \"r\") as src:\n", " results = TargetStats.merge([TargetStats.from_video(src[videoindex]) for videoindex in src.keys()])" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "TargetStats(original=array([35.71428571, 35.71428571, 23.80952381, 23.80952381, 23.80952381,\n", " 29.76190476, 23.80952381, 23.80952381, 9.52380952, 35.71428571,\n", " 30.76923077, 14.42307692, 31.73076923, 36.53846154, 14.42307692,\n", " 33.65384615, 14.42307692, 15.38461538, 26.92307692, 31.73076923,\n", " 33.65384615, 17.30769231, 14.42307692, 28.84615385, 30.76923077,\n", " 30.76923077, 32.69230769, 17.30769231, 19.23076923, 28.84615385]), pos_error=array([ 1.38709929, 1.06794939, 3.65677357, 13.80664483, 8.2894077 ,\n", " 0.92417927, 3.61634059, 3.80566875, 3.50238693, 3.46352231,\n", " 0.64711828, 0.71464402, 0.6171724 , 0.27023297, 1.31481593,\n", " 0.80025092, 0.7988079 , 1.21964605, 0.30992211, 0.37613891,\n", " 0.54170281, 1.70370626, 1.94817381, 0.51719482, 0.91036887,\n", " 0.27187247, 0.4841196 , 0.75321826, 0.78217209, 0.49433082]), sign=array([-1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1,\n", " 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1]), mean=array([36.65548438, 36.21980606, 24.5266196 , 32.2727805 , 26.31913332,\n", " 29.15530482, 24.61903465, 23.95391535, 6.69954191, 35.56412505,\n", " 30.20531515, 16.86120614, 31.35356647, 36.32492285, 14.89796694,\n", " 33.54886264, 16.72477019, 17.0824827 , 26.6752408 , 31.4076078 ,\n", " 33.54349722, 17.94487416, 15.04820687, 28.43807316, 29.95534036,\n", " 30.5637455 , 32.48861576, 17.45516573, 19.68250535, 28.93040827]), std=array([2.16879385, 1.4787597 , 1.03687526, 7.76585626, 3.01841493,\n", " 0.59420189, 1.12171502, 0.91364757, 7.6338 , 0.58761095,\n", " 0.48997848, 3.73872121, 0.51949183, 0.45309109, 1.01733642,\n", " 0.51188606, 2.6740836 , 1.58311338, 0.53606018, 0.50494755,\n", " 0.52821948, 1.87430294, 1.75285262, 0.60947669, 1.65641202,\n", " 0.54713395, 0.54995621, 1.1353144 , 0.97467438, 0.80658194]), subject=array(['MLA-041630', 'MLA-041630', 'MLA-041630', 'MLA-041630',\n", " 'MLA-041630', 'S005-19', 'S005-19', 'S006-19', 'S006-19',\n", " 'S006-19', 'SNA-079258', 'SNA-079258', 'SNA-079258', 'SNA-079258',\n", " 'SNA-079258', 'SNA-079258', 'SNA-079258', 'SNA-079258',\n", " 'SNA-079258', 'SNA-079259', 'SNA-079259', 'SNA-079259',\n", " 'SNA-079259', 'SNA-079259', 'SNA-079260', 'SNA-079260',\n", " 'SNA-079260', 'SNA-079260', 'SNA-079260', 'SNA-079260'],\n", " dtype='" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "_ = plt.hist(error_merged, bins=80, range=(0, 20))\n", "_ = plt.xlim(0, 12)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(27, 21)" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "threshold = 2\n", "tip1_good = results.Tip1diff < threshold\n", "tip2_good = results.Tip2diff < threshold\n", "tip3_good = results.Tip3diff < threshold\n", "\n", "position_good = tip2_good\n", "spread_good = np.logical_and(tip1_good, tip3_good)\n", "\n", "np.count_nonzero(position_good), np.count_nonzero(spread_good)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "## Estimation errors (real-time vs post-hoc, by part; non-filtered)\n", "\n", "N=30 videos (7 sessions from 6 subjects).\n", "\n", "- Tip1: 1.7361±2.5146 mm, min 0.1522 mm, max 11.4955 mm, median 0.7247 mm, 25% 0.3246 mm, 75% 2.2450 mm\n", "- Tip2: 1.0142±0.9606 mm, min 0.2702 mm, max 4.0401 mm, median 0.7237 mm, 25% 0.4867 mm, 75% 1.0177 mm\n", "- Tip3: 1.4132±0.8350 mm, min 0.3512 mm, max 3.8823 mm, median 1.3191 mm, 25% 0.7531 mm, 75% 1.8012 mm\n", "- Tip1 vs Tip2: p=0.23593998, NS (Wilcoxon's signed-rank test with Bonferroni correction)\n", "- Tip2 vs Tip3: p=0.00359201** (Wilcoxon's signed-rank test with Bonferroni correction)\n", "- Tip1 vs Tip3: p=1.28529009, NS (Wilcoxon's signed-rank test with Bonferroni correction)\n", "\n" ] }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAALoAAADbCAYAAADaposOAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8vihELAAAACXBIWXMAAAsTAAALEwEAmpwYAAAR8UlEQVR4nO3dfZAU9ZnA8e/DLAKGiDHcagARiZZAGCHDmiNeVALnW3LW1l1FrfI4I9yyeBIISFKnJhGiEr1Dg1AxCbgoe1Ekd5bnap3ZUPHkDpMqrXVUBuXFAKunIC4qq4ugOD73R/du7c6+Tc9OT3dPP5+qqaF7Znse12d/8+vfq6gqxpS7QUEHYEwpWKKbWLBEN7FgiW5iwRLdxIIluomFskn0yy67TAF72KNHZZPohw4dCjoEE2Jlk+jG9MUS3cSCJbqJBUv0CBIRli5d2nF89913s3z5cgB27drFjBkzmDp1KhMnTqS2tjagKMPFEj2ChgwZwmOPPdbjDfiiRYtYsmQJL730Ejt27GDhwoUBRBg+lugRVFFRQW1tLatWrer22oEDBxgzZkzHcTKZLGVooWWJHlELFizg4YcfprW1tcv5JUuWMHPmTC6//HJWrVrF4cOHgwkwZCzRI+qkk07i2muvZc2aNV3Oz5kzhx07dnDllVeyZcsWpk+fzscffxxQlOFhiR5hixcvZv369Rw5cqTL+VGjRjF37lwaGhqoqKhg+/btAUUYHpboEXbKKadw1VVXsX79+o5zjY2NHD9+HIC3336bd999l9GjRwcVYmhYokfc0qVLu7S+bN68mcmTJzNlyhQuvfRSVq5cyWmnnRZghOEg5TJntKqqSpuamoIOwwRPejppJbqJhYqgAwi7u+66i2PHjgUdRtkZOnQoN910U8k+zxK9H8eOHevoXjfFU+rfqVVd+jFu3LigQyhLpf692s2oKTd2M2rys2HDBrZs2dLl3PLly2lubg4knmKwOrrpsHbtWiorKwFQVdatW0dLSwsXXnghAJ9++ikrVqxg1qxZTJ8+PchQPbMS3XSYO3cue/bs4d577+WWW25h0KBBLF68mMbGRjZt2sT111/PpEmTIpfkYIlucohIx3Mikej1XNRY1cV0eOCBBzjzzDNZvHgxZ5xxBrt372b16tVccsklVFRUMHv2bDZt2kRlZWX0SnVVLYvHtGnTNGwAvfHGGzuOV65cqcuWLVNV1Z07d+pFF12kU6ZM0QkTJui8efMCirK7Bx98UJ955pku55YtW6b79u0LJB6PeswPK9F91D7l7eabb2bkyJFdXmuf8lZdXQ1AJpMZ8OcVsxe3ubm5W8vLhg0binJtsJ7RstJ5ytuKFSu6vObHlLco9eJaz2iZKeWUtyj14lrPaIHC2DM6fPhw2trauPXWWxk8eDDDhg2jra2tozTbv38/jY2NNDQ0sGvXLl5++WWGDBkSbNDRZz2jQbEpb8GzRC8Bm/IWPEv0ErEpb8EqaR1dRC4EfgBMA0YBc1R1g/vaYOAO4HLgy8AHwDPATar6Rn/XDmMd3QQiFHX04cB24PvA0ZzXTgRSwAr3uRo4HWgUEWsGNQNS0gRS1aeApwBEZEPOa63AxZ3Pich84BVgIjDwHpUCRGkqXak7YaIk7CXlSe7z+0EFYJ0w5SG0N6MicgJwD/Ckqr4ZdDwm2kKZ6G6d/CHgZGBOH++rFZEmEWlqaWnxJRbrbSwPgfWMikgb8L32VpdO5yuAR4AkMENV387netbqYlw9trqEqo7uNjFuAibjIcmN6U9JE11EhgNnuYeDgLEiMhV4D9gP/AdwHnAFoCLS3oPSqqq5zZHG5K3UdfQq4EX3MQz4qfvv24AxOG3no4AXgAOdHleXOE7AGSNeX19flLHiJlilbkffQi91KFdfr5VUJpOhpqaGbDZLIpGgrq7OtkmJsFC2uoRBOp0mm80CkM1mSafTAUdkBsISvRepVKpjxnsikSCVSgUckRmIULW6hEkymaSuro50Ok0qlbJqS8RZovchmUxagpcJq7qYWLBEN7FgiW5iwRLdxIIluokFS3QTC5boJhYs0U0s5N1hJCJDga8D43BGHrYAaVXd409oxhRPv4kuIn+FszzFFcBgoBVnqYpTgCEishdYB/xaVT/0MVZjCtZn1UVEGoB/B14HLgE+r6pfVNUxqnoicDbOokOzgN0icnHvVzMmOP2V6JuBK1X1k55eVNW9wF6gXkS+gjNpwpjQ6TPRVfW+fC+kqq/gLDZkTOgUNHrRvTHtUu1R1Y+KEpExPsi7eVFEzhCRBhH5ADgCfJjzKLlSrOtiykPe67qIyFZgKPAL4CDQ5QdV9fdFj84DW9fFuAa8rstXgfNUdUdx4jGmdLz0jL4M/IVfgRjjJy8lei2wRkTW4Kxxfrzzi/ks1m9MULwk+iCgEvhPutbPxT2O5ibxJha8JHo9zviWK+jhZtSYMPOS6BOAqaq6269gjPGLl5vR54Ez/QrEGD95KdF/BdwrIvfg7CeUezNqa7aZ0PKS6I+4z+t6eM1uRk2oeUl0q7aYyMo70VX1dT8DMcZPnkYvujtQnI/Tnp47evGXRYzLmKLyMmd0NlCH00H0Pl3b0RWwRDeh5aVEXwH8K3Cbqn7qUzzG+MJLO/pJwAZLchNFXhL9YeDbfgVijJ+8VF1uBB4XkVn03GF0WzEDM6aYvCT6fOAy4BDOXqG5N6OW6Ca0vFRdfgIsVdVKVZ2sqslOj3OLEYyIJETkdhHZJyLH3Oc73G3TjSmYlwRKAE/4FYjrn4EFwHdxqkfn4gwP/hi43efPNmXMS4n+IPD3fgXiOh94UlWfVNVmVX0C54/rL33+XFPmvJToJwI1InIpsI3uN6OLihDPs8ANIjJBVXeKyCRgJnBnEa5tYsxLok8EXnT/PSHntWLNNvoX4PPAqyKSxYlvhQ0vMAPlZVDXN/0MxHU1cC1wDc7ydlOB1SKyT1XX575ZRGpxJm0zduzYEoRnoirvBYxKQUT+D7hbVVd3Ovdj4DpVPauvn/VjAaNMJmM7R0eP9wWMRKQOuL2/IboiIjil8CBV/U3BITr3Admcc1kC2Jkjk8lQU1NDNpslkUhQV1dnyR5h/VVd3gS2ichzOK0fTcAB4BjwBWAS8A2cKkczTqfSQDwJ3CQi+3CqLl/F6ZH9twFe17N0Ok026/zNZbNZ0um0JXqE9bds9HIRuQ+Yh5PEq3Pe8iHwB2COqm4uQjwLcdrLf4kz5v0AcD8B9LqmUikSiURHiZ5KpUodgikiT3V0EfkCMBZnD6NDwB4NSSXf6ujGNeBFRlHV93EmXcRCMpm0BC8Ttv2iiQVLdBMLlugmFrxs7XKiiNgfhomkvBJXRBI4G+nmjnExJhLySnRVzeJsqnuCv+EY4w8vVZHbgbtEZKRfwRjjFy/t6D/AWX/xLRF5E2cLxg7Fmk5njB+8JPqjvkVhjM+8jEf/qZ+BGOMnz7PrRWQmzqhFBV5R1S3FDsqYYvOyyOhonB3ppgH73dOjRKQJ+FtV3d/rDxsTMC+tLmtwJkGcpaqnq+rpwNnuuTV+BGdMsXipulwMzFDVfe0nVHWviCwCni56ZMYUUTG69D8rwjWM8ZWXRH8aZ4v009tPiMhYnFlHVqKbUPOS6ItwJi/vFZHXRaQZ2OOeK8biRcb4xksd/V3ga8A3cQZ3CfCqqv7Bj8DyYeu6mHzlNWfUHb14DJiiqq/6HlUB/JgzaiKpxzmjNnrRxIKNXjSxYKMXTSzY6EUTC3kluogMBj4H3GdbpZsoyvdm9DjwT/RyR2tM2Hm5Gd2Ms/uEMZHjpY7+NPAzETkXeIHuN6OPFTMwY4rJS6L/wn3uqbtfcXatMyaUvEyls8WLTGRZ8ppY6DfRReRPInJyp+M7ReSUTscjReQNn+IzpijyKdGn03WMywLg5E7HCWB0EWMypugKqbpYW7qJHKujm1jIJ9GV7jtDh2LfImPylU/zogAPicjH7vFQ4H4R+cg9HuJLZMYUUT6JXp9z/FAP7yn5PqDGeNFvoqvqnFIE0hMRuQVYgTNq8ntBxWGiz/Pai6UiItNxNvLd5vdn3XPPPezatavb+W3bnI8+99yuc0rOOeccli5d6ndYpohC2eoiIiOAh4F/JMB9TVtbW2ltbQ3q4z3JZDLU19eTyWSCDiWUwlqirwMeVdX/FpFb/f6w3krn2tpaJ5h16/wOYUAymQw1NTUd27nX1dXZRsA5Qleii8g84CzgJ0HHEhXpdJpsNgtANpslnU4HHFHfgvj2CVWJLiLnAD8DLlDVT/J4vy1gBKRSKRKJREeJnkqlgg6pV0F9+4Qq0YGvAyOB7SIdIw0SwIUicj3wOVVtb89HVdfhVHOoqqqKbSdWMpmkrq6OdDpNKpUKdbWlp2+fOCb640DuclsPAq/hlPT9lvJxlUwmQ53g7YL69glVoqvqYeBw53MicgR4T1W3BxGTKa6gvn1CleimcJlMJhJVFwjm2yf0ia6qM4KOIewymQzXXHMNbW1tDB8+nI0bN4Y62YP4owx9opv+NTQ0sHfvXgDeeecdGhoaQpvoQbW6hK4d3ZS3oNr8LdHLQHV1NePHj6eyspLx48dTXV0ddEi9am91AeLb6mIKk0wm2bhxYyRuRq3VxQxIVNrRIZhYrepiYsES3cSCJbqJBUt0EwuW6CYWLNFNLFiim1iwdnTjq55WWAhidQVLdFNyQaysYIlufNVTCR3E6gqxTPSqqqq83nfw4MG839/UlDsD0IRJLBM9X6eeemrQIZgisVYXEwuRTnQRqRWRJhFpamlpCTqcQNmSdH2LdNXF1nVx2JJ0/Yt0iW4cUVuSLgiRLtGNIyxL0vnRmgXFadGyRC8DUVqSDoJpzbJELxNRmkoXBKujm1iwEj1ibBuawliil4kwbEGzdu3aoEPolSV6xER9G5qgWKKbopk/f74v1y1G86LdjJpYsBI95MLcCRMlluhlwoYU982qLiYWYlmih7kZLFeUYs1XezWrlN9CsUx044986/02Z7RE/GgG8+vmLsxNdlFidfQycfDgwY4qgekuliV6lIS5OhAloUx0EbkB+CHwJeAVYLGqbi11HEHcNPWnt0FdW7c6v572hG8X9KCunuINItbQJbqIXA2sBm4AnnWffycik1T1jWJ8RjmWkiNGjAg6hLwFEauohmtOsYg8B2xT1Xmdzr0GPKqqN/f2c1VVVVroDVZ/peQFF1zQ5XzQpaTpk/R0MlQ3oyJyAjAN2Jzz0mbg/FLHM2LEiEiVlKZ3Yau6jAQSQG7zwUHgr/36UCudy1/YEr1dbn1KejiHiNQC7Xc0bSLSvf5h4qZRVS/LPRm2RD8EZIHTcs5X0r2U77KAkTF9CVUdXVU/AV4ALs556WLgT6WPyJSLsJXoAD8HfiMizwN/BK4HRgG/DjQqE2mhS3RV/a2IfBH4MU6H0XbgW6r6erCRmSgLXTu6MX4IVR3dGL9YortE5DoRaQs6jnxYrN7FItFFRPt5bAB+C4z3eN2viMijIrLXvc7yEMc6T0S2ish7InJYRJ4RkW+ENNYr3Q0eDovIERF5SUS+O5BYQ3cz6pMvdfr33wD355w7qqpHgaMer3si0Aw8BtwxkAA78SvWGThJ90fgI2AJ8HsRmaqqr4Us1ndxfp87gePutdeLSIuqPlVQpKoaqwfwHec/u9v564C2TsfLcVp8aoA33P9ZjwMje7nudmB5FGJ1f0aAt4GFYY/V/bk0cGeh8cWi6jIA44DZQDXOWJuzgQeCDKgP4/AW6wnAUOB93yPrbhx5xiqOWcA5wP8W+oFxqboUahhwrbrj4EVkPrBVRM7Wwr/u/eI11juANuCJEsbYrt9YRWQE8BYwBGdYyAJV/V2hH2glet/e0q6TPZ4DPgMmBhRPX/KOVUS+D8wH/k5VPyhRfJ3lE+uHwFTgPOBHwM/dkr0gVqLHjJvkdwCXq+rzQcfTG1X9DPize/iSiEwEbgGeLuR6VqL3bbSInN7p+Gs4v7MdAcXTl35jFZEbgRXAt1X12RLH11khv9dBONWYgliJ3rejQL2bIMNwBpb9V6d65AnAJPe9Q4HTRGQqTivDn3u4XpCx/hAnyWcDu0WkfSj0UVUt9S4C/cX6I5zqzF6c5P4W8A/AwkI/0BK9b83AJuBJnNlPm3GaxdqNAl7sdPxlnLrv/+C0W5dSM33HugAYjNOW3lk9ThNgKTXTd6zDgV8BY3D+KHbi3Lw+UugH2qCuXri9nN9R1clBx9Ifi7V/Vkc3sWCJbmLBqi4mFqxEN7FgiW5iwRLdxIIluokFS3QTC5boJhb+H8iXK4F+LkThAAAAAElFTkSuQmCC\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "labelsize = 14\n", "linewidth = 1.5\n", "medianwidth = 1.5\n", "parts = (\"Tip1\", \"Tip2\", \"Tip3\")\n", "color = \"k\"\n", "\n", "fig = plt.figure(figsize=(3, 3))\n", "for i, part in enumerate(parts, start=1):\n", " plt.boxplot(getattr(results, f\"{part}diff\"), positions=(i,), \n", " widths=(0.5,),\n", " showfliers=True,\n", " patch_artist=True,\n", " capprops=dict(linewidth=linewidth, color=color, alpha=.8),\n", " boxprops=dict(linewidth=0, color=color, facecolor=color, alpha=.8),\n", " whiskerprops=dict(linewidth=linewidth, color=color, alpha=.8),\n", " flierprops=dict(marker=\"o\", mfc=color, mec=\"none\", alpha=.8, ms=4),\n", " medianprops=dict(linewidth=medianwidth, color=\"w\")\n", " )\n", "log(f\"## Estimation errors (real-time vs post-hoc, by part; non-filtered)\\n\")\n", "subjects = set(sub for sub in results.subject)\n", "sessions = set((sub, ses) for sub, ses in zip(results.subject, results.session))\n", "log(f\"N={results.subject.size} videos ({len(sessions)} sessions from {len(subjects)} subjects).\\n\")\n", "\n", "for part in parts:\n", " v = getattr(results, f\"{part}diff\")\n", " log(f\"- {part}: {v.mean():.4f}±{v.std(ddof=1):.4f} mm, \" + \\\n", " f\"min {v.min():.4f} mm, max {v.max():.4f} mm, \" + \\\n", " f\"median {np.median(v):.4f} mm, 25% {np.percentile(v, 25):.4f} mm, 75% {np.percentile(v, 75):.4f} mm\")\n", "for h, (i, j) in enumerate(((0, 1), (1, 2), (0, 2))):\n", " part1, part2 = parts[i], parts[j]\n", " values1, values2 = getattr(results, f\"{part1}diff\"), getattr(results, f\"{part2}diff\")\n", " p = sstats.wilcoxon(values1, values2).pvalue * 3\n", " log(f\"- {part1} vs {part2}: p={p:.8f}{psign(p, append=True)} (Wilcoxon's signed-rank test with Bonferroni correction)\")\n", " xbottom = 12 + 0.8 * h\n", " xtop = xbottom + 0.2\n", " plt.gca().plot((i+1,i+1,j+1,j+1), (xbottom, xtop, xtop, xbottom), \"k-\", lw=0.5)\n", " if p < 0.05:\n", " plt.text((i+j)/2 + 1, xtop - 0.5, psign(p), fontsize=labelsize-2, ha=\"center\", va=\"bottom\")\n", " else:\n", " plt.text((i+j)/2 + 1, xtop, psign(p), fontsize=labelsize-4, ha=\"center\", va=\"bottom\")\n", "plt.ylim(0, 15)\n", "plt.xlim(0.5, 3.5)\n", "plt.xticks((1, 2, 3), parts)\n", "plt.yticks((0, 2, 4, 6, 8, 10, 12), (\"0\", \"\", \"4\", \"\", \"8\", \"\", \"12\"))\n", "plt.ylabel(\"Error (mm)\", fontsize=labelsize)\n", "for side in (\"top\", \"right\", ):\n", " plt.gca().spines[side].set_visible(False)\n", "plt.tick_params(labelsize=labelsize, bottom=False)\n", "plt.subplots_adjust(bottom=.15, left=.3, right=.9, top=1)\n", "\n", "outdir = Path(\"F04_summary\")\n", "name = \"part-error\"\n", "if not outdir.exists():\n", " outdir.mkdir(parents=True)\n", "fig.savefig(str(outdir / f\"{name}.png\"), dpi=400)\n", "fig.savefig(str(outdir / f\"{name}.svg\"))\n", "\n", "log(\"\")" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "## Sessions used for accuracy evaluation\n", "\n", "the following analyses were restricted to videos where the estimation error of the related tip(s) was below 2.0 mm/frame.\n", "\n", "### Position\n", "\n", "N=15 videos (6 sessions from 5 subjects):\n", "\n", "- MLA-041630/2020-09-11-test (2/2 videos)\n", "- S005-19/2020-09-11-test (1/1 video)\n", "- S006-19/2020-09-11-test (0/1 video)\n", "- SNA-079258/2020-12-06-test (3/3 videos)\n", "- SNA-079258/2020-12-07-test (2/2 videos)\n", "- SNA-079259/2020-12-11-test (3/3 videos)\n", "- SNA-079260/2020-12-11-test (4/4 videos)\n", "\n", "### Spread\n", "\n", "N=8 videos (4 sessions from 3 subjects):\n", "\n", "- MLA-041630/2020-09-11-test (0/3 videos)\n", "- S005-19/2020-09-11-test (0/1 video)\n", "- S006-19/2020-09-11-test (0/2 videos)\n", "- SNA-079258/2020-12-06-test (2/2 videos)\n", "- SNA-079258/2020-12-07-test (2/2 videos)\n", "- SNA-079259/2020-12-11-test (2/2 videos)\n", "- SNA-079260/2020-12-11-test (2/2 videos)\n", "\n", "### Sessions where both position- and spread-based triggering were valid\n", "\n", "N=20 videos (4 sessions from 3 subjects):\n", "\n", "- SNA-079258/2020-12-07-test (4/4 videos)\n", "- SNA-079259/2020-12-11-test (5/5 videos)\n", "- SNA-079260/2020-12-11-test (6/6 videos)\n", "- SNA-079258/2020-12-06-test (5/5 videos)\n", "\n" ] } ], "source": [ "EvalType = namedtuple(\"EvalType\", (\"name\", \"valid\", \"mask\", \"color\"))\n", "\n", "position = EvalType(\"Position\", np.logical_and(position_good, results.sign < 0), results.sign < 0, \"m\")\n", "spread = EvalType(\"Spread\", np.logical_and(spread_good, results.sign > 0), results.sign > 0, \"g\")\n", "evs = (position, spread)\n", "\n", "log(\"## Sessions used for accuracy evaluation\\n\")\n", "log(f\"the following analyses were restricted to videos where the estimation error of the related tip(s) was below {threshold:.1f} mm/frame.\\n\")\n", "\n", "# count N\n", "overlap = set((sub, sess) for sub, sess in zip(results.subject, results.session))\n", "for evt in evs:\n", " log(f\"### {evt.name}\\n\")\n", " subjects = set(sub for sub in results.subject[evt.valid])\n", " sessions = set((sub, sess) for sub, sess in zip(results.subject[evt.valid], results.session[evt.valid]))\n", " overlap = overlap & sessions\n", " log(f\"N={np.count_nonzero(evt.valid)} videos ({len(sessions)} sessions from {len(subjects)} subjects):\\n\")\n", " for sess in sorted(set((sub, sess) for sub, sess in zip(results.subject, results.session)),\n", " key=lambda sess: sess[0]):\n", " _valid = [(sub, ses) for sub, ses in zip(results.subject[evt.valid], results.session[evt.valid]) \\\n", " if (sub, ses) == sess]\n", " _total = [(sub, ses) for sub, ses in zip(results.subject[evt.mask], results.session[evt.mask]) \\\n", " if (sub, ses) == sess]\n", " _num = \"\" if len(_total) == 1 else \"s\"\n", " log(f\"- {sess[0]}/{sess[1]} ({len(_valid)}/{len(_total)} video{_num})\")\n", " log(\"\")\n", "\n", "log(\"### Sessions where both position- and spread-based triggering were valid\\n\")\n", "subjects = set(sess[0] for sess in overlap)\n", "counted = np.logical_or(position.mask, spread.mask)\n", "runs = [(sub, sess) for sub, sess in zip(results.subject[counted], results.session[counted])\\\n", " if (sub, sess) in overlap]\n", "log(f\"N={len(runs)} videos ({len(overlap)} sessions from {len(subjects)} subjects):\\n\")\n", "for sess in overlap:\n", " _valid = [(sub, ses) for sub, ses in zip(results.subject[counted], results.session[counted]) \\\n", " if (sub, ses) == sess]\n", " _total = [(sub, ses) for sub, ses in zip(results.subject, results.session) \\\n", " if (sub, ses) == sess]\n", " _num = \"\" if len(_total) == 1 else \"s\"\n", " log(f\"- {sess[0]}/{sess[1]} ({len(_valid)}/{len(_total)} video{_num})\")\n", "log(\"\")" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "## Evaluation of real-time accuracy\n", "\n", "### Threshold positions in real-time triggering\n", "\n", "- Position: -0.1765±0.4356 mm, min -0.8139 mm, max 0.9412 mm, median -0.2135 mm, 25% -0.3926 mm, 75% -0.1077; p=0.06372070, NS (Wilcoxon's signed-rank test)\n", "- Spread: 1.0968±0.9063 mm, min 0.1475 mm, max 2.4381 mm, median 0.6312 mm, 25% 0.4691 mm, 75% 1.8488; p=0.00781250** (Wilcoxon's signed-rank test)\n", "- Position vs Spread: p=0.00044319*** (Mann-Whitney's U-test)\n", "\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "delta = results.mean - results.original\n", "labelsize = 14\n", "linewidth = 1.5\n", "medianwidth = 1.5\n", "\n", "log(\"## Evaluation of real-time accuracy\\n\")\n", "\n", "log(\"### Threshold positions in real-time triggering\\n\")\n", "\n", "fig = plt.figure(figsize=(2.5, 3))\n", "for i, ev in enumerate(evs, start=1):\n", " v = delta[ev.valid]\n", " plt.boxplot(v, positions=(i,), \n", " widths=(0.5,),\n", " showfliers=True,\n", " patch_artist=True,\n", " capprops=dict(linewidth=linewidth, color=ev.color, alpha=.8),\n", " boxprops=dict(linewidth=0, color=ev.color, facecolor=ev.color, alpha=.8),\n", " whiskerprops=dict(linewidth=linewidth, color=ev.color, alpha=.8),\n", " flierprops=dict(marker=\"o\", mfc=ev.color, mec=\"none\", alpha=.8, ms=4),\n", " medianprops=dict(linewidth=medianwidth, color=\"w\")\n", " )\n", " p = sstats.wilcoxon(results.mean[ev.valid], results.original[ev.valid]).pvalue\n", " log(f\"- {ev.name}: {v.mean():.4f}±{v.std(ddof=1):.4f} mm, \" + \\\n", " f\"min {v.min():.4f} mm, max {v.max():.4f} mm, \" + \\\n", " f\"median {np.median(v):.4f} mm, 25% {np.percentile(v, 25):.4f} mm, 75% {np.percentile(v, 75):.4f}; \" + \\\n", " f\"p={p:.8f}{psign(p, append=True)} (Wilcoxon's signed-rank test)\")\n", " if p < 0.05:\n", " plt.text(i, delta[ev.valid].max()+.3, psign(p), \n", " fontsize=labelsize-2, ha=\"center\", va=\"center\")\n", " else:\n", " plt.text(i, delta[ev.valid].max()+.8, psign(p), \n", " fontsize=labelsize-2, ha=\"center\", va=\"center\")\n", "p = sstats.mannwhitneyu(delta[position.valid], delta[spread.valid]).pvalue\n", "log(f\"- Position vs Spread: p={p:.8f}{psign(p, append=True)} (Mann-Whitney's U-test)\")\n", "base = 3.5\n", "plt.gca().plot((1,1,2,2), (base, base+.1, base+.1, base), \"k-\", lw=0.5)\n", "if p < 0.05:\n", " plt.text(1.5, base+.1, psign(p), fontsize=labelsize-2, ha=\"center\", va=\"bottom\")\n", "else:\n", " plt.text(1.5, base+.1, psign(p), fontsize=labelsize-2, ha=\"center\", va=\"bottom\")\n", "plt.hlines(0, 0, 3, linestyle=\"--\", linewidth=1, color=\"k\")\n", "plt.ylim(-1.5, 3.8)\n", "plt.xlim(0.5, 2.5)\n", "plt.xticks((1, 2), [f\"{ev.name[0]} ({np.count_nonzero(ev.valid)})\" for ev in evs])\n", "plt.yticks((-1, 0, 1, 2, 3), (\"\", \"0\", \"\", \"2\", \"\", ))\n", "plt.ylabel(\"∆Threshold (mm)\", fontsize=labelsize)\n", "for side in (\"top\", \"right\", \"bottom\"):\n", " plt.gca().spines[side].set_visible(False)\n", "plt.tick_params(labelsize=labelsize, bottom=False)\n", "plt.subplots_adjust(bottom=.15, left=.3, right=.9, top=0.9)\n", "\n", "outdir = Path(\"F04_summary\")\n", "name = \"threshold-difference\"\n", "if not outdir.exists():\n", " outdir.mkdir(parents=True)\n", "fig.savefig(str(outdir / f\"{name}.png\"), dpi=400)\n", "fig.savefig(str(outdir / f\"{name}.svg\"))\n", "\n", "log(\"\")" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "### Threshold variability in real-time triggering\n", "\n", "- Position: 1.5940±1.0531 mm, min 0.9062 mm, max 4.3376 mm, median 1.0943 mm, 25% 1.0314 mm, 75% 1.4161 mm\n", "- Spread: 3.6876±1.8970 mm, min 1.9493 mm, max 7.4774 mm, median 3.3360 mm, 25% 2.2116 mm, 75% 4.1485 mm\n", "- Position vs Spread: p=0.00134299** (Mann-Whitney's U-test)\n", "\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "labelsize = 14\n", "linewidth = 1.5\n", "medianwidth = 1.5\n", "\n", "acc = results.std * 2\n", "\n", "log(\"### Threshold variability in real-time triggering\\n\")\n", "\n", "fig = plt.figure(figsize=(2.5, 3))\n", "for i, ev in enumerate(evs, start=1):\n", " mwidth = medianwidth\n", " v = acc[ev.valid]\n", " log(f\"- {ev.name}: {v.mean():.4f}±{v.std(ddof=1):.4f} mm, \" + \\\n", " f\"min {v.min():.4f} mm, max {v.max():.4f} mm, \" + \\\n", " f\"median {np.median(v):.4f} mm, 25% {np.percentile(v, 25):.4f} mm, 75% {np.percentile(v, 75):.4f} mm\")\n", " plt.boxplot(v, positions=(i,), \n", " widths=(0.5,),\n", " showfliers=True,\n", " patch_artist=True,\n", " capprops=dict(linewidth=linewidth, color=ev.color, alpha=.8),\n", " boxprops=dict(linewidth=0, color=ev.color, facecolor=ev.color, alpha=.8),\n", " whiskerprops=dict(linewidth=linewidth, color=ev.color, alpha=.8),\n", " flierprops=dict(marker=\"o\", mfc=ev.color, mec=\"none\", alpha=.8, ms=4),\n", " medianprops=dict(linewidth=mwidth, color=\"w\")\n", " )\n", "p = sstats.mannwhitneyu(acc[position.valid], acc[spread.valid]).pvalue\n", "log(f\"- Position vs Spread: p={p:.8f}{psign(p, append=True)} (Mann-Whitney's U-test)\")\n", "plt.gca().plot((1,1,2,2), (8, 8.2, 8.2, 8), \"k-\", lw=0.5)\n", "if p < 0.05:\n", " plt.text(1.5, 8.2, psign(p), fontsize=labelsize-2, ha=\"center\", va=\"bottom\")\n", "plt.ylim(0, 8.3)\n", "plt.xlim(0.5, 2.5)\n", "plt.xticks((1, 2), [f\"{ev.name[0]} ({np.count_nonzero(ev.valid)})\" for ev in evs])\n", "plt.yticks((0, 1, 2, 3, 4, 5, 6, 7), (\"0\", \"\", \"2\", \"\", \"4\", \"\", \"6\", \"\"))\n", "plt.ylabel(\"Variability (mm)\", fontsize=labelsize)\n", "for side in (\"top\", \"right\",):\n", " plt.gca().spines[side].set_visible(False)\n", "plt.tick_params(labelsize=labelsize, bottom=False)\n", "plt.subplots_adjust(bottom=.15, left=.3, right=.9, top=0.9)\n", "\n", "outdir = Path(\"F04_summary\")\n", "name = \"variability\"\n", "if not outdir.exists():\n", " outdir.mkdir(parents=True)\n", "fig.savefig(str(outdir / f\"{name}.png\"), dpi=400)\n", "fig.savefig(str(outdir / f\"{name}.svg\"))\n", "\n", "log(\"\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 4 }