{
"cells": [
{
"cell_type": "markdown",
"id": "3d333b09",
"metadata": {
"colab_type": "text",
"id": "view-in-github",
"tags": []
},
"source": [
""
]
},
{
"cell_type": "markdown",
"id": "5be65f0d-d29b-4756-ab52-b080ba4a24d5",
"metadata": {
"id": "5be65f0d-d29b-4756-ab52-b080ba4a24d5",
"tags": []
},
"source": [
"# Kinematics and Kinetics of Walking\n",
"\n",
"We can’t understand anything about how the nervous system organizes and controls a movement until we can understand the movement itself and the biomechanics of the body controlled by the nervous system. \n",
"\n",
"Kinematics is the study and quantification of how objects move. In working through the contents of this notebook, you will learn more about movement and how it is measured and analyzed. You will also practice using data processing and visualization techniques common to neuroscience, physics, biology, and many data science professions. \n",
"\n",
"Specifically, you will learn about how (some) human bodies walk in the context of current standard practices in the fields of biomechanics. The [dataset you will explore](https://doi.org/10.6084/m9.figshare.5722711) was published by [Fukuchi et al (2018)](https://peerj.com/articles/4640/). They placed markers placed at specific locations along the body were tracked during walking (either overland or on a treadmill). Note that the nomenclature for the marker points in this dataset vary slightly from [the marker placement resource](https://www.sciencedirect.com/science/article/pii/S0966636207000124?via%3Dihub) referenced in Fukuchi et al (2018). See marker key in slides from class. Force plates were used to quantify ground contact forces during walking (\"kinetics\").\n",
"\n",
"
\n",
"\n",
"\n",
"Your work on this notebook will scaffold discussions throughout the course about kinematics of a variety of movements, the challenges faced by the nervous system in controlling movement, and emerging technologies that are revolutionizing ways of quantifying movement. \n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "770856d7-2f01-469c-b77e-e0026b48763b",
"metadata": {
"id": "770856d7-2f01-469c-b77e-e0026b48763b"
},
"source": [
"# Setup\n",
"\n",
"Complete the following preparatory steps to set up the Colab notebook environment for your work.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "52fd61e0-d203-4664-b783-a6e2c7392fde",
"metadata": {
"cellView": "form",
"id": "52fd61e0-d203-4664-b783-a6e2c7392fde",
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode: \"form\"}\n",
"\n",
"#@markdown RUN this cell to set up the notebook (import packages, define functions, etc)\n",
"# In Python, anything with a \"#\" in front of it is code annotation,\n",
"# and is not read by the computer.\n",
"# You can run a cell (this box) by pressing ctrl-enter or shift-enter.\n",
"# You can also run a cell by clicking the play button in the menu bar \n",
"# at the top of the page (single right arrow, not double).\n",
"# Click in this cell and then press shift and enter simultaneously.\n",
"# This print function below allows us to generate a message.\n",
"print('Nice work!')\n",
"\n",
"# No need to edit anything in this code cell\n",
"#################################\n",
"\n",
"import numpy as np\n",
"import pandas as pd\n",
"from scipy import ndimage\n",
"from scipy.signal import find_peaks\n",
"from copy import deepcopy\n",
"import math\n",
"from sklearn.decomposition import PCA\n",
"from sklearn.cluster import KMeans\n",
"from pathlib import Path\n",
"import matplotlib.pyplot as plt\n",
"import plotly.express as px\n",
"import plotly.graph_objects as go\n",
"from plotly.subplots import make_subplots\n",
"from ipywidgets import widgets,interactive, interactive_output\n",
"import matplotlib.gridspec as gridspec\n",
"from IPython.display import display\n",
"import csv\n",
"from scipy.signal import hilbert,medfilt,resample\n",
"from scipy.io import wavfile\n",
"from sklearn.decomposition import PCA\n",
"import scipy\n",
"import seaborn as sns\n",
"from datetime import datetime,timezone,timedelta\n",
"pal = sns.color_palette(n_colors=15)\n",
"pal = pal.as_hex()\n",
"\n",
"%config InlineBackend.figure_format = 'retina'\n",
"plt.style.use(\"https://raw.githubusercontent.com/NeuromatchAcademy/course-content/master/nma.mplstyle\")\n",
"\n",
"# datafolder = \"/content/drive/Shareddrives/BIOL358/Data/WBDSascii\"\n",
"# datafolder = \"/content/drive/MyDrive/Classroom/BIOL358: Motor Systems/Data/WBDSascii\"\n",
"# datafolder = '/Users/kperks/OneDrive - wesleyan.edu/Teaching/MotorSystems_SP22/Data/WBDSascii/'\n",
"\n",
"fs_grf = 300\n",
"fs_mkr = 150\n",
"\n",
"def cart2pol(x, y):\n",
" rho = np.sqrt(x**2 + y**2)\n",
" phi = np.arctan2(y, x)\n",
" return(rho, phi)\n",
" \n",
"print('Task completed at ' + str(datetime.now(timezone(-timedelta(hours=5)))))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fbed1e34-7bc2-4ef0-bc7c-ef79b7ec11be",
"metadata": {
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode: \"form\"}\n",
"\n",
"#@markdown RUN this cell to download and unzip the raw data from \"figshare\".\n",
"#@markdown Note that this step may take a few minutes. If the \"run cell\" button is still spinning, then it is still working.\n",
"\n",
"!wget https://figshare.com/ndownloader/files/10058986\n",
"\n",
"# use --no-check-certificate to ignore SSL certificate errors that might occur on websites with an expired SSL certificate\n",
"\n",
"!unzip -q /content/10058986 -d /content/WBDSascii \n",
"datafolder = \"/content/WBDSascii\"\n",
"\n",
"# !unzip -q ./10058986 -d ./WBDSascii \n",
"# datafolder = \"./WBDSascii\"\n",
"\n",
"print('Task completed at ' + str(datetime.now(timezone(-timedelta(hours=5)))))"
]
},
{
"cell_type": "markdown",
"id": "f060ffb0-2696-42e1-b3d7-b1973c928fcc",
"metadata": {},
"source": [
"The dataset should now be located in your Colab file directory in a folder called \"WBDSascii\". Check that it is there before proceeding."
]
},
{
"cell_type": "markdown",
"id": "edbebb54-81d1-4cc7-a4ab-70ecf17c11e5",
"metadata": {
"id": "edbebb54-81d1-4cc7-a4ab-70ecf17c11e5"
},
"source": [
"# Dataset Selection\n",
"\n",
"Run the code cell below to activate dropdown menus for selecting which person, environment, and speed you want to analyze (note different speed selection options based on the environment chosen).\n",
" > NOTE: You only need to activate these menus once per active colab session. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c7e34cf9-b37a-4630-8059-c7bd38672707",
"metadata": {
"cellView": "form",
"id": "c7e34cf9-b37a-4630-8059-c7bd38672707",
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode: \"form\"}\n",
"\n",
"#@markdown Activate Dropdown Menus if needed.\n",
" \n",
"w_subj = widgets.Select(\n",
" options=['select person (01-42)'] + [format(x,'02d') for x in range(1,43,1)],\n",
" value='select person (01-42)',\n",
" # rows=10,\n",
" disabled=False\n",
")\n",
"w_env = widgets.Select(\n",
" options=['select environment','Overland','Treadmill'],\n",
" value='select environment',\n",
" rows=3,disabled=False\n",
")\n",
"w_Ospeed = widgets.Select(\n",
" options=['select speed for overland','Comfortable','Fast','Slow'],\n",
" value='select speed for overland',\n",
" rows=4,disabled=False\n",
")\n",
"w_Tspeed = widgets.Select(\n",
" options=['select speed for treadmill (01-08)'] + [format(x,'02d') for x in range(1,9,1)],\n",
" value='select speed for treadmill (01-08)',\n",
" rows=5,disabled=False\n",
")\n",
"display(w_subj,w_env,w_Ospeed,w_Tspeed)\n",
"print('Dropdowns generated at ' + str(datetime.now(timezone(-timedelta(hours=5)))))"
]
},
{
"cell_type": "markdown",
"id": "0c51636c-1ee8-4815-8f4a-77533d397d18",
"metadata": {},
"source": [
"## Load Data "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e6a3b532-d435-4ba2-8a88-fd9148f2fff0",
"metadata": {
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode: \"form\"} \n",
"\n",
"#@markdown Run this code cell to load the data you have selected.\n",
"\n",
"if w_env.value=='Overland':\n",
" speed = w_Ospeed.value[0]\n",
" trial = '01'\n",
" filename = 'WBDS' + str(w_subj.value) + 'walk' + w_env.value[0] + trial + speed \n",
"\n",
"if w_env.value=='Treadmill':\n",
" speed = w_Tspeed.value\n",
" filename = 'WBDS' + str(w_subj.value) + 'walk' + w_env.value[0] + speed \n",
"\n",
"\n",
"filepath = Path(datafolder) / (filename + 'mkr.txt')\n",
"if filepath.exists():\n",
" dataframe = pd.read_csv(filepath, sep='\\t')\n",
" print('Kinematic Data loaded at ' + str(datetime.now(timezone(-timedelta(hours=5)))))\n",
"\n",
"if filepath.exists()==False:\n",
" print('Data selection does not exist. Check selections and correct errors.')\n",
" print('Task not yet completed, but cell ran at ' + str(datetime.now(timezone(-timedelta(hours=5))))) \n",
"\n",
"filepath_grf = Path(datafolder) / (filename + 'grf.txt')\n",
"if filepath_grf.exists():\n",
" grf_trial = pd.read_csv(filepath_grf,sep='\\t')\n",
" grf_trial['Time']/=fs_grf\n",
" print('Kinetic Data loaded at ' + str(datetime.now(timezone(-timedelta(hours=5)))))\n",
"\n",
"if filepath_grf.exists()==False:\n",
" print('Data selection does not exist. Check selections and correct errors.')\n",
" print('Task not yet completed, but cell ran at ' + str(datetime.now(timezone(-timedelta(hours=5)))))\n",
" \n",
"filename_norm = 'WBDS' + str(w_subj.value) + 'walk' + w_env.value[0] + speed \n",
"\n",
"filepath_norm = Path(datafolder) / (filename_norm + 'ang.txt')\n",
"ang_norm = pd.read_csv(filepath_norm,sep = '\\t')\n",
"print('Processed kinematics data loaded at ' + str(datetime.now(timezone(-timedelta(hours=5)))))\n",
"\n",
"\n",
"filepath_norm = Path(datafolder) / (filename_norm + 'knt.txt')\n",
"grf_norm = pd.read_csv(filepath_norm,sep = '\\t')\n",
"print('Processed ground fource data loaded at ' + str(datetime.now(timezone(-timedelta(hours=5)))))\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "BPpUAFtHP3ME",
"metadata": {
"id": "BPpUAFtHP3ME"
},
"source": [
"# RAW data\n"
]
},
{
"cell_type": "markdown",
"id": "31d41f94-24df-4420-ac7d-639a2b48b37e",
"metadata": {
"id": "31d41f94-24df-4420-ac7d-639a2b48b37e"
},
"source": [
"## Kinematics \n",
"\n",
"You can select different kinematics variables and re-make the following plot as many times as you want without re-loading the dataset. \n",
"\n",
"Follow your curiosities about what kinematics are related to each other and in what ways. There are many to choose from. Explore both ‘ipsilateral’ and ‘contralateral’ comparisons. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "64d7729d-eb40-4c6b-8561-17c407b66aac",
"metadata": {
"cellView": "form",
"id": "8ff3a3e7-6072-4957-affc-00ae78ee5888",
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode: \"form\"}\n",
"\n",
"#@markdown Run this code cell to get a list of kinematic variables to choose from.\n",
"\n",
"w_markers = widgets.SelectMultiple(\n",
" options=list(dataframe.columns)[1:],\n",
" rows=15)\n",
"\n",
"print('')\n",
"print('')\n",
"print('Select which kinematics variables you want to plot from the following list:')\n",
"display(w_markers)\n",
"print('')\n",
"\n",
"print('Dropdown menus created at ' + str(datetime.now(timezone(-timedelta(hours=5)))))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c11c12d6-e730-4b47-b2cd-855e7889b90b",
"metadata": {
"cellView": "form",
"id": "8ff3a3e7-6072-4957-affc-00ae78ee5888",
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@markdown Run this code cell to plot the selected kinematics variables\n",
"\n",
"\n",
"fig = go.Figure()\n",
"if len(w_markers.value)>0:\n",
" for marker in list(w_markers.value):\n",
" fig.add_trace(go.Scatter(x = dataframe['Time'], y = dataframe[marker],name=marker))\n",
" print('Task completed at ' + str(datetime.now(timezone(-timedelta(hours=5)))))\n",
"\n",
"if len(w_markers.value)==0:\n",
" print('Need to select markers to plot')\n",
" print('Task not completed yet, but cell run at ' + str(datetime.now(timezone(-timedelta(hours=5)))))\n",
" \n",
"fig.update_layout(xaxis_title=\"time(seconds)\", yaxis_title='position (centimeters)',width=800, height=500)"
]
},
{
"cell_type": "markdown",
"id": "80923d9b-7946-40ec-b795-838718d7a116",
"metadata": {
"id": "80923d9b-7946-40ec-b795-838718d7a116"
},
"source": [
"## Kinetics\n",
"\n",
"Select a subset of ground force measurements to examine. \n",
" > Treadmill environments have more ground force data than overland. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "937445b0-c576-4296-bffc-41ea9a1748f2",
"metadata": {
"cellView": "form",
"id": "Hw0TwgunODC-",
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode: \"form\"}\n",
"\n",
"#@markdown Run this code cell to get a list of kinetic variables to choose from.\n",
"\n",
"w_fp = widgets.SelectMultiple(\n",
" options=list(grf_trial.columns)[1:],\n",
" rows=10)\n",
"\n",
"print('')\n",
"print('Select which kinetic variables you want to plot from the following list:')\n",
"display(w_fp)\n",
"print('Dropdown menus created at ' + str(datetime.now(timezone(-timedelta(hours=5)))))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "497d8d55-d565-4e1a-a54e-c7da8ba2d772",
"metadata": {
"cellView": "form",
"id": "497d8d55-d565-4e1a-a54e-c7da8ba2d772",
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode: \"form\"}\n",
"\n",
"#@markdown Run this code cell to plot the kinetics data you selected.\n",
"\n",
"fig = go.Figure()\n",
"if len(w_fp.value)>0:\n",
" for fp in list(w_fp.value):\n",
" fig.add_trace(go.Scatter(x = grf_trial['Time'], y = grf_trial[fp],name=fp))\n",
" print('Plot created at ' + str(datetime.now(timezone(-timedelta(hours=5)))))\n",
"\n",
"if len(w_fp.value)==0:\n",
" print('Need to select markers to plot')\n",
" print('Task not completed, but cell run at ' + str(datetime.now(timezone(-timedelta(hours=5)))))\n",
"\n",
"fig.update_layout(xaxis_title=\"time(seconds)\", yaxis_title='force',width=800, height=500)\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "iR6gql9Ba4NT",
"metadata": {
"id": "iR6gql9Ba4NT"
},
"source": [
"### Compare across trials\n",
"\n",
"**First, load data from a treadmill condition.**\n",
" > The treadmill condition has more trials.\n",
"\n",
"Each \"trial\" is be defined as a single gait cycle. The onset of a gait cycle is determined based on kinetic data (ground reaction forces measured by the force plates).\n",
"\n",
"Based on the ground force measurements you just plotted in the last code cell, use your mouse hover to determine the start times of 3 trials. In the code cell below, use the form to create a list of trial start times and an approximate gait cycle period (seconds). \n",
" > The formatting of the list must be trial start times (in seconds) separated by commas. The whole list must be surrounded by hard brackets. For example: ```[1.24,5.67,8.98]```\n",
"\n",
"After filling in the necessary information, run the code cell to overlay the kinetic data across those trials in a single plot. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7be69845-df61-4c4f-a40b-449c091cc1fd",
"metadata": {
"cellView": "form",
"id": "1814abac-ca52-4ddb-8490-b131456a3eba",
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode: \"form\"}\n",
"\n",
"#@markdown Type a list of trial times below. \n",
"trials = [None] #@param \n",
"\n",
"#@markdown Type the gait cycle period below (in seconds). \n",
"period = None #@param \n",
"\n",
"#@markdown After you have created a list of trial start times and specified a period,\n",
"#@markdown run this cell to make a plot of the overlaid kinetics data. \n",
"\n",
"offset = 0.5\n",
"persamps = int(period*fs_grf)+int(offset*fs_grf)\n",
"trial_inds = [int((t-offset)*fs_grf) for t in trials]\n",
"xtime = np.linspace(-offset,period,persamps)\n",
"\n",
"\n",
"fig = go.Figure()\n",
"\n",
"if len(w_fp.value)>0:\n",
" for fp in list(w_fp.value):\n",
" for t in trial_inds:\n",
" fig.add_trace(go.Scatter(x = xtime, y = grf_trial[fp][t:t+persamps],name=fp+' at trial ' +str(round(t/fs_grf,2)+offset)))\n",
" print('Task completed at ' + str(datetime.now(timezone(-timedelta(hours=5)))))\n",
"\n",
"if len(w_fp.value)==0:\n",
" print('Need to select markers to plot')\n",
" print('Task not completed, but cell run at ' + str(datetime.now(timezone(-timedelta(hours=5)))))\n",
"\n",
"fig.update_layout(xaxis_title=\"time(seconds)\", yaxis_title='force',width=800, height=500)\n",
"\n",
"\n",
"\n",
"# #@markdown Run this code cell to plot the kinematics measurements on each trial overlaid.\n",
"\n",
"# offset = 0.5\n",
"# persamps = int(period*fs_mkr)+int(offset*fs_mkr)\n",
"# trial_inds = [int((t-offset)*fs_mkr) for t in trials]\n",
"# xtime = np.linspace(-offset,period,persamps)\n",
"\n",
"# fig = go.Figure()\n",
"\n",
"# if len(w_markers.value)>0:\n",
"# for marker in list(w_markers.value):\n",
"# for t in trial_inds:\n",
"# fig.add_trace(go.Scatter(x = xtime, y = dataframe[marker][t:t+persamps],name=str(marker)+' at trial ' +str(round(t/fs_mkr,2))))\n",
"# print('Task completed at ' + str(datetime.now(timezone(-timedelta(hours=5)))))\n",
"\n",
"# if len(w_markers.value)==0:\n",
"# print('Need to select markers to plot')\n",
"# print('Task not completed, but cell run at ' + str(datetime.now(timezone(-timedelta(hours=5)))))\n",
"\n",
"# fig.update_layout(xaxis_title=\"time(seconds)\", yaxis_title='position',width=800, height=500)\n"
]
},
{
"cell_type": "markdown",
"id": "4N8i5DBwchGM",
"metadata": {
"id": "4N8i5DBwchGM"
},
"source": [
"What do you notice across trials?\n"
]
},
{
"cell_type": "markdown",
"id": "7126c4e1-f8f2-4fb5-955e-c91039bfed90",
"metadata": {
"id": "51cdb5b3-5657-43c3-8ccf-c8147e5f7efb"
},
"source": [
"# Time-normalized (processed) data\n",
"\n",
"Now that you have a sense of the data, let's look at trial-averaged and time-normalized data. The data is \"time-normalized\" so that the kinematics and kinetics are a function of the gait cycle (from start to stop - 0 to 100% of the gait cycle) instead of time. \n",
" > Think about why time normalization important for an analysis of kinematics and kinetics."
]
},
{
"cell_type": "markdown",
"id": "b530661f-e23e-4a00-af09-e69ce2a8554d",
"metadata": {
"id": "51cdb5b3-5657-43c3-8ccf-c8147e5f7efb"
},
"source": [
"## Kinematics and Kinetics "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "af2fc820-a458-4886-b1e3-5a880dcc7ee6",
"metadata": {
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode: \"form\"} \n",
"\n",
"#@markdown Run this cell to explore the average kinematics and kinetics per gait cycle. \n",
"\n",
"select_kinematics = widgets.SelectMultiple(\n",
" options=list(ang_norm.columns)[1:], # start with a single trial on a single bout... it will update when runs ; old: np.arange(len(trial_times)),\n",
" value=[list(ang_norm.columns)[1:][0]],\n",
" rows=10,\n",
" description='Kinematics',\n",
" style = {'description_width': '100px'},\n",
" disabled=False\n",
")\n",
"\n",
"select_kinetics = widgets.SelectMultiple(\n",
" options=list(grf_norm.columns)[1:], # start with a single trial on a single bout... it will update when runs ; old: np.arange(len(trial_times)),\n",
" value=[list(grf_norm.columns)[1:][0]],\n",
" rows=10,\n",
" description='Kinetics',\n",
" style = {'description_width': '100px'},\n",
" disabled=False\n",
")\n",
"\n",
"ui_selections = widgets.HBox([select_kinematics, select_kinetics])\n",
"\n",
"def update_plot(kinematics_,kinetics_):\n",
" fig, ax = plt.subplots(figsize=(10,7),num=1,nrows=2,ncols=1); #specify figure number so that it does not keep creating new ones\n",
" \n",
" for m_ in kinematics_:\n",
" ax[0].plot(ang_norm['Time'],ang_norm[m_],label=m_)\n",
"\n",
" ax[0].legend(bbox_to_anchor=(1.05, 1))\n",
" ax[0].set_ylabel('degrees')\n",
" \n",
" for n_ in kinetics_:\n",
" ax[1].plot(grf_norm['Time'],grf_norm[n_],label=n_)\n",
"\n",
" ax[1].legend(bbox_to_anchor=(1.05, 1))\n",
" ax[1].set_ylabel('newton-meters')\n",
" ax[1].set_xlabel('percent gait cycle')\n",
"\n",
"# w_normalized_plots_ = interactive(update_plot, kinematics_=select_kinematics, \n",
"# kinetics_=select_kinetics);\n",
"# display(w_normalized_plots_)\n",
"\n",
"w = interactive_output(update_plot, {'kinematics_':select_kinematics,\n",
" 'kinetics_':select_kinetics\n",
" });\n",
"display(ui_selections,w)"
]
},
{
"cell_type": "markdown",
"id": "718b2a6d-65c2-4c42-9e49-3838187b861f",
"metadata": {
"id": "80923d9b-7946-40ec-b795-838718d7a116"
},
"source": [
"## Ground Reaction Force\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "COTaAuNIASQn",
"metadata": {
"cellView": "form",
"id": "COTaAuNIASQn",
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode: \"form\"} \n",
"\n",
"#@markdown Run this cell to plot the ground reaction force vector direction and magnitude\n",
"\n",
"\n",
"fig = plt.figure(figsize=(15,10),num=1)\n",
"\n",
"gs = gridspec.GridSpec(2, 2, figure=fig)\n",
"ax1 = fig.add_subplot(gs[0, 0])\n",
"ax2 = fig.add_subplot(gs[1, 0])\n",
"ax3 = fig.add_subplot(gs[:, 1], polar=True) # identical to ax3 = plt.subplot(gs.new_subplotspec((0, 0), rowspan=2))\n",
"\n",
"\n",
"rho,phi = cart2pol(grf_norm['RGRFX'],grf_norm['RGRFY'])\n",
"ax1.scatter(grf_norm['Time'],np.degrees(phi),label='right',s=10,color='blue')\n",
"ax2.scatter(grf_norm['Time'],rho,label='right',s=10,color='blue')\n",
"ax3.scatter(phi,rho, color='blue', alpha=0.5,s=5)\n",
"\n",
"rho,phi = cart2pol(grf_norm['LGRFX'],grf_norm['LGRFY'])\n",
"ax1.scatter(grf_norm['Time'],np.degrees(phi),label='left',s=10,color='red')\n",
"ax2.scatter(grf_norm['Time'],rho,label='left',s=10,color='red')\n",
"ax3.scatter(phi,rho, color='red', alpha=0.5,s=5)\n",
"\n",
"ax1.legend();\n",
"ax1.set_ylabel('angle')\n",
"ax2.set_ylabel('magnitude')\n",
"ax2.set_xlabel('percent gait cycle');\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "xRO4R-zOt8lw",
"metadata": {
"id": "xRO4R-zOt8lw"
},
"source": [
"