NeuralEnsemble / python-neo

Neo is a package for representing electrophysiology data in Python, together with support for reading a wide range of neurophysiology file formats
http://neo.readthedocs.io/en/latest/
BSD 3-Clause "New" or "Revised" License
325 stars 248 forks source link

Object description metadata lost/wrong when saving NWB files #1516

Open kohlerca opened 3 months ago

kohlerca commented 3 months ago

When saving Neo objects to NWB files using the NWBIO, the information in the description metadata field of several objects is lost or wrongly changed:

When saving a single block, the behavior for AnalogSignals, Segments and SpikeTrains is similar.

The code below reproduces the behavior:

from datetime import datetime
import numpy as np
import quantities as pq
import neo
from elephant.spike_train_generation import StationaryPoissonProcess

def print_metadata(block):
    print(f"Block: {block.name}, description: {block.description}")
    seg = block.segments[0]
    print(f"\tSegment: {seg.name}, description: {seg.description}")
    signal = seg.analogsignals[0]
    print(f"\t\tSignal: {signal.name}, description: {signal.description}")
    for idx, st in enumerate(seg.spiketrains):
        print(f"\t\tST {idx}: {st.name}, description: {st.description}")

start_time = datetime.now()
blocks = []
for i in range(1, 3):
    block = neo.Block(name=f"Data {i}", description=f"The block {i}",
                      session_start_time=start_time)

    signal = neo.AnalogSignal(np.random.normal(size=(1000, 5)), units=pq.mV,
                              name=f"Signal {i}",
                              description=f"The signal of block {i}",
                              sampling_rate=1000*pq.Hz)

    segment = neo.Segment(name=f"Segment {i}",
                          description=f"The segment of block {i}")

    spiketrains = StationaryPoissonProcess(rate=10*pq.Hz).generate_n_spiketrains(3)
    for idx, st in enumerate(spiketrains, start=1):
        st.name = f"SpikeTrain {idx}"
        st.description = f"SpikeTrain {idx} of block {i}"

    segment.add(signal)
    segment.add(*spiketrains)
    block.add(segment)

    blocks.append(block)

# Inspect the metadata
print_metadata(blocks[0])
print_metadata(blocks[1])

print("\nSaving\n\n")
# Save and load as NWB
nwb_file = "test.nwb"
io = neo.NWBIO(nwb_file, 'w')
io.write_all_blocks(blocks)

print("\nLoading\n\n")
io = neo.NWBIO(nwb_file, 'r')
read_blocks = io.read_all_blocks()

# Inspect the metadata
print_metadata(read_blocks[0])
print_metadata(read_blocks[1])

Output (warnings on existing AnalogSignal names removed):

Block: Data 1, description: The block 1
    Segment: Segment 1, description: The segment of block 1
        Signal: Signal 1, description: The signal of block 1
        ST 0: SpikeTrain 1, description: SpikeTrain 1 of block 1
        ST 1: SpikeTrain 2, description: SpikeTrain 2 of block 1
        ST 2: SpikeTrain 3, description: SpikeTrain 3 of block 1
Block: Data 2, description: The block 2
    Segment: Segment 2, description: The segment of block 2
        Signal: Signal 2, description: The signal of block 2
        ST 0: SpikeTrain 1, description: SpikeTrain 1 of block 2
        ST 1: SpikeTrain 2, description: SpikeTrain 2 of block 2
        ST 2: SpikeTrain 3, description: SpikeTrain 3 of block 2

Saving

Loading

Block: Data 1, description: The block 1
    Segment: Segment 1, description: None
        Signal: Segment 1 Signal 1 0, description: no description
        ST 0: SpikeTrain 1, description: None
        ST 1: SpikeTrain 2, description: None
        ST 2: SpikeTrain 3, description: None
Block: Data 2, description: The block 1
    Segment: Segment 2, description: None
        Signal: Segment 2 Signal 2 0, description: no description
        ST 0: SpikeTrain 1, description: None
        ST 1: SpikeTrain 2, description: None
        ST 2: SpikeTrain 3, description: None

Expected behaviour Preserve metadata when saving to NWB, only changing names to guarantee uniqueness

Environment:

zm711 commented 3 months ago

@apdavison is working on updating the nwb reading/writing in neo so he can read over this