Skip to content

Commit 83180f8

Browse files
authored
Generalize photometry plotting functions, add plots for LabVIEW photometry processing (calderast#142)
* Generalize photometry plotting functions * Plot labview processing pipeline and photometry signals
1 parent 0cf2742 commit 83180f8

File tree

3 files changed

+198
-197
lines changed

3 files changed

+198
-197
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,5 @@ frank_lab/data/*
183183
# Other
184184
example_data/*
185185
metadata/*
186+
output/*
187+
nwbs/*

src/jdb_to_nwb/convert_photometry.py

Lines changed: 93 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,8 @@
1818

1919
from ndx_fiber_photometry import FiberPhotometryResponseSeries, ExcitationSource, OpticalFiber, Photodetector
2020
from .plotting.plot_photometry import (
21-
plot_raw_photometry_signals,
22-
plot_405_470_correlation,
23-
plot_405_565_correlation,
24-
plot_470_565_correlation,
25-
plot_ratio_565_correlation,
26-
plot_normalized_signals
21+
plot_signal_correlation,
22+
plot_photometry_signals,
2723
)
2824

2925
# Get the location of the resources directory when the package is installed from pypi
@@ -501,15 +497,12 @@ def process_and_add_pyphotometry_to_nwb(nwbfile: NWBFile, ppd_file_path, logger,
501497
raw_405 = pd.Series(ppd_data['analog_3'])
502498
relative_raw_signal = raw_green / raw_405
503499

504-
# Get port visits and sampling rate from ppd file
500+
# Get port visits (in photometry sample time) and sampling rate from ppd file
505501
visits = ppd_data['pulse_inds_1'][1:]
506502
logger.debug(f"There were {len(visits)} port visits recorded by pyPhotometry")
507503
sampling_rate = ppd_data['sampling_rate']
508504
logger.info(f"pyPhotometry sampling rate: {sampling_rate} Hz")
509505

510-
# Convert port visits to seconds
511-
visits = [visit_time / sampling_rate for visit_time in visits]
512-
513506
# Convert pyphotometry photometry start time to datetime object and set timezone to Pacific Time
514507
photometry_start = datetime.strptime(ppd_data['date_time'], "%Y-%m-%dT%H:%M:%S.%f")
515508
photometry_start = photometry_start.replace(tzinfo=ZoneInfo("America/Los_Angeles"))
@@ -519,14 +512,20 @@ def process_and_add_pyphotometry_to_nwb(nwbfile: NWBFile, ppd_file_path, logger,
519512
for phot_key in ppd_data:
520513
logger.debug(f"{phot_key}: {ppd_data[phot_key]}")
521514

522-
# Plot the raw signals
523-
plot_raw_photometry_signals(visits, raw_green, raw_red, raw_405, relative_raw_signal,
524-
sampling_rate, fig_dir)
515+
# Plot the raw pyPhotometry signals
516+
plot_photometry_signals(visits=visits,
517+
sampling_rate=sampling_rate,
518+
signals=[raw_green, raw_405, relative_raw_signal, raw_red],
519+
signal_labels=["Raw 470", "Raw 405", "Raw 470/405 ratio", "Raw 565"],
520+
signal_colors=["blue", "purple", "grey", "red"],
521+
title="Raw pyPhotometry signals",
522+
signal_units=["V", "V", "ratio", "V"],
523+
fig_dir=fig_dir)
525524

526525
# Plot the correlation between the raw signals
527-
plot_405_470_correlation(raw_405, raw_green, fig_dir)
528-
plot_405_565_correlation(raw_405, raw_red, fig_dir)
529-
plot_470_565_correlation(raw_green, raw_red, fig_dir)
526+
plot_signal_correlation(sig1=raw_405, sig2=raw_green, label1='Raw 405', label2='Raw 470', fig_dir=fig_dir)
527+
plot_signal_correlation(sig1=raw_405, sig2=raw_red, label1='Raw 405', label2='Raw 565', fig_dir=fig_dir)
528+
plot_signal_correlation(sig1=raw_green, sig2=raw_red, label1='Raw 470', label2='Raw 565', fig_dir=fig_dir)
530529

531530
# Low pass filter at 10Hz to remove high frequency noise
532531
print('Filtering data...')
@@ -549,10 +548,11 @@ def process_and_add_pyphotometry_to_nwb(nwbfile: NWBFile, ppd_file_path, logger,
549548
red_highpass = filtfilt(b,a, red_denoised, padtype='even')
550549
ratio_highpass = filtfilt(b,a, ratio_denoised, padtype='even')
551550
highpass_405 = filtfilt(b,a, denoised_405, padtype='even')
552-
551+
553552
# Plot the correlation between the filtered signals of interest (ACh and DA)
554-
plot_ratio_565_correlation(ratio_highpass, red_highpass, fig_dir)
555-
553+
plot_signal_correlation(sig1=ratio_highpass, sig2=red_highpass,
554+
label1='GACh3.8 470/405 ratio', label2='rDA3m', fig_dir=fig_dir)
555+
556556
# Z-score each signal to normalize the data
557557
print('Z-scoring photometry signals...')
558558
logger.info('Z-scoring filtered photometry signals...')
@@ -561,10 +561,16 @@ def process_and_add_pyphotometry_to_nwb(nwbfile: NWBFile, ppd_file_path, logger,
561561
zscored_405 = np.divide(np.subtract(highpass_405,highpass_405.mean()),highpass_405.std())
562562
ratio_zscored = np.divide(np.subtract(ratio_highpass,ratio_highpass.mean()),ratio_highpass.std())
563563

564-
# Plot the processed photometry signals
565-
plot_normalized_signals(visits, green_zscored, zscored_405,
566-
red_zscored, ratio_zscored, sampling_rate, fig_dir)
567-
564+
# Plot the processed pyPhotometry signals
565+
plot_photometry_signals(visits=visits,
566+
sampling_rate=sampling_rate,
567+
signals=[green_zscored, zscored_405, ratio_zscored, red_zscored],
568+
signal_labels=["ACh3.8 470nm", "ACh3.8 405nm", "ACh3.8 470/405 ratio", "rDA3m 565nm"],
569+
signal_colors=["blue", "purple", "grey", "red"],
570+
title="Processed pyPhotometry signals",
571+
signal_units="Z-score",
572+
fig_dir=fig_dir)
573+
568574
# Add photometry signals to the NWB
569575
print("Adding photometry signals to NWB...")
570576
logger.info("Adding photometry signals to NWB...")
@@ -643,14 +649,17 @@ def process_and_add_pyphotometry_to_nwb(nwbfile: NWBFile, ppd_file_path, logger,
643649
nwbfile.add_acquisition(z_scored_565_response_series)
644650
nwbfile.add_acquisition(z_scored_ratio_response_series)
645651

652+
# Convert port visits to seconds to use for alignment
653+
visits_in_seconds = [visit_time / sampling_rate for visit_time in visits]
654+
646655
# Return photometry start time, sampling rate, and port visit times in seconds to use for alignment
647656
# Add 'signals_to_plot' indicating processed signals to plot aligned to port entry (after behavior is parsed)
648657
signals_to_plot = ["zscored_565", "zscored_470_405_ratio"]
649-
return {'sampling_rate': sampling_rate, 'port_visits': visits,
658+
return {'sampling_rate': sampling_rate, 'port_visits': visits_in_seconds,
650659
'photometry_start': photometry_start, 'signals_to_plot': signals_to_plot}
651660

652661

653-
def process_and_add_labview_to_nwb(nwbfile: NWBFile, signals, logger):
662+
def process_and_add_labview_to_nwb(nwbfile: NWBFile, signals, logger, fig_dir=None):
654663
"""
655664
Process LabVIEW signals and add the processed signals to the NWB file.
656665
@@ -678,8 +687,19 @@ def process_and_add_labview_to_nwb(nwbfile: NWBFile, signals, logger):
678687
raw_green = pd.Series(np.squeeze(signals["sig1"])[:: int(SR / Fs)])
679688
port_visits = np.divide(np.squeeze(signals["visits"]), SR / Fs).astype(int)
680689

681-
# Convert port visits to seconds
682-
port_visits = [visit_time / Fs for visit_time in port_visits]
690+
# Plot the raw LabVIEW signals
691+
plot_photometry_signals(visits=port_visits,
692+
sampling_rate=Fs,
693+
signals=[raw_green, raw_reference],
694+
signal_labels=["Raw 470nm signal", "Raw 405nm signal"],
695+
signal_colors=["blue", "purple"],
696+
title="Raw LabVIEW photometry signals",
697+
signal_units="a.u.",
698+
fig_dir=fig_dir)
699+
700+
# Plot the correlation between the raw 405nm and 470nm signals
701+
plot_signal_correlation(sig1=raw_green, sig2=raw_reference,
702+
label1="Raw 470", label2="Raw 405", fig_dir=fig_dir)
683703

684704
# Smooth the signals using a rolling mean
685705
smooth_window = int(Fs / 30)
@@ -737,6 +757,45 @@ def process_and_add_labview_to_nwb(nwbfile: NWBFile, signals, logger):
737757
logger.info("Calculating deltaF/F via subtraction (z_scored_green - z_scored_reference_fitted)...")
738758
z_scored_green_dFF = z_scored_green - z_scored_reference_fitted
739759

760+
# Plot the processing steps for 470nm wavelength
761+
signals_to_plot = [raw_green, signal_green, baseline_subtracted_green, z_scored_green]
762+
signal_labels = ["Raw 470nm signal", "Smoothed 470nm signal",
763+
"Baseline-subtracted 470nm signal", "Z-scored 470nm signal"]
764+
plot_photometry_signals(visits=port_visits,
765+
sampling_rate=Fs,
766+
signals=signals_to_plot,
767+
signal_labels=signal_labels,
768+
title="470nm signal processing",
769+
signal_units=["a.u.", "a.u.", "a.u.", "Z-score"],
770+
overlay_signals=[(green_baseline, 1, "red", "airPLS baseline")],
771+
fig_dir=fig_dir)
772+
773+
# Plot the processing steps for 405nm wavelength
774+
signals_to_plot = [raw_reference, reference, baseline_subtracted_ref, z_scored_reference]
775+
signal_labels = ["Raw 405nm signal", "Smoothed 405nm signal",
776+
"Baseline-subtracted 405nm signal", "Z-scored 405nm signal"]
777+
plot_photometry_signals(visits=port_visits,
778+
sampling_rate=Fs,
779+
signals=signals_to_plot,
780+
signal_labels=signal_labels,
781+
title="405nm signal processing",
782+
signal_units=["a.u.", "a.u.", "a.u.", "Z-score"],
783+
overlay_signals=[(ref_baseline, 1, "red", "airPLS baseline")],
784+
fig_dir=fig_dir)
785+
786+
# Plot steps of isosbestic correction
787+
signals_to_plot = [z_scored_green, z_scored_reference, z_scored_reference_fitted, z_scored_green_dFF]
788+
signal_labels = ["Z-scored 470nm signal", "Z-scored 405nm signal",
789+
"Predicted 470nm signal from 405nm signal", "Z-scored dF/F (post isosbestic correction)"]
790+
plot_photometry_signals(visits=port_visits,
791+
sampling_rate=Fs,
792+
signals=signals_to_plot,
793+
signal_labels=signal_labels,
794+
signal_colors=["blue", "purple", "gray", "green"],
795+
title="dLight isosbestic correction",
796+
signal_units=["Z-score", "Z-score", "Z-score", "Z-score"],
797+
fig_dir=fig_dir)
798+
740799
# Add photometry signals to the NWB
741800
print("Adding photometry signals to NWB...")
742801
logger.info("Adding photometry signals to NWB...")
@@ -777,10 +836,13 @@ def process_and_add_labview_to_nwb(nwbfile: NWBFile, signals, logger):
777836
nwbfile.add_acquisition(raw_green_response_series)
778837
nwbfile.add_acquisition(raw_reference_response_series)
779838

839+
# Convert port visits to seconds to use for alignment
840+
visits_in_seconds = [visit_time / Fs for visit_time in port_visits]
841+
780842
# Return photometry start time, sampling rate, and port visit times in seconds to use for alignment
781843
# Add 'signals_to_plot' indicating processed signals to plot aligned to port entry (after behavior is parsed)
782844
signals_to_plot = ['z_scored_green_dFF']
783-
return {'sampling_rate': Fs, 'port_visits': port_visits,
845+
return {'sampling_rate': Fs, 'port_visits': visits_in_seconds,
784846
'photometry_start': signals.get('photometry_start'), 'signals_to_plot': signals_to_plot}
785847

786848

@@ -907,7 +969,7 @@ def add_photometry(nwbfile: NWBFile, metadata: dict, logger, fig_dir=None):
907969
phot_file_path = metadata["photometry"]["phot_file_path"]
908970
box_file_path = metadata["photometry"]["box_file_path"]
909971
signals = process_raw_labview_photometry_signals(phot_file_path, box_file_path, logger)
910-
photometry_data_dict = process_and_add_labview_to_nwb(nwbfile, signals, logger)
972+
photometry_data_dict = process_and_add_labview_to_nwb(nwbfile, signals, logger, fig_dir)
911973

912974
# If we have already processed the LabVIEW .phot and .box files into signals.mat (true for older recordings)
913975
elif "signals_mat_file_path" in metadata["photometry"]:
@@ -919,8 +981,8 @@ def add_photometry(nwbfile: NWBFile, metadata: dict, logger, fig_dir=None):
919981
print("Processing signals.mat file of photometry signals from LabVIEW...")
920982
signals_mat_file_path = metadata["photometry"]["signals_mat_file_path"]
921983
signals = scipy.io.loadmat(signals_mat_file_path, matlab_compatible=True)
922-
photometry_data_dict = process_and_add_labview_to_nwb(nwbfile, signals, logger)
923-
984+
photometry_data_dict = process_and_add_labview_to_nwb(nwbfile, signals, logger, fig_dir)
985+
924986
# If we have a ppd file from pyPhotometry
925987
elif "ppd_file_path" in metadata["photometry"]:
926988
# Process ppd file from pyPhotometry and add signals to the NWB

0 commit comments

Comments
 (0)