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

Update behavior #8

Merged
merged 7 commits into from
Oct 9, 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
Expand Up @@ -2,7 +2,7 @@
from pynwb.file import NWBFile
from pydantic import FilePath
import numpy as np
from h5py import File
from pymatreader import read_mat
from hdmf.common.table import DynamicTableRegion
from pynwb.behavior import BehavioralTimeSeries, TimeSeries
from pynwb.device import Device
Expand Down Expand Up @@ -77,35 +77,52 @@ def get_metadata_schema(self) -> dict:
},
},
}
metadata_schema["properties"]["Behavior"]["properties"]["Trials"] = {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"description": {"type": "string"},
},
},
}
return metadata_schema

def add_to_nwbfile(self, nwbfile: NWBFile, metadata: dict):
# 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()
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"]:
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
file = read_mat(file_path)
behavioral_time_series, name_to_times, name_to_values, name_to_trial_array = [], dict(), dict(), dict()
for time_series_dict in metadata["Behavior"]["TimeSeries"]:
name = time_series_dict["name"]
timestamps = np.array(file["continuous"][name]["time"]).squeeze()
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"]:
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

trial_start_times = np.array(file["events"]["push"]["time"]).squeeze()
trial_stop_times = np.array(file["events"]["push"]["time_end"]).squeeze()
for trials_dict in metadata["Behavior"]["Trials"]:
name = trials_dict["name"]
trial_array = np.array(file["events"]["push"][name]).squeeze()
name_to_trial_array[name] = trial_array

# Add Data to NWBFile
behavior_module = nwb_helpers.get_module(
Expand Down Expand Up @@ -167,6 +184,14 @@ def add_to_nwbfile(self, nwbfile: NWBFile, metadata: dict):
task = Task(event_types=event_types_table)
nwbfile.add_lab_meta_data(task)

# Add Trials Table
for start_time, stop_time in zip(trial_start_times, trial_stop_times):
nwbfile.add_trial(start_time=start_time, stop_time=stop_time)
for trials_dict in metadata["Behavior"]["Trials"]:
name = trials_dict["name"]
trial_array = name_to_trial_array[name]
nwbfile.add_trial_column(name=name, description=trials_dict["description"], data=trial_array)

# Add Devices
for device_kwargs in metadata["Behavior"]["Devices"]:
device = Device(**device_kwargs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@
from schneider_lab_to_nwb.schneider_2024 import Schneider2024NWBConverter


def session_to_nwb(data_dir_path: Union[str, Path], output_dir_path: Union[str, Path], stub_test: bool = False):

data_dir_path = Path(data_dir_path)
def session_to_nwb(
recording_folder_path: Union[str, Path],
pauladkisson marked this conversation as resolved.
Show resolved Hide resolved
sorting_folder_path: Union[str, Path],
behavior_file_path: Union[str, Path],
video_folder_path: Union[str, Path],
output_dir_path: Union[str, Path],
stub_test: bool = False,
):
recording_folder_path = Path(recording_folder_path)
sorting_folder_path = Path(sorting_folder_path)
behavior_file_path = Path(behavior_file_path)
video_folder_path = Path(video_folder_path)
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"
video_folder_path = data_dir_path / "Video" / "m69_231031"
video_file_paths = [
file_path for file_path in video_folder_path.glob("*.mp4") if not file_path.name.startswith("._")
]
Expand Down Expand Up @@ -96,14 +101,25 @@ def session_to_nwb(data_dir_path: Union[str, Path], output_dir_path: Union[str,

def main():
# Parameters for conversion
data_dir_path = Path("/Volumes/T7/CatalystNeuro/Schneider/Schneider sample Data")
data_dir_path = Path("/Volumes/T7/CatalystNeuro/Schneider")
output_dir_path = Path("/Volumes/T7/CatalystNeuro/Schneider/conversion_nwb")
stub_test = True

if output_dir_path.exists():
shutil.rmtree(output_dir_path, ignore_errors=True)

# Example Session w/ old ephys + new behavior
recording_folder_path = data_dir_path / "Schneider sample Data" / "Raw Ephys" / "m69_2023-10-31_17-24-15_Day1_A1"
sorting_folder_path = (
data_dir_path / "Schneider sample Data" / "Processed Ephys" / "m69_2023-10-31_17-24-15_Day1_A1"
)
behavior_file_path = data_dir_path / "NWB_Share" / "Sample behavior data" / "m74_ephysSample.mat"
video_folder_path = data_dir_path / "Schneider sample Data" / "Video" / "m69_231031"
session_to_nwb(
data_dir_path=data_dir_path,
recording_folder_path=recording_folder_path,
sorting_folder_path=sorting_folder_path,
behavior_file_path=behavior_file_path,
video_folder_path=video_folder_path,
output_dir_path=output_dir_path,
stub_test=stub_test,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,41 @@ Behavior:
- name: lick
description: Samples values for entire duration of experiment for voltage signal readout from a custom infrared/capacitive lickometer sensor (Schneider Lab). Digital signals for licking and lever movement were collected by a data acquisition card (National Instruments) connected to a computer and logged by custom Matlab software (Mathworks, PsychToolBox) and sampled at 2kHz.
Events:
- name: target
description: Time at which the target zone is entered during a press.
- name: toneIN
description: Time at which target zone is entered and target entry tone is played.
- 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.
description: Time at which target exit tone is played (this is delayed 50ms relative to targetOUT so that entry and exit tones don't bleed into each other.
- 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.
description: Times at which tuning tones are played to an animal after a behavioral experiment during ephys recording sessions. Integer values correspond to the frequency of the tuning tone played i.e. 2 for 2kHz, 4 for 4kHz, etc.
Devices:
- name: rotary_encoder
description: H5 BALL BEARING OPTICAL SHAFT ENCODER
manufacturer: US Digital
- name: lickometer
description: The lickometer comprised a custom-mounted (3D printed using Formlabs Form2) IR-beam emitter and receiver. IR signal was titrated and pre-processed using a custom printed circuit board (designed by Melissa Caras and Dan Sanes) to generate a binary TTL signal with IR sensitivity controlled by a potentiometer.
manufacturer: Schneider Lab
Trials:
- name: rewarded
description: Indicates if trial was rewarded (NaN = trial not rewarded, 1 = trial rewarded).
- name: time_reward_s
description: Time of reward if rewarded, otherwise NaN.
- name: opto_trial
description: Indicates if trial was an optogenetic stimulation trial (NaN = non opto trial, 1 = opto trial).
- name: opto_time
description: Time of optogenetic stimulation if opto trial, otherwise NaN.
- name: opto_time_end
description: Time of start of optogenetic stimulation if it occurs, otherwise NaN.
- name: ITI_respect
description: Whether or not trial start obeyed inter trial interval wait time (300ms).
- name: ThresholdVector
description: Position of start of the target zone on a given trial (in raw encoder values corresponding to value read out by quadrature encoder).
- name: endZone_ThresholdVector
description: Position of ending/exit position of target zone on a given trial (in raw encoder values corresponding to value read out by quadrature encoder).
VideoCamera1:
- name: video_camera_1
description: Two IR video cameras (AAK CA20 600TVL 2.8MM) are used to monitor the experiments from different angles of interest, allowing for offline analysis of body movements, pupillometry, and other behavioral data if necessary. Camera 1 is a side angle view of the mouse.
Expand All @@ -62,13 +84,6 @@ Behavior:
- name: video_camera_2
description: Two IR video cameras (AAK CA20 600TVL 2.8MM) are used to monitor the experiments from different angles of interest, allowing for offline analysis of body movements, pupillometry, and other behavioral data if necessary. Camera 2 is a zoomed-in view of the pupil of the mouse.
unit: Frames
Devices:
pauladkisson marked this conversation as resolved.
Show resolved Hide resolved
- name: rotary_encoder
description: H5 BALL BEARING OPTICAL SHAFT ENCODER
manufacturer: US Digital
- name: lickometer
description: The lickometer comprised a custom-mounted (3D printed using Formlabs Form2) IR-beam emitter and receiver. IR signal was titrated and pre-processed using a custom printed circuit board (designed by Melissa Caras and Dan Sanes) to generate a binary TTL signal with IR sensitivity controlled by a potentiometer.
manufacturer: Schneider Lab

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,6 @@
# 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?
pauladkisson marked this conversation as resolved.
Show resolved Hide resolved
- 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?

## Video

Expand Down
Loading