Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prototype Behavior Interface #3

Merged
merged 8 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,25 +1,115 @@
"""Primary class for converting experiment-specific behavior."""
from pynwb.file import NWBFile
from pydantic import FilePath
import numpy as np
from h5py import File
pauladkisson marked this conversation as resolved.
Show resolved Hide resolved
from hdmf.common.table import DynamicTableRegion
from pynwb.behavior import BehavioralTimeSeries, TimeSeries
from ndx_events import EventTypesTable, EventsTable, Task, TimestampVectorData

from neuroconv.basedatainterface import BaseDataInterface
from neuroconv.utils import DeepDict
from neuroconv.tools import nwb_helpers


class Schneider2024BehaviorInterface(BaseDataInterface):
"""Behavior interface for schneider_2024 conversion"""

keywords = ["behavior"]

def __init__(self):
# This should load the data lazily and prepare variables you need
pass

def __init__(self, file_path: FilePath):
super().__init__(file_path=file_path)

def get_metadata(self) -> DeepDict:
# Automatically retrieve as much metadata as possible from the source files available
metadata = super().get_metadata()
metadata = super().get_metadata()

return metadata

def add_to_nwbfile(self, nwbfile: NWBFile, metadata: dict):
# All the custom code to add the data the nwbfile
# Read Data
file_path = self.source_data["file_path"]
with File(file_path, "r") as file:
behavioral_time_series, name_to_times, name_to_values = [], dict(), dict()
for time_series_dict in metadata["Behavior"]["TimeSeries"]:
name = time_series_dict["name"]
timestamps = np.array(file["continuous"][name]["time"]).squeeze()
pauladkisson marked this conversation as resolved.
Show resolved Hide resolved
data = np.array(file["continuous"][name]["value"]).squeeze()
time_series = TimeSeries(
name=name,
timestamps=timestamps,
data=data,
unit="a.u.",
description=time_series_dict["description"],
)
behavioral_time_series.append(time_series)
for event_dict in metadata["Behavior"]["Events"]:
name = event_dict["name"]
times = np.array(file["events"][name]["time"]).squeeze()
name_to_times[name] = times
for event_dict in metadata["Behavior"]["ValuedEvents"]:
pauladkisson marked this conversation as resolved.
Show resolved Hide resolved
name = event_dict["name"]
times = np.array(file["events"][name]["time"]).squeeze()
values = np.array(file["events"][name]["value"]).squeeze()
name_to_times[name] = times
name_to_values[name] = values

# Add Data to NWBFile
behavior_module = nwb_helpers.get_module(
nwbfile=nwbfile,
name="behavior",
description="Behavioral data from the experiment.",
pauladkisson marked this conversation as resolved.
Show resolved Hide resolved
)

# Add BehavioralTimeSeries
behavioral_time_series = BehavioralTimeSeries(
time_series=behavioral_time_series,
name="behavioral_time_series",
pauladkisson marked this conversation as resolved.
Show resolved Hide resolved
)
behavior_module.add(behavioral_time_series)

# Add Events
event_types_table = EventTypesTable(name="event_types", description="Metadata about event types.")
event_type_name_to_row = dict()
i = 0
for event_dict in metadata["Behavior"]["Events"]:
event_type_name_to_row[event_dict["name"]] = i
event_types_table.add_row(
event_name=event_dict["name"],
event_type_description=event_dict["description"],
)
i += 1
for event_dict in metadata["Behavior"]["ValuedEvents"]:
event_type_name_to_row[event_dict["name"]] = i
event_types_table.add_row(
event_name=event_dict["name"],
event_type_description=event_dict["description"],
)
i += 1
events_table = EventsTable(
name="events_table",
description="Metadata about events.",
target_tables={"event_type": event_types_table},
)
for event_dict in metadata["Behavior"]["Events"]:
event_times = name_to_times[event_dict["name"]]
event_type = event_type_name_to_row[event_dict["name"]]
for event_time in event_times:
events_table.add_row(timestamp=event_time, event_type=event_type)
valued_events_table = EventsTable(
name="valued_events_table",
description="Metadata about valued events.",
target_tables={"event_type": event_types_table},
)
valued_events_table.add_column(name="value", description="Value of the event.")
for event_dict in metadata["Behavior"]["ValuedEvents"]:
event_times = name_to_times[event_dict["name"]]
event_values = name_to_values[event_dict["name"]]
event_type = event_type_name_to_row[event_dict["name"]]
for event_time, event_value in zip(event_times, event_values):
valued_events_table.add_row(timestamp=event_time, event_type=event_type, value=event_value)
behavior_module.add(events_table)
behavior_module.add(valued_events_table)

raise NotImplementedError()
task = Task(event_types=event_types_table)
pauladkisson marked this conversation as resolved.
Show resolved Hide resolved
nwbfile.add_lab_meta_data(task)
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def session_to_nwb(data_dir_path: Union[str, Path], output_dir_path: Union[str,
output_dir_path = Path(output_dir_path)
recording_folder_path = data_dir_path / "Raw Ephys" / "m69_2023-10-31_17-24-15_Day1_A1"
sorting_folder_path = data_dir_path / "Processed Ephys" / "m69_2023-10-31_17-24-15_Day1_A1"
behavior_file_path = data_dir_path / "Behavior" / "m69_231031" / "raw_m69_231031_001.mat"
if stub_test:
output_dir_path = output_dir_path / "nwb_stub"
recording_folder_path = recording_folder_path.with_name(recording_folder_path.name + "_stubbed")
Expand All @@ -38,9 +39,9 @@ def session_to_nwb(data_dir_path: Union[str, Path], output_dir_path: Union[str,
source_data.update(dict(Sorting=dict(folder_path=sorting_folder_path)))
conversion_options.update(dict(Sorting=dict()))

# # Add Behavior
# source_data.update(dict(Behavior=dict()))
# conversion_options.update(dict(Behavior=dict()))
# Add Behavior
source_data.update(dict(Behavior=dict(file_path=behavior_file_path)))
conversion_options.update(dict(Behavior=dict()))

converter = Schneider2024NWBConverter(source_data=source_data)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,27 @@ Ecephys:
ElectricalSeries:
- name: ElectricalSeries
description: Recording of AC neural responses in mice performing this behavioral task will utilize dense 128-channel recording probes (Masmanidis Lab). These recording probes span a depth ~1mm, allowing for sampling of all layers of cortex. Electrophysiology data will be recorded using OpenEphys Acquisition Board v2.4 and associated OpenEphys GUI software.
# UnitProperties:
# - name: n_spikes
# description: Number of spikes recorded from each unit.
# - name: fr
# description: Average firing rate of each unit.
# - name: depth
# description: Estimated depth of each unit in micrometers.
# - name: Amplitude
# description: Per-template amplitudes, computed as the L2 norm of the template.
# - name: ContamPct
# description: Contamination rate for each template, computed as fraction of refractory period violations relative to expectation based on a Poisson process.
# - name: KSLabel
# description: Label indicating whether each template is 'mua' (multi-unit activity) or 'good' (refractory).
# - name: original_cluster_id
# description: Original cluster ID assigned by Kilosort.
# - name: amp
# description: For every template, the maximum amplitude of the template waveforms across all channels.
# - name: ch
# description: The channel label of the best channel, as defined by the user.
# - name: sh
# description: The shank label of the best channel.

Behavior:
TimeSeries:
- name: encoder
description: Sampled values for entire duration of experiment for lever pressing/treadmill behavior read from a quadrature encoder.
- name: lick
description: Samples values for entire duration of experiment for voltage signal readout from an infrared/capacitive) lickometer sensor.
pauladkisson marked this conversation as resolved.
Show resolved Hide resolved
Events:
- name: target
description: Time at which the target zone is entered during a press.
- name: targetOUT
description: Time at which the target zone is overshot during a press.
- name: toneIN
description: Time at which target entry tone is played.
- name: toneOUT
description: Time at which target exit tone is played.
- name: valve
description: Times at which solenoid valve opens to deliver water after a correct trial.
ValuedEvents:
- name: tuningTones
description: Times at which tuning tones are played to an animal after a behavioral experiment during ephys recording sessions.

Sorting:
units_description: Neural spikes will be sorted offline using Kilosort 2.5 and Phy2 software and manually curated to ensure precise spike time acquisition.
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
# Notes concerning the schneider_2024 conversion

## Behavior
- opto events is just [0, 1] like empty variables (lick events), and opto_time in 'push' is all NaNs. Where is the actual opto stim times recorded?
- TuningTones has a values field with integers, what does this correspond to?
- 'push' is basically some kind of trials table, but need descriptions for variables ex. ITI_respect?

## Data Requests
- Mice sexes
- Remaining data for Grant's project
- More detailed position info for recording probe
- Subfield of auditory cortex: A1? A2? AAF? etc.
- stereotactic coordinates of the whole probe
-
- Detailed description of the behavioral paradigm
- Description of lickometer and lever/treadmill quadrature encoder.
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ class Schneider2024NWBConverter(NWBConverter):
data_interface_classes = dict(
Recording=OpenEphysRecordingInterface,
Sorting=PhySortingInterface,
Behavior=Schneider2024BehaviorInterface,
)