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.