aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThéo de la Hogue2023-06-15 16:39:37 +0200
committerThéo de la Hogue2023-06-15 16:39:37 +0200
commita08cd2838e5c506f1af6f966f0681eae60a1573e (patch)
tree447c54b781124de7b1a4788cc2c58e341c1387cd
parent212f8daba5552e19e92cabfc8530030254dcb2b1 (diff)
downloadargaze-a08cd2838e5c506f1af6f966f0681eae60a1573e.zip
argaze-a08cd2838e5c506f1af6f966f0681eae60a1573e.tar.gz
argaze-a08cd2838e5c506f1af6f966f0681eae60a1573e.tar.bz2
argaze-a08cd2838e5c506f1af6f966f0681eae60a1573e.tar.xz
Adding and testing TimeStampedGazePositions.from_dataframe classmethod.
-rw-r--r--src/argaze.test/GazeFeatures.py47
-rw-r--r--src/argaze/GazeFeatures.py54
2 files changed, 94 insertions, 7 deletions
diff --git a/src/argaze.test/GazeFeatures.py b/src/argaze.test/GazeFeatures.py
index f73eefe..8cd2e56 100644
--- a/src/argaze.test/GazeFeatures.py
+++ b/src/argaze.test/GazeFeatures.py
@@ -13,6 +13,7 @@ from dataclasses import dataclass
from argaze import GazeFeatures
import numpy
+import pandas
def random_gaze_positions(size, screen_dimension: tuple[float, float] = (1, 1)):
""" Generate random TimeStampedGazePsoitions for testing purpose.
@@ -209,6 +210,52 @@ class TestTimeStampedGazePositionsClass(unittest.TestCase):
self.assertEqual(repr(ts_gaze_positions), "{\"0\": {\"message\": null, \"value\": [null, null], \"precision\": null}}")
+ def test_from_dataframe(self):
+ """Test from_dataframe classmethod."""
+
+ data = {'Specific timestamp label': [0, 1, 2, 3, 4],
+ 'Specific gaze position x label': [0, 10, numpy.nan, 30, 40],
+ 'Specific gaze position y label': [0, 100, numpy.nan, 300, 400]
+ }
+ dataframe = pandas.DataFrame().from_dict(data)
+
+ ts_gaze_positions = GazeFeatures.TimeStampedGazePositions.from_dataframe(dataframe, timestamp='Specific timestamp label', x='Specific gaze position x label', y='Specific gaze position y label')
+
+ # Check buffer length
+ self.assertEqual(len(ts_gaze_positions), 5)
+
+ # Check first gaze position is correctly stored and accessible as a GazePosition
+ self.assertIsInstance(ts_gaze_positions[0], GazeFeatures.GazePosition)
+ self.assertEqual(ts_gaze_positions[0].precision, 0)
+ self.assertEqual(ts_gaze_positions[0].valid, True)
+
+ # Check third gaze position is correctly stored and accessible as a UnvalidGazePosition
+ self.assertIsInstance(ts_gaze_positions[2], GazeFeatures.UnvalidGazePosition)
+ self.assertEqual(ts_gaze_positions[2].precision, None)
+ self.assertEqual(ts_gaze_positions[2].valid, False)
+
+ data = {'Specific timestamp label': [0, 1, 2, 3, 4],
+ 'Specific gaze position x label': [0, 10, numpy.nan, 30, 40],
+ 'Specific gaze position y label': [0, 100, numpy.nan, 300, 400],
+ 'Specific precisison label': [15, 15, numpy.nan, 15, 15]
+ }
+ dataframe = pandas.DataFrame().from_dict(data)
+
+ ts_gaze_positions = GazeFeatures.TimeStampedGazePositions.from_dataframe(dataframe, timestamp='Specific timestamp label', x='Specific gaze position x label', y='Specific gaze position y label', precision='Specific precisison label')
+
+ # Check buffer length
+ self.assertEqual(len(ts_gaze_positions), 5)
+
+ # Check first gaze position is correctly stored and accessible as a GazePosition
+ self.assertIsInstance(ts_gaze_positions[0], GazeFeatures.GazePosition)
+ self.assertEqual(ts_gaze_positions[0].precision, 15)
+ self.assertEqual(ts_gaze_positions[0].valid, True)
+
+ # Check third gaze position is correctly stored and accessible as a UnvalidGazePosition
+ self.assertIsInstance(ts_gaze_positions[2], GazeFeatures.UnvalidGazePosition)
+ self.assertEqual(ts_gaze_positions[2].precision, None)
+ self.assertEqual(ts_gaze_positions[2].valid, False)
+
def test_as_dataframe(self):
"""Test inherited as_dataframe method."""
diff --git a/src/argaze/GazeFeatures.py b/src/argaze/GazeFeatures.py
index 53bec25..870cd3c 100644
--- a/src/argaze/GazeFeatures.py
+++ b/src/argaze/GazeFeatures.py
@@ -125,9 +125,15 @@ class TimeStampedGazePositions(DataStructures.TimeStampedBuffer):
assert(set(['value', 'precision']).issubset(value.keys()))
- if 'message' in value.keys():
+ if math.isnan(value['precision']):
- value = UnvalidGazePosition(value['message'])
+ if 'message' in value.keys():
+
+ value = UnvalidGazePosition(value['message'])
+
+ else :
+
+ value = UnvalidGazePosition()
else:
@@ -148,14 +154,48 @@ class TimeStampedGazePositions(DataStructures.TimeStampedBuffer):
return TimeStampedGazePositions({ast.literal_eval(ts_str): json_buffer[ts_str] for ts_str in json_buffer})
@classmethod
- def from_dataframe(self, dataframe: pandas.DataFrame, exclude=[]) -> TimeStampedGazePositionsType:
- """Create a TimeStampedGazePositions from [Pandas DataFrame](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html)."""
+ def from_dataframe(self, dataframe: pandas.DataFrame, timestamp: str, x: str, y: str, precision: str = None) -> TimeStampedGazePositionsType:
+ """Create a TimeStampedGazePositions from [Pandas DataFrame](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html).
+
+ Parameters:
+ timestamp: specific timestamp column label.
+ x: specific x column label.
+ y: specific y column label.
+ precision: specific precision column label if exist.
+ """
+
+ # Select columns
+ if precision:
+
+ df = dataframe[[timestamp, x, y, precision]]
+
+ else:
- dataframe.drop(exclude, inplace=True, axis=True)
+ df = dataframe[[timestamp, x, y]]
- assert(dataframe.index.name == 'timestamp')
+ # Merge x and y columns into one 'value' column
+ df['value'] = tuple(zip(df[x], df[y]))
+ df.drop(columns= [x, y], inplace=True, axis=1)
+
+ # Handle precision data
+ if precision:
+
+ # Rename precision column into 'precision' column
+ df.rename(columns={precision: 'precision'}, inplace=True)
+
+ else:
+
+ # Append a precision column where precision is NaN if value is a tuple of NaN else 0
+ df['precision'] = df.apply(lambda row: numpy.nan if math.isnan(row.value[0]) or math.isnan(row.value[1]) else 0, axis=True)
+
+ # Rename timestamp column into 'timestamp' column then use it as index
+ df.rename(columns={timestamp: 'timestamp'}, inplace=True)
+ df.set_index('timestamp', inplace=True)
+
+ # Filter duplicate timestamps
+ df = df[df.index.duplicated() == False]
- return TimeStampedGazePositions(dataframe.to_dict('index'))
+ return TimeStampedGazePositions(df.to_dict('index'))
GazeMovementType = TypeVar('GazeMovement', bound="GazeMovement")
# Type definition for type annotation convenience