From 0c2c3bd9e9d713be0370975175bfa553d68e3ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaquier=20Aur=C3=A9lien=20Tristan?= Date: Mon, 22 Jul 2024 11:56:55 +0200 Subject: [PATCH 1/4] add impedance plotting functions --- bluepyefe/extract.py | 6 +++- bluepyefe/plotting.py | 71 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/bluepyefe/extract.py b/bluepyefe/extract.py index 167a1a3..0193040 100644 --- a/bluepyefe/extract.py +++ b/bluepyefe/extract.py @@ -1042,7 +1042,11 @@ def extract_efeatures( if plot: plot_all_recordings_efeatures( - cells, protocols, output_dir=output_directory, mapper=map_function + cells, + protocols, + output_dir=output_directory, + mapper=map_function, + efel_settings=efel_settings ) if extract_per_cell and write_files: diff --git a/bluepyefe/plotting.py b/bluepyefe/plotting.py index e98d21f..2bc56b9 100644 --- a/bluepyefe/plotting.py +++ b/bluepyefe/plotting.py @@ -343,7 +343,7 @@ def plot_grouped_efeatures( def plot_all_recordings_efeatures( - cells, protocols, output_dir=None, show=False, mapper=map + cells, protocols, output_dir=None, show=False, mapper=map, efel_settings=None ): """Generate plots for all recordings and efeatures both for individual cells and across all cells.""" @@ -351,6 +351,7 @@ def plot_all_recordings_efeatures( colors, markers = _get_colors_markers_wheels(cells) plot_all_recordings(cells, output_dir, mapper=mapper) + plot_all_impedances(cells, output_dir, mapper=map, efel_settings=efel_settings) for key_amp in ["amp", "amp_rel"]: plot_individual_efeatures( @@ -372,3 +373,71 @@ def plot_all_recordings_efeatures( key_amp=key_amp, show=show ) + + +def plot_impedance(cell, output_dir, efel_settings, dt=0.1, Z_max_freq=50): + """Plots the impedance.""" + from scipy.ndimage.filters import gaussian_filter1d + + for protocol_name in cell.get_protocol_names(): + if "sinespec" in protocol_name.lower(): + recordings = cell.get_recordings_by_protocol_name(protocol_name) + for rec in recordings: + voltage = rec.voltage + current = rec.current + + efel_vals = rec.call_efel( + [ + "voltage_base", + "steady_state_voltage_stimend", + "current_base", + "steady_state_current_stimend", + ], + efel_settings + ) + if efel_vals[0]["voltage_base"] is not None: + holding_voltage = efel_vals[0]["voltage_base"][0] + else: + holding_voltage = efel_vals[0]["steady_state_voltage_stimend"][0] + if efel_vals[0]["current_base"] is not None: + holding_current = efel_vals[0]["current_base"][0] + else: + holding_current = efel_vals[0]["steady_state_current_stimend"][0] + + normalized_voltage = voltage - holding_voltage + normalized_current = current - holding_current + + fft_volt = numpy.fft.fft(normalized_voltage) + fft_cur = numpy.fft.fft(normalized_current) + if any(fft_cur) == 0: + return None + # convert dt from ms to s to have freq in Hz + freq = numpy.fft.fftfreq(len(normalized_voltage), d=dt / 1000.) + Z = fft_volt / fft_cur + norm_Z = abs(Z) / max(abs(Z)) + select_idxs = numpy.swapaxes( + numpy.argwhere((freq > 0) & (freq <= Z_max_freq)), 0, 1 + )[0] + smooth_Z = gaussian_filter1d(norm_Z[select_idxs], 10) + + filename = "{}_{}_impedance.pdf".format( + cell.name, protocol_name + ) + dirname = pathlib.Path(output_dir) / cell.name / "impedance" + + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + ax.plot(freq, smooth_Z) + _save_fig(dirname, filename) + plt.close("all") + plt.clf() + + +def plot_all_impedances(cells, output_dir, mapper=map, efel_settings=None): + """Plot recordings for all cells and all protocols""" + if mapper == map: + # For the built-in map(), ensure immediate evaluation as it returns a lazy iterator + # which won't execute the function until iterated over. Converting to a list forces this iteration. + list(mapper(partial(plot_impedance, output_dir=output_dir, efel_settings=efel_settings), cells)) + else: + mapper(partial(plot_impedance, output_dir=output_dir, efel_settings=efel_settings), cells) From 3d312c705e703850bd1129d3557ef5ba9b755210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaquier=20Aur=C3=A9lien=20Tristan?= Date: Tue, 23 Jul 2024 13:14:49 +0200 Subject: [PATCH 2/4] add impedance plotting feature --- bluepyefe/plotting.py | 87 +++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/bluepyefe/plotting.py b/bluepyefe/plotting.py index 2bc56b9..192904c 100644 --- a/bluepyefe/plotting.py +++ b/bluepyefe/plotting.py @@ -342,47 +342,20 @@ def plot_grouped_efeatures( _ = _plot_legend(colors, markers, output_dir, show=show) -def plot_all_recordings_efeatures( - cells, protocols, output_dir=None, show=False, mapper=map, efel_settings=None -): - """Generate plots for all recordings and efeatures both for individual - cells and across all cells.""" - - colors, markers = _get_colors_markers_wheels(cells) - - plot_all_recordings(cells, output_dir, mapper=mapper) - plot_all_impedances(cells, output_dir, mapper=map, efel_settings=efel_settings) - - for key_amp in ["amp", "amp_rel"]: - plot_individual_efeatures( - cells, - protocols, - output_dir, - colors=colors, - markers=markers, - key_amp=key_amp, - show=show, - mapper=mapper, - ) - plot_grouped_efeatures( - cells, - protocols, - output_dir, - colors=colors, - markers=markers, - key_amp=key_amp, - show=show - ) - - -def plot_impedance(cell, output_dir, efel_settings, dt=0.1, Z_max_freq=50): +def plot_impedance(cell, output_dir, efel_settings): """Plots the impedance.""" from scipy.ndimage.filters import gaussian_filter1d + dt = 0.1 + Z_max_freq = 50.0 + if efel_settings is not None: + dt = efel_settings.get("interp_step", dt) + Z_max_freq = efel_settings.get("impedance_max_freq", Z_max_freq) + for protocol_name in cell.get_protocol_names(): if "sinespec" in protocol_name.lower(): recordings = cell.get_recordings_by_protocol_name(protocol_name) - for rec in recordings: + for i, rec in enumerate(recordings): voltage = rec.voltage current = rec.current @@ -403,7 +376,7 @@ def plot_impedance(cell, output_dir, efel_settings, dt=0.1, Z_max_freq=50): holding_current = efel_vals[0]["current_base"][0] else: holding_current = efel_vals[0]["steady_state_current_stimend"][0] - + normalized_voltage = voltage - holding_voltage normalized_current = current - holding_current @@ -420,17 +393,16 @@ def plot_impedance(cell, output_dir, efel_settings, dt=0.1, Z_max_freq=50): )[0] smooth_Z = gaussian_filter1d(norm_Z[select_idxs], 10) - filename = "{}_{}_impedance.pdf".format( - cell.name, protocol_name + amp = rec.amp if rec.amp is not None else 0.0 + filename = "{}_{}_{}_{:.3}_impedance.pdf".format( + cell.name, protocol_name, i, amp ) dirname = pathlib.Path(output_dir) / cell.name / "impedance" fig = plt.figure() ax = fig.add_subplot(1, 1, 1) - ax.plot(freq, smooth_Z) + ax.plot(freq[:len(smooth_Z)], smooth_Z) _save_fig(dirname, filename) - plt.close("all") - plt.clf() def plot_all_impedances(cells, output_dir, mapper=map, efel_settings=None): @@ -441,3 +413,36 @@ def plot_all_impedances(cells, output_dir, mapper=map, efel_settings=None): list(mapper(partial(plot_impedance, output_dir=output_dir, efel_settings=efel_settings), cells)) else: mapper(partial(plot_impedance, output_dir=output_dir, efel_settings=efel_settings), cells) + + +def plot_all_recordings_efeatures( + cells, protocols, output_dir=None, show=False, mapper=map, efel_settings=None +): + """Generate plots for all recordings and efeatures both for individual + cells and across all cells.""" + + colors, markers = _get_colors_markers_wheels(cells) + + plot_all_recordings(cells, output_dir, mapper=mapper) + plot_all_impedances(cells, output_dir, mapper=map, efel_settings=efel_settings) + + for key_amp in ["amp", "amp_rel"]: + plot_individual_efeatures( + cells, + protocols, + output_dir, + colors=colors, + markers=markers, + key_amp=key_amp, + show=show, + mapper=mapper, + ) + plot_grouped_efeatures( + cells, + protocols, + output_dir, + colors=colors, + markers=markers, + key_amp=key_amp, + show=show + ) From ebd8b9937aba5facb3ca101c3d0d2a22487c7c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaquier=20Aur=C3=A9lien=20Tristan?= Date: Tue, 23 Jul 2024 13:41:52 +0200 Subject: [PATCH 3/4] add title, and axis labels --- bluepyefe/plotting.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bluepyefe/plotting.py b/bluepyefe/plotting.py index 192904c..dcfe0be 100644 --- a/bluepyefe/plotting.py +++ b/bluepyefe/plotting.py @@ -393,8 +393,8 @@ def plot_impedance(cell, output_dir, efel_settings): )[0] smooth_Z = gaussian_filter1d(norm_Z[select_idxs], 10) - amp = rec.amp if rec.amp is not None else 0.0 - filename = "{}_{}_{}_{:.3}_impedance.pdf".format( + amp = "{:.3}".format(rec.amp) if rec.amp is not None else "None" + filename = "{}_{}_{}_{}_impedance.pdf".format( cell.name, protocol_name, i, amp ) dirname = pathlib.Path(output_dir) / cell.name / "impedance" @@ -402,6 +402,9 @@ def plot_impedance(cell, output_dir, efel_settings): fig = plt.figure() ax = fig.add_subplot(1, 1, 1) ax.plot(freq[:len(smooth_Z)], smooth_Z) + ax.set_xlabel("Frequency (Hz)") + ax.set_ylabel("normalized Z") + fig .suptitle(f"Impedance for {cell.name} at amp {amp}") _save_fig(dirname, filename) From 2ba10de105b2220bf5b3c4798d78bbb32224f234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaquier=20Aur=C3=A9lien=20Tristan?= Date: Tue, 23 Jul 2024 15:48:19 +0200 Subject: [PATCH 4/4] create recording name and update impedance plot title --- bluepyefe/plotting.py | 5 ++--- bluepyefe/recording.py | 12 ++++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/bluepyefe/plotting.py b/bluepyefe/plotting.py index dcfe0be..778f31e 100644 --- a/bluepyefe/plotting.py +++ b/bluepyefe/plotting.py @@ -393,9 +393,8 @@ def plot_impedance(cell, output_dir, efel_settings): )[0] smooth_Z = gaussian_filter1d(norm_Z[select_idxs], 10) - amp = "{:.3}".format(rec.amp) if rec.amp is not None else "None" filename = "{}_{}_{}_{}_impedance.pdf".format( - cell.name, protocol_name, i, amp + cell.name, protocol_name, i, rec.name ) dirname = pathlib.Path(output_dir) / cell.name / "impedance" @@ -404,7 +403,7 @@ def plot_impedance(cell, output_dir, efel_settings): ax.plot(freq[:len(smooth_Z)], smooth_Z) ax.set_xlabel("Frequency (Hz)") ax.set_ylabel("normalized Z") - fig .suptitle(f"Impedance for {cell.name} at amp {amp}") + fig .suptitle(f"Impedance for {rec.name}\nfor cell {cell.name}") _save_fig(dirname, filename) diff --git a/bluepyefe/recording.py b/bluepyefe/recording.py index f4edafd..a7fcf0e 100644 --- a/bluepyefe/recording.py +++ b/bluepyefe/recording.py @@ -25,6 +25,7 @@ import numpy import efel import matplotlib.pyplot as plt +from pathlib import Path from .tools import to_ms, to_mV, to_nA, set_efel_settings @@ -81,6 +82,17 @@ def __init__(self, config_data, reader_data, protocol_name): self.auto_threshold = None self.peak_time = None + @property + def name(self): + """Proxy that can be used to name the recording.""" + if self.id: + return self.id + if "filepath" in self.config_data and self.config_data["filepath"] is not None: + return str(Path(self.config_data["filepath"]).stem) + if "v_file" in self.config_data: + return str(Path(self.config_data["v_file"]).stem) + return "" + @property def time(self): """Alias of the time attribute"""