Source code for dispel.providers.bdh.tasks.ft
"""Processing functionality for Finger Tapping (FT) task."""
from typing import List, Union
import pandas as pd
from dispel.data.raw import RawDataValueDefinition
from dispel.processing.core import ProcessingStep
from dispel.processing.data_set import transformation
from dispel.processing.level import ProcessingStepGroup
from dispel.processing.transform import TransformStep
from dispel.providers.bdh.data import BDHReading
from dispel.providers.generic.sensor import SetTimestampIndex
from dispel.providers.generic.tasks.ft.const import TOUCH_ATTRIBUTES
from dispel.providers.generic.tasks.ft.steps import TASK_NAME, GenericFingerTappingSteps
from dispel.providers.generic.touch import TOUCH_COLUMNS, Gesture, split_touches
from dispel.providers.registry import process_factory
[docs]
class TransformRawTouchEvents(TransformStep):
    """Preprocess the io raw touch events.
    The touch path ids that we receive with the io format are not increasing
    over time, this is just a counter of the number of fingers on the screen.
    In order to use the :class: `~dispel.providers.generic.touch.Gesture`
    module we need to transform those ids into an increasing sequence with one
    touch path id per tap.
    """
    data_set_ids = "screen"
    new_data_set_id = "processed_screen"
    definitions = [RawDataValueDefinition(column, column) for column in TOUCH_COLUMNS]
[docs]
    @staticmethod
    @transformation
    def preprocess_raw_touch_events(data):
        """Pre-process the raw touch events."""
        return split_touches(
            data, begin=data["tsTouch"].min(), end=data["tsTouch"].max()
        )
[docs]
class SetTimestampIndexBDHonly(SetTimestampIndex):
    """BDH specific set timestamp processing step."""
[docs]
class TransformGesture(TransformStep):
    """Generate a gesture dataset from the preprocessed raw touch events."""
    data_set_ids = "processed_screen"
    new_data_set_id = "gestures"
    definitions = [RawDataValueDefinition("gestures", "gestures")]
    @staticmethod
    @transformation
    def _generate_gesture(data):
        return pd.Series(Gesture.from_data_frame(data))
[docs]
class TransformTapsFromRaw(TransformStep):
    """Retrieve the tap events from the gesture dataset."""
    data_set_ids = "gestures"
    new_data_set_id = "taps_from_raw"
    definitions = [
        RawDataValueDefinition(column, column)
        for column in TOUCH_ATTRIBUTES + ["tap_duration"]
    ]
    @staticmethod
    @transformation
    def _generate_taps_from_raw(data):
        new_data = {}
        # Explode the gesture dataset to have individual taps
        exploded_touch = (
            data["gestures"].apply(lambda x: x.touches).explode().reset_index(drop=True)
        )
        # Extract the touch attributes
        for attribute in TOUCH_ATTRIBUTES:
            new_data[attribute] = exploded_touch.apply(lambda x: getattr(x, attribute))
        df = pd.DataFrame(new_data).sort_values(by="begin")
        df["tap_duration"] = (df["end"] - df["begin"]).dt.total_seconds() * 1e3
        return df
[docs]
class TransformTapEvents(TransformStep):
    """Generate a gesture dataset from the preprocessed raw touch events."""
    data_set_ids = ["taps_from_raw", "tap_events_ts"]
    new_data_set_id = "enriched_tap_events"
    definitions = [
        RawDataValueDefinition(column, column)
        for column in TOUCH_ATTRIBUTES + ["tap_duration", "location"]
    ]
[docs]
    @staticmethod
    @transformation
    def enrich(raw_ts, taps):
        """Enrich the tap events with the information provided by the raw events."""
        raw_ts_copy = raw_ts.copy().sort_values(by="begin")
        taps_copy = taps.copy().sort_index()
        if len(raw_ts) == len(taps):
            return pd.concat([taps_copy.reset_index(), raw_ts_copy], axis=1).drop(
                columns="timestamp"
            )
        return pd.merge_asof(
            raw_ts_copy,
            taps_copy,
            left_on="begin",
            right_index=True,
            direction="nearest",
        )
[docs]
class BDHPreprocessingStepGroup(ProcessingStepGroup):
    """BDH preprocessing step group for finger tapping."""
    steps: List[Union[ProcessingStep, ProcessingStepGroup]] = [
        TransformRawTouchEvents(),
        # Generate gesture from the raw  touch events
        TransformGesture(),
        # Generate the tap events from the gesture dataset
        TransformTapsFromRaw(),
        SetTimestampIndexBDHonly(
            data_set_id="tap_events",
            columns=["location"],
            time_stamp_column="timestamp",
            duplicates="first",
        ),
        TransformTapEvents(),
        SetTimestampIndexBDHonly(
            data_set_id="enriched_tap_events",
            columns=["location", "end", "tap_duration", "first_position"],
            time_stamp_column="begin",
            duplicates="first",
        ),
    ]
[docs]
class BDHSteps(ProcessingStepGroup):
    """BDH steps used to process finger tapping records."""
    steps: List[ProcessingStep] = [
        BDHPreprocessingStepGroup(),
        GenericFingerTappingSteps(),
    ]
process_ft = process_factory(
    task_name=TASK_NAME,
    steps=BDHSteps(),
    codes="fingertap-activity",
    supported_type=BDHReading,
)