Skip to content

Commit 1c37bb6

Browse files
authored
Merge pull request #1105 from JuliaSprenger/fix/nixio_quantity_annotations
[nixio] fix quantity property handling and add test
2 parents 615c89b + 3e829f5 commit 1c37bb6

File tree

2 files changed

+33
-20
lines changed

2 files changed

+33
-20
lines changed

neo/io/nixio.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,13 +1235,7 @@ def _write_property(self, section, name, v):
12351235
:return: The newly created property
12361236
"""
12371237

1238-
if isinstance(v, pq.Quantity):
1239-
if len(v.shape):
1240-
section.create_property(name, tuple(v.magnitude))
1241-
else:
1242-
section.create_property(name, v.magnitude.item())
1243-
section.props[name].unit = str(v.dimensionality)
1244-
elif isinstance(v, datetime_types):
1238+
if isinstance(v, datetime_types):
12451239
value, annotype = dt_to_nix(v)
12461240
prop = section.create_property(name, value)
12471241
prop.definition = annotype
@@ -1256,22 +1250,27 @@ def _write_property(self, section, name, v):
12561250
values = []
12571251
unit = None
12581252
definition = None
1259-
if len(v) == 0:
1253+
# handling (quantity) arrays with only a single element
1254+
if hasattr(v, "ndim") and v.ndim == 0:
1255+
values = v.item()
1256+
# handling empty arrays or lists
1257+
elif (hasattr(v, 'size') and (v.size == 0)) or (len(v) == 0):
12601258
# NIX supports empty properties but dtype must be specified
12611259
# Defaulting to String and using definition to signify empty
12621260
# iterable as opposed to empty string
12631261
values = nix.DataType.String
12641262
definition = EMPTYANNOTATION
1265-
elif hasattr(v, "ndim") and v.ndim == 0:
1266-
values = v.item()
1267-
if isinstance(v, pq.Quantity):
1268-
unit = str(v.dimensionality)
12691263
else:
12701264
for item in v:
12711265
if isinstance(item, str):
12721266
item = item
12731267
elif isinstance(item, pq.Quantity):
1274-
unit = str(item.dimensionality)
1268+
current_unit = str(item.dimensionality)
1269+
if unit is None:
1270+
unit = current_unit
1271+
elif unit != current_unit:
1272+
raise ValueError(f'Inconsistent units detected for '
1273+
f'property {name}: {v}')
12751274
item = item.magnitude.item()
12761275
elif isinstance(item, Iterable):
12771276
self.logger.warn("Multidimensional arrays and nested "
@@ -1281,6 +1280,8 @@ def _write_property(self, section, name, v):
12811280
else:
12821281
item = item
12831282
values.append(item)
1283+
if hasattr(v, 'dimensionality'):
1284+
unit = str(v.dimensionality)
12841285
section.create_property(name, values)
12851286
section.props[name].unit = unit
12861287
section.props[name].definition = definition
@@ -1308,16 +1309,16 @@ def _nix_attr_to_neo(nix_obj):
13081309
if nix_obj.metadata:
13091310
for prop in nix_obj.metadata.inherited_properties():
13101311
values = list(prop.values)
1311-
if prop.unit:
1312-
units = prop.unit
1313-
values = create_quantity(values, units)
13141312
if not len(values):
13151313
if prop.definition == EMPTYANNOTATION:
13161314
values = list()
13171315
elif prop.data_type == nix.DataType.String:
13181316
values = ""
13191317
elif len(values) == 1:
13201318
values = values[0]
1319+
if prop.unit:
1320+
units = prop.unit
1321+
values = create_quantity(values, units)
13211322
if prop.definition in (DATEANNOTATION, TIMEANNOTATION, DATETIMEANNOTATION):
13221323
values = dt_from_nix(values, prop.definition)
13231324
if prop.type == ARRAYANNOTATION:

neo/test/iotest/test_nixio.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,8 +1430,16 @@ def test_annotations_special_cases(self):
14301430
def test_empty_array_annotations(self):
14311431
wblock = Block("block with spiketrain")
14321432
wseg = Segment()
1433+
empty_array_annotations = {'emptylist': [],
1434+
'emptyarray': np.array([]),
1435+
'quantitylist': [] * pq.s,
1436+
'quantityarray': np.array([]) * pq.s}
1437+
expected_array_annotations = {'emptylist': np.array([]),
1438+
'emptyarray': np.array([]),
1439+
'quantitylist': np.array([]) * pq.s,
1440+
'quantityarray': np.array([]) * pq.s}
14331441
wseg.spiketrains = [SpikeTrain(times=[] * pq.s, t_stop=1 * pq.s,
1434-
array_annotations={'empty': []})]
1442+
array_annotations=empty_array_annotations)]
14351443
wblock.segments = [wseg]
14361444
self.writer.write_block(wblock)
14371445
try:
@@ -1441,9 +1449,13 @@ def test_empty_array_annotations(self):
14411449
+ ' reading the block with an empty array annotation:\n'
14421450
+ str(exc))
14431451
rst = rblock.segments[0].spiketrains[0]
1444-
self.assertEqual(len(rst.array_annotations), 1)
1445-
self.assertIn('empty', rst.array_annotations.keys())
1446-
self.assertEqual(len(rst.array_annotations['empty']), 0)
1452+
for k, v in expected_array_annotations.items():
1453+
self.assertIn(k, rst.array_annotations)
1454+
np.testing.assert_array_equal(rst.array_annotations[k], v)
1455+
if hasattr(v, 'units'):
1456+
self.assertEqual(rst.array_annotations[k].units, v.units)
1457+
else:
1458+
self.assertFalse(hasattr(rst.array_annotations[k], 'units'))
14471459

14481460
def test_write_proxyobjects(self):
14491461

0 commit comments

Comments
 (0)