Skip to content

Commit

Permalink
add behavioral data
Browse files Browse the repository at this point in the history
  • Loading branch information
h-mayorquin committed Aug 29, 2024
1 parent 24f821a commit d5f2413
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 1 deletion.
Empty file added src/fox_lab_to_nwb/__init__.py
Empty file.
107 changes: 107 additions & 0 deletions src/fox_lab_to_nwb/behavior.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from pathlib import Path
from typing import Optional

from neuroconv import BaseDataInterface
from pydantic import FilePath
from pynwb import NWBFile
from pynwb import TimeSeries
from pymatreader import read_mat


class BehaviorInterface(BaseDataInterface):
def __init__(self, file_path: FilePath):
self.file_path = Path(file_path)

def add_to_nwbfile(
self,
nwbfile: NWBFile,
metadata: Optional[dict] = None,
) -> None:

mat = read_mat(self.file_path)
rec_struct = mat["rec"]
daq_struct = rec_struct["daq"]

timestamps = daq_struct["tstamps"]

# Extracted from the matlab manually
# neither scipy or pymatreader can read string characters from the .mat file
channel_names = [
"CamSync",
"CamTrigger",
"OptoTrigger",
"LWingBeatAmp",
"RWingBeatAmp",
"WingBeatFreq",
"LHutchen",
"RHutchen",
"PTrigger",
]

# TODO: figure how how to store this
# Synchronization signals
cam_sync = daq_struct["data"][:, 0]
cam_trigger = daq_struct["data"][:, 1]
opto_trigger = daq_struct["data"][:, 2]
ptrigger = daq_struct["data"][:, 8]

# Behavior signals
left_wing_beat_amplitude = daq_struct["data"][:, 3]
right_wing_beat_amplitude = daq_struct["data"][:, 4]
wing_beat_frequency = daq_struct["data"][:, 5]

unit = "tbd"
description = "tbd"
left_wing_beat_amplitude_time_series = TimeSeries(
name="LeftWingBeatAmplitudeTimeSeries",
data=left_wing_beat_amplitude,
unit=unit,
timestamps=timestamps,
description=description,
)

description = "tbd"
right_wing_beat_amplitude_time_series = TimeSeries(
name="RightWingBeatAmplitudeTimeSeries",
data=right_wing_beat_amplitude,
unit=unit,
timestamps=timestamps,
description=description,
)

description = "tbd"
unit = "Hz" # TODO: Figure this out, the values in the plot are around 3 but should be higher for flies
wing_beat_frequency_time_series = TimeSeries(
name="WingBeatFrequencyTimeSeries",
data=wing_beat_frequency,
unit=unit,
timestamps=timestamps,
description=description,
)

nwbfile.add_acquisition(left_wing_beat_amplitude_time_series)
nwbfile.add_acquisition(right_wing_beat_amplitude_time_series)
nwbfile.add_acquisition(wing_beat_frequency_time_series)

# TODO: Ask Ben if this nesting makes sense? probably not
# time_series = [left_wing_beat_amplitude_time_series, right_wing_beat_amplitude_time_series, wing_beat_frequency_time_series]
# behavioral_time_series_container = BehavioralTimeSeries(name="BehavioralTimeSeries", time_series=time_series)
# nwbfile.add_acquisition(behavioral_time_series_container)

# Not clear what are those signals, haltere?
lhutchen = daq_struct["data"][:, 6]
rhutchen = daq_struct["data"][:, 7]

unit = "tbd"
description = "tbd"
lhutchen_time_series = TimeSeries(
name="LHutchenTimeSeries", data=lhutchen, unit=unit, timestamps=timestamps, description=description
)

description = "tbd"
rhutchen_time_series = TimeSeries(
name="RHutchenTimeSeries", data=rhutchen, unit=unit, timestamps=timestamps, description=description
)

nwbfile.add_acquisition(lhutchen_time_series)
nwbfile.add_acquisition(rhutchen_time_series)
73 changes: 73 additions & 0 deletions src/fox_lab_to_nwb/conversion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from pathlib import Path
import time
from datetime import datetime
from typing import Optional
from zoneinfo import ZoneInfo
from neuroconv.utils import dict_deep_update, load_dict_from_file

from neuroconv import ConverterPipe
from fox_lab_to_nwb.behavior import BehaviorInterface


def run_trial_conversion(trial_data_folder: Path, output_dir_path: Optional[Path] = None, verbose: bool = True):

if verbose:
start_time = time.time()

if output_dir_path is None:
output_dir_path = Path.home() / "conversion_nwb"

# session_id = f"{subject}_{session_date}_{session_time}"
session_id = "session"
nwbfile_path = output_dir_path / f"{session_id}.nwb"

file_path = trial_data_folder / "Tshx18D07_240124_115923_f3_r1.fly2"
assert file_path.exists(), f"File {file_path} does not exist"
interface = BehaviorInterface(file_path=file_path)

converter = ConverterPipe(data_interfaces={"behavior": interface})


# Add datetime to conversion
metadata = converter.get_metadata()
session_start_time = datetime.now().astimezone(ZoneInfo("America/New_York"))
metadata["NWBFile"]["session_start_time"] = session_start_time

# Update default metadata with the editable in the corresponding yaml file
editable_metadata_path = Path(__file__).parent / "metadata.yaml"
editable_metadata = load_dict_from_file(editable_metadata_path)
metadata = dict_deep_update(metadata, editable_metadata)

subject_metadata = metadata["Subject"]
subject = "subject"
subject_metadata["subject_id"] = f"{subject}"

# Run conversion, this adds the basic data to the NWBFile
conversion_options = {}
converter.run_conversion(
metadata=metadata,
nwbfile_path=nwbfile_path,
conversion_options=conversion_options,
overwrite=True,
)

if verbose:
stop_time = time.time()
conversion_time_seconds = stop_time - start_time
if conversion_time_seconds <= 60 * 3:
print(f"Conversion took {conversion_time_seconds:.2f} seconds")
elif conversion_time_seconds <= 60 * 60:
print(f"Conversion took {conversion_time_seconds / 60:.2f} minutes")
else:
print(f"Conversion took {conversion_time_seconds / 60 / 60:.2f} hours")


if __name__ == "__main__":

verbose = True
data_path = Path("/home/heberto/cohen_project/Sample data/Fox Lab")
assert data_path.exists(), f"Folder {data_path} does not exist"
trial_data_folder = data_path / "Tshx18D07_240124_115923_f3_r1"
assert trial_data_folder.exists(), f"Folder {trial_data_folder} does not exist"

run_trial_conversion(trial_data_folder=trial_data_folder, verbose=verbose)
3 changes: 2 additions & 1 deletion src/fox_lab_to_nwb/conversion_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ Channel names:
- LWingBeatAmp
- RWingBeatAmp
- WingBeatFreq
- LHutchen
- LHutchen # What does Hutchen mean? Could be halteres?
- RHutchen
- PTrigger

These can't be extracted with pyton from matlab because they are strings. Are they always the same order?

## Cameras
### Fastec
Expand Down
20 changes: 20 additions & 0 deletions src/fox_lab_to_nwb/metadata.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
NWBFile:
keywords:
- Fly
- Wingbeat
- Behavior
related_publications:
- https://doi.org/great_publication or link to APA or MLA citation of the publication
session_description:
A rich text description of the experiment. Can also just be the abstract of the publication.
experiment_description: 'Task: Rapid serial visual presentation (RSVP).'
institution: Case Western Reserve University
lab: Fox
experimenter:
- Streets, Amy
- Lea, Kriss
surgery: TBD
Subject:
sex: M
species: Drosophila melanogaster # TODO: Figure this one out
description: fly

0 comments on commit d5f2413

Please sign in to comment.