Skip to content

Commit

Permalink
Enh! Rename scanpaths.ns to scanpaths.n because it's only one value p…
Browse files Browse the repository at this point in the history
…er scanpath

Signed-off-by: Matthias Kümmerer <[email protected]>
  • Loading branch information
matthias-k committed Apr 1, 2024
1 parent cc3d863 commit 3f8538c
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 30 deletions.
29 changes: 19 additions & 10 deletions pysaliency/datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1095,7 +1095,7 @@ class Scanpaths(object):
Attributes:
xs (VariableLengthArray): The x-coordinates of the scanpaths.
ys (VariableLengthArray): The y-coordinates of the scanpaths.
ns (np.ndarray): The number of fixations in each scanpath.
n (np.ndarray): The image index
lengths (np.ndarray): The lengths of each scanpath.
scanpath_attributes (dict): Additional attributes associated with the scanpaths.
fixation_attributes (dict): Additional attributes associated with the fixations in the scanpaths.
Expand All @@ -1105,18 +1105,18 @@ class Scanpaths(object):

xs: VariableLengthArray
ys: VariableLengthArray
ns: np.ndarray
n: np.ndarray

def __init__(self,
xs: Union[np.ndarray, VariableLengthArray],
ys: Union[np.ndarray, VariableLengthArray],
ns: np.ndarray,
n: np.ndarray,
lengths=None,
scanpath_attributes: Optional[Dict[str, np.ndarray]] = None,
fixation_attributes: Optional[Dict[str, Union[np.ndarray, VariableLengthArray]]]=None,
attribute_mapping=Dict[str, str]):

self.ns = np.asarray(ns)
self.n = np.asarray(n)

if not isinstance(xs, VariableLengthArray):
self.xs = VariableLengthArray(xs, lengths)
Expand All @@ -1131,8 +1131,8 @@ def __init__(self,

self.ys = self._as_variable_length_array(ys)

if not len(self.xs) == len(self.ys) == len(self.ns):
raise ValueError("Length of xs, ys, ts and ns has to match")
if not len(self.xs) == len(self.ys) == len(self.n):
raise ValueError("Length of xs, ys, ts and n has to match")

# setting scanpath attributes

Expand Down Expand Up @@ -1168,6 +1168,15 @@ def _as_variable_length_array(self, data: Union[np.ndarray, VariableLengthArray]
def __len__(self):
return len(self.xs)

@property
def ts(self) -> VariableLengthArray:
return self.fixation_attributes['ts']

@property
def subject(self) -> VariableLengthArray:
return self.scanpath_attributes['subject']


@hdf5_wrapper(mode='w')
def to_hdf5(self, target):
""" Write scanpaths to hdf5 file or hdf5 group
Expand All @@ -1177,7 +1186,7 @@ def to_hdf5(self, target):

target.create_dataset('xs', data=self.xs._data)
target.create_dataset('ys', data=self.ys._data)
target.create_dataset('ns', data=self.ns)
target.create_dataset('n', data=self.n)
target.create_dataset('lengths', data=self.lengths)

scanpath_attributes_group = target.create_group('scanpath_attributes')
Expand Down Expand Up @@ -1209,7 +1218,7 @@ def read_hdf5(cls, source):
lengths = source['lengths'][...]
xs = VariableLengthArray(source['xs'][...], lengths)
ys = VariableLengthArray(source['ys'][...], lengths)
ns = source['ns'][...]
n = source['n'][...]

scanpath_attributes = _load_attribute_dict_from_hdf5(source['scanpath_attributes'])

Expand All @@ -1224,7 +1233,7 @@ def read_hdf5(cls, source):
return cls(
xs=xs,
ys=ys,
ns=ns,
n=n,
lengths=lengths,
scanpath_attributes=scanpath_attributes,
fixation_attributes=fixation_attributes,
Expand All @@ -1242,7 +1251,7 @@ def __getitem__(self, index):
elif isinstance(index, int):
raise NotImplementedError("Not implemented yet")
else:
return type(self)(self.xs[index], self.ys[index], self.ns[index], self.lengths[index],
return type(self)(self.xs[index], self.ys[index], self.n[index], self.lengths[index],
scanpath_attributes={key: value[index] for key, value in self.scanpath_attributes.items()},
fixation_attributes={key: value[index] for key, value in self.fixation_attributes.items()},
attribute_mapping=self.attribute_mapping)
Expand Down
2 changes: 2 additions & 0 deletions pysaliency/utils/variable_length_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ def __init__(self, data: Union[np.ndarray, List[list]], lengths: Optional[np.nda
if len(lengths) and np.max(lengths) > data.shape[1]:
raise ValueError("The specified lengths are larger than the number of columns in the data array")

lengths = np.array(lengths, dtype=int)

else:
if isinstance(data, np.ndarray):
raise ValueError("If data is a numpy array, lengths must be provided")
Expand Down
40 changes: 20 additions & 20 deletions tests/datasets/test_scanpaths.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,17 @@ def assert_scanpaths_equal(scanpaths1: Scanpaths, scanpaths2: Scanpaths, scanpat
def test_scanpaths():
xs = np.array([[0, 1, 2], [2, 2, np.nan], [1, 5, 3]])
ys = np.array([[10, 11, 12], [12, 12, np.nan], [21, 25, 33]])
ns = np.array([0, 0, 1])
n = np.array([0, 0, 1])
lengths = np.array([3, 2, 3])
scanpath_attributes = {'task': np.array([0, 1, 0])}
fixation_attributes = {'attribute1': np.array([[1, 1, 2], [2, 2, np.nan], [0, 1, 3]]), 'attribute2': np.array([[3, 1.3, 5], [1, 42, np.nan], [0, -1, -3]])}
attribute_mapping = {'attribute1': 'attr1', 'attribute2': 'attr2'}

scanpaths = Scanpaths(xs, ys, ns, lengths, scanpath_attributes, fixation_attributes, attribute_mapping)
scanpaths = Scanpaths(xs, ys, n, lengths, scanpath_attributes, fixation_attributes, attribute_mapping)

assert isinstance(scanpaths.xs, VariableLengthArray)
assert isinstance(scanpaths.ys, VariableLengthArray)
assert isinstance(scanpaths.ns, np.ndarray)
assert isinstance(scanpaths.n, np.ndarray)
assert isinstance(scanpaths.lengths, np.ndarray)
assert isinstance(scanpaths.scanpath_attributes, dict)
assert isinstance(scanpaths.scanpath_attributes['task'], np.ndarray)
Expand All @@ -61,7 +61,7 @@ def test_scanpaths():

np.testing.assert_array_equal(scanpaths.xs._data, xs)
np.testing.assert_array_equal(scanpaths.ys._data, ys)
np.testing.assert_array_equal(scanpaths.ns, ns)
np.testing.assert_array_equal(scanpaths.n, n)
np.testing.assert_array_equal(scanpaths.lengths, lengths)
np.testing.assert_array_equal(scanpaths.scanpath_attributes['task'], np.array([0, 1, 0]))
np.testing.assert_array_equal(scanpaths.fixation_attributes['attribute1']._data, np.array([[1, 1, 2], [2, 2, np.nan], [0, 1, 3]]))
Expand All @@ -72,17 +72,17 @@ def test_scanpaths():
def test_scanpaths_from_lists():
xs = [[0, 1, 2], [2, 2], [1, 5, 3]]
ys = [[10, 11, 12], [12, 12], [21, 25, 33]]
ns = [0, 0, 1]
n = [0, 0, 1]
expected_lengths = np.array([3, 2, 3])
scanpath_attributes = {'task': [0, 1, 0]}
fixation_attributes = {'attribute1': [[1, 1, 2], [2, 2], [0, 1, 3]], 'attribute2': [[3, 1.3, 5], [1, 42], [0, -1, -3]]}
attribute_mapping = {'attribute1': 'attr1', 'attribute2': 'attr2'}

scanpaths = Scanpaths(xs, ys, ns, lengths=None, scanpath_attributes=scanpath_attributes, fixation_attributes=fixation_attributes, attribute_mapping=attribute_mapping)
scanpaths = Scanpaths(xs, ys, n, lengths=None, scanpath_attributes=scanpath_attributes, fixation_attributes=fixation_attributes, attribute_mapping=attribute_mapping)

np.testing.assert_array_equal(scanpaths.xs._data, np.array([[0, 1, 2], [2, 2, np.nan], [1, 5, 3]]))
np.testing.assert_array_equal(scanpaths.ys._data, np.array([[10, 11, 12], [12, 12, np.nan], [21, 25, 33]]))
np.testing.assert_array_equal(scanpaths.ns, ns)
np.testing.assert_array_equal(scanpaths.n, n)
np.testing.assert_array_equal(scanpaths.lengths, expected_lengths)
np.testing.assert_array_equal(scanpaths.scanpath_attributes['task'], np.array([0, 1, 0]))
np.testing.assert_array_equal(scanpaths.fixation_attributes['attribute1']._data, np.array([[1, 1, 2], [2, 2, np.nan], [0, 1, 3]]))
Expand All @@ -93,57 +93,57 @@ def test_scanpaths_from_lists():
def test_scanpaths_init_inconsistent_lengths():
xs = np.array([[0, 1, 2], [2, 2, np.nan], [1, 5, 3]])
ys = np.array([[10, 11, 12], [12, 12, np.nan]]) # too short, should fail
ns = np.array([0, 0, 1])
n = np.array([0, 0, 1])
lengths = np.array([3, 2, 3])
scanpath_attributes = {'task': np.array([0, 1, 0])}
fixation_attributes = {'attribute1': np.array([[1, 1, 2], [2, 2, np.nan], [0, 1, 3]]), 'attribute2': np.array([[3, 1.3, 5], [1, 42, np.nan], [0, -1, -3]])}
attribute_mapping = {'attribute1': 'attr1', 'attribute2': 'attr2'}

with pytest.raises(ValueError):
Scanpaths(xs, ys, ns, lengths, scanpath_attributes, fixation_attributes, attribute_mapping)
Scanpaths(xs, ys, n, lengths, scanpath_attributes, fixation_attributes, attribute_mapping)

def test_scanpaths_init_invalid_scanpath_attributes():
xs = np.array([[0, 1, 2], [2, 2, np.nan], [1, 5, 3]])
ys = np.array([[10, 11, 12], [12, 12, np.nan], [21, 25, 33]])
ns = np.array([0, 0, 1])
n = np.array([0, 0, 1])
lengths = np.array([3, 2, 3])
scanpath_attributes = {'invalid_attribute': np.array([1, 2]), 'attribute2': np.array([4, 5, 6])} # Invalid attribute length
scanpath_fixation_attributes = {'fixation_attribute1': np.array([[1, 2], [3, 4], [5, 6]]), 'fixation_attribute2': np.array([[7, 8], [9, 10], [11, 12]])}
scanpath_attribute_mapping = {'attribute1': 'attr1', 'attribute2': 'attr2'}

with pytest.raises(ValueError):
Scanpaths(xs, ys, ns, lengths, scanpath_attributes, scanpath_fixation_attributes, scanpath_attribute_mapping)
Scanpaths(xs, ys, n, lengths, scanpath_attributes, scanpath_fixation_attributes, scanpath_attribute_mapping)

def test_scanpaths_init_invalid_scanpath_fixation_attributes():
xs = np.array([[0, 1, 2], [2, 2, np.nan], [1, 5, 3]])
ys = np.array([[10, 11, 12], [12, 12, np.nan], [21, 25, 33]])
ns = np.array([0, 0, 1])
n = np.array([0, 0, 1])
lengths = np.array([3, 2, 3])
scanpath_attributes = {'attribute1': np.array([1, 2, 3]), 'attribute2': np.array([4, 5, 6])}
scanpath_fixation_attributes = {'valid_fixation_attribute': np.array([[1, 2], [3, 4], [5, 6]]), 'invalid_fixation_attribute': np.array([[7, 8], [9, 10]])} # Invalid fixation attribute length
scanpath_attribute_mapping = {'attribute1': 'attr1', 'attribute2': 'attr2'}

with pytest.raises(ValueError):
Scanpaths(xs, ys, ns, lengths, scanpath_attributes, scanpath_fixation_attributes, scanpath_attribute_mapping)
Scanpaths(xs, ys, n, lengths, scanpath_attributes, scanpath_fixation_attributes, scanpath_attribute_mapping)

def test_scanpaths_init_invalid_scanpath_fixation_attributes_dimensions():
xs = np.array([[0, 1, 2], [2, 2, np.nan], [1, 5, 3]])
ys = np.array([[10, 11, 12], [12, 12, np.nan], [21, 25, 33]])
ns = np.array([0, 0, 1])
n = np.array([0, 0, 1])
lengths = np.array([3, 2, 3])
scanpath_attributes = {'attribute1': np.array([1, 2, 3]), 'attribute2': np.array([4, 5, 6])}
scanpath_fixation_attributes = {'fixation_attribute1': np.array([1, 2, 3]), 'fixation_attribute2': np.array([[7, 8], [9, 10], [11, 12]])} # Invalid fixation attribute dimensions
scanpath_attribute_mapping = {'attribute1': 'attr1', 'attribute2': 'attr2'}

with pytest.raises(ValueError):
Scanpaths(xs, ys, ns, lengths, scanpath_attributes, scanpath_fixation_attributes, scanpath_attribute_mapping)
Scanpaths(xs, ys, n, lengths, scanpath_attributes, scanpath_fixation_attributes, scanpath_attribute_mapping)

def test_scanpaths_init_invalid_scanpath_lengths():

data = {
'xs': [[0, 1, 2], [2, 2], [1, 5, 3]],
'ys': [[10, 11, 12], [12, 12], [21, 25, 33]],
'ns': [0, 0, 1],
'n': [0, 0, 1],
'scanpath_attributes': {'task': [0, 1, 0]},
'fixation_attributes': {'attribute1': [[1, 1, 2], [2, 2], [0, 1, 3]], 'attribute2': [[3, 1.3, 5], [1, 42], [0, -1, -3]]},
'attribute_mapping': {'attribute1': 'attr1', 'attribute2': 'attr2'},
Expand Down Expand Up @@ -179,12 +179,12 @@ def test_scanpaths_init_invalid_scanpath_lengths():
def test_scanpaths_slicing(inds):
xs = [[0, 1, 2], [2, 2], [1, 5, 3]]
ys = [[10, 11, 12], [12, 12], [21, 25, 33]]
ns = [0, 0, 1]
n = [0, 0, 1]
scanpath_attributes = {'task': [0, 1, 0]}
fixation_attributes = {'attribute1': [[1, 1, 2], [2, 2], [0, 1, 3]], 'attribute2': [[3, 1.3, 5], [1, 42], [0, -1, -3]]}
attribute_mapping = {'attribute1': 'attr1', 'attribute2': 'attr2'}

scanpaths = Scanpaths(xs, ys, ns, lengths=None, scanpath_attributes=scanpath_attributes, fixation_attributes=fixation_attributes, attribute_mapping=attribute_mapping)
scanpaths = Scanpaths(xs, ys, n, lengths=None, scanpath_attributes=scanpath_attributes, fixation_attributes=fixation_attributes, attribute_mapping=attribute_mapping)

sliced_scanpaths = scanpaths[inds]
assert_scanpaths_equal(sliced_scanpaths, scanpaths, inds)
Expand All @@ -195,12 +195,12 @@ def test_write_read_scanpaths_pathlib(tmp_path):

xs = [[0, 1, 2], [2, 2], [1, 5, 3]]
ys = [[10, 11, 12], [12, 12], [21, 25, 33]]
ns = [0, 0, 1]
n = [0, 0, 1]
scanpath_attributes = {'task': [0, 1, 0]}
fixation_attributes = {'attribute1': [[1, 1, 2], [2, 2], [0, 1, 3]], 'attribute2': [[3, 1.3, 5], [1, 42], [0, -1, -3]]}
attribute_mapping = {'attribute1': 'attr1', 'attribute2': 'attr2'}

scanpaths = Scanpaths(xs, ys, ns, lengths=None, scanpath_attributes=scanpath_attributes, fixation_attributes=fixation_attributes, attribute_mapping=attribute_mapping)
scanpaths = Scanpaths(xs, ys, n, lengths=None, scanpath_attributes=scanpath_attributes, fixation_attributes=fixation_attributes, attribute_mapping=attribute_mapping)

scanpaths.to_hdf5(filename)

Expand Down

0 comments on commit 3f8538c

Please sign in to comment.