Container emulation in Python
The Python data model makes it easy to implement classes that allow concise data access. For example, when analyzing skin marker motion capture data, I frequently access data for a particular marker (identified by its name). Below I demonstrate how to access marker data from a trial (say arm raising and lowering) using an instance method, and via container emulation.
class MarkerTrial:
def __init__(self, trial_name, trial_data):
self.trial_name = trial_name
self._trial_data = trial_data
def __getitem__(self, marker):
"""Return marker data, (n, 3) numpy array view"""
return self._trial_data.loc[: marker:(marker + '.2')].to_numpy()
def marker_data(self, marker):
"""Return marker data, (n, 3) numpy array view"""
return self._trial_data.loc[: marker:(marker + '.2')].to_numpy()
And this is how marker data could be accessed with each technique (RSHO stands for right shoulder marker):
import pandas as pd
import numpy as np
trial = MarkerTrial('S010_SA_t01', pd.read_csv('S010_SA_t01.csv', header=[0],
dtype=np.float64))
marker_data_via_method = trial.marker_data('RSHO')
marker_data_via_emulation = trial['RSHO']
Accessing the skin marker data from the trial via container emulation is much more concise and adheres to a known convention for data access (dictionary). But, what if I had two types of skin marker data for a trial? In my use case, I frequently analyze raw and smoothed marker data. The smoothed marker data (as the name implies) has been processed and filtered via various algorithms. I like to plot the raw and smoothed marker data overlaid to quickly assess how the data was processed. My solution was to create a "nested" container.
class NestedContainer:
"""A class which allows access to items in the nested container."""
def __init__(self, nested_container):
self._nested_container = nested_container
def __getitem__(self, marker):
return self._nested_container.loc[: marker:(marker + '.2')].to_numpy()
class MarkerTrial:
def __init__(self, trial_name, trial_data_raw, trial_data_smooth):
self.trial_name = trial_name
self.raw = NestedContainer(trial_data_raw)
self.smooth = NestedContainer(trial_data_smooth)
import pandas as pd
import numpy as np
raw_trial_data = pd.read_csv('S010_SA_t01_raw.csv', header=[0], dtype=np.float64)
smoothed_trial_data = pd.read_csv('S010_SA_t01_smoothed.csv', header=[0],
dtype=np.float64)
trial = MarkerTrial('S010_SA_t01', raw_trial_data, smoothed_trial_data)
raw_marker_data = trial.raw['RSHO']
smoothed_marker_data = trial.smooth['RSHO']
The nested container can be made generic as well:
class NestedContainer:
"""A class which allows access to items in the nested container."""
def __init__(self, nested_container, get_item_method):
self._nested_container = nested_container
self._get_item_method = get_item_method
def __getitem__(self, item):
return self._get_item_method(self._nested_container, item)
def get_marker_method(csv_data, marker):
"""Return marker data, (n, 3) numpy array view."""
return csv_data.loc[:, marker:(marker + '.2')].to_numpy()
class MarkerTrial:
def __init__(self, trial_name, trial_data_raw, trial_data_smooth):
self.trial_name = trial_name
self.raw = NestedContainer(trial_data_raw, get_marker_method)
self.smooth = NestedContainer(trial_data_smooth, get_marker_method)
Container emulation is a great feature of the Python data model that enables a programmer to write concise code.