diff --git a/src/pymovements/__init__.py b/src/pymovements/__init__.py index 8cf4f938..a2b2c657 100644 --- a/src/pymovements/__init__.py +++ b/src/pymovements/__init__.py @@ -18,6 +18,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. """Provides top-level access to submodules.""" +import logging +import os + from pymovements import _version from pymovements import datasets from pymovements import events @@ -44,7 +47,6 @@ from pymovements.measure import SampleMeasureLibrary from pymovements.stimulus import text - __all__ = [ 'Dataset', 'DatasetDefinition', @@ -79,3 +81,7 @@ ] __version__ = _version.get_versions()['version'] + +LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO') +LOG_CONFIG = '[%(levelname)s] %(asctime)s %(name)s:%(lineno)d - %(message)s' +logging.basicConfig(level=LOG_LEVEL, format=LOG_CONFIG) diff --git a/src/pymovements/dataset/dataset.py b/src/pymovements/dataset/dataset.py index 9c851341..f0aa4416 100644 --- a/src/pymovements/dataset/dataset.py +++ b/src/pymovements/dataset/dataset.py @@ -35,7 +35,6 @@ from pymovements.dataset.dataset_paths import DatasetPaths from pymovements.events.frame import EventDataFrame from pymovements.events.precomputed import PrecomputedEventDataFrame -from pymovements.events.processing import EventGazeProcessor from pymovements.gaze import GazeDataFrame from pymovements.reading_measures.frame import ReadingMeasures @@ -713,7 +712,7 @@ def compute_event_properties( name: str | None Process only events that match the name. (default: None) verbose : bool - If ``True``, show progress bar. (default: True) + If ``True``, show more info. (default: True) Raises ------ @@ -728,22 +727,9 @@ def compute_event_properties( Dataset Returns self, useful for method cascading. """ - processor = EventGazeProcessor(event_properties) - - identifier_columns = [ - column - for column in self.fileinfo['gaze'].columns - if column != 'filepath' - ] - disable_progressbar = not verbose - for events, gaze in tqdm(zip(self.events, self.gaze), disable=disable_progressbar): - new_properties = processor.process( - events, gaze, identifiers=identifier_columns, name=name, - ) - join_on = identifier_columns + ['name', 'onset', 'offset'] - events.add_event_properties(new_properties, join_on=join_on) - + for gaze in tqdm(self.gaze, disable=disable_progressbar): + gaze.compute_event_properties(event_properties, name=name, verbose=verbose) return self def compute_properties( diff --git a/src/pymovements/events/processing.py b/src/pymovements/events/processing.py index 51776555..1cc4f2fb 100644 --- a/src/pymovements/events/processing.py +++ b/src/pymovements/events/processing.py @@ -137,7 +137,7 @@ def process( events: EventDataFrame Event data to process event properties from. gaze: pm.GazeDataFrame - Gaze data to process event properties from. + Raw gaze data to process event properties from. identifiers: str | list[str] Column names to join on events and gaze dataframes. name: str | None diff --git a/src/pymovements/gaze/gaze_dataframe.py b/src/pymovements/gaze/gaze_dataframe.py index 10f3e8bc..a8d8f629 100644 --- a/src/pymovements/gaze/gaze_dataframe.py +++ b/src/pymovements/gaze/gaze_dataframe.py @@ -21,6 +21,7 @@ from __future__ import annotations import inspect +import logging import warnings from collections.abc import Callable from copy import deepcopy @@ -30,12 +31,16 @@ import polars as pl import pymovements as pm # pylint: disable=cyclic-import +from pymovements.events.processing import EventGazeProcessor from pymovements.gaze import transforms from pymovements.gaze.experiment import Experiment from pymovements.utils import checks from pymovements.utils.aois import get_aoi +logger = logging.getLogger(__name__) + + class GazeDataFrame: """A DataFrame for gaze time series data. @@ -547,6 +552,7 @@ def deg2pix( pixel_origin: str = 'upper left', position_column: str = 'position', pixel_column: str = 'pixel', + verbose: bool = False, ) -> None: """Compute gaze positions in pixel position coordinates from degrees of visual angle. @@ -883,6 +889,54 @@ def detect( how='diagonal', ) + def compute_event_properties( + self, + event_properties: str | tuple[str, dict[str, Any]] + | list[str | tuple[str, dict[str, Any]]], + name: str | None = None, + verbose: bool = True, + ) -> None: + """Calculate an event property for and add it as a column to the event dataframe. + + Parameters + ---------- + event_properties: str | tuple[str, dict[str, Any]] | list[str | tuple[str, dict[str, Any]]] + The event properties to compute. + name: str | None + Process only events that match the name. (default: None) + verbose: bool + If ``True``, print information about the progress. (default: True) + Raises + ------ + InvalidProperty + If ``property_name`` is not a valid property. See + :py:mod:`pymovements.events.event_properties` for an overview of supported properties. + RuntimeError + If specified event name ``name`` is missing from ``events``. + If no events are available to compute event properties. Consider calling detect before. + + Returns + ------- + pm.EventDataFrame + Returns self, useful for method cascading. + """ + if self.events is None: + raise RuntimeError( + 'No events available to compute event properties. ' + 'Consider calling detect before.', + ) + + if verbose is True: + logger.debug(f'Processing events {event_properties} matching {name} \ +for \n{self.events.frame.head()}') + + processor = EventGazeProcessor(event_properties) + new_properties = processor.process( + self.events, self, identifiers=self.trial_columns, name=name, + ) + join_on = self.trial_columns + ['name', 'onset', 'offset'] + self.events.add_event_properties(new_properties, join_on=join_on) + def measure_samples( self, method: str | Callable[..., pl.Expr],