Skip to content
This repository was archived by the owner on Feb 26, 2025. It is now read-only.

Commit ed9ce32

Browse files
Merge pull request #189 from AurelienJaquier/impedance-plot
Impedance plot
2 parents 5bff6a2 + 9c6e31b commit ed9ce32

3 files changed

Lines changed: 94 additions & 2 deletions

File tree

bluepyefe/extract.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1042,7 +1042,11 @@ def extract_efeatures(
10421042

10431043
if plot:
10441044
plot_all_recordings_efeatures(
1045-
cells, protocols, output_dir=output_directory, mapper=map_function
1045+
cells,
1046+
protocols,
1047+
output_dir=output_directory,
1048+
mapper=map_function,
1049+
efel_settings=efel_settings
10461050
)
10471051

10481052
if extract_per_cell and write_files:

bluepyefe/plotting.py

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,15 +342,91 @@ def plot_grouped_efeatures(
342342
_ = _plot_legend(colors, markers, output_dir, show=show)
343343

344344

345+
def plot_impedance(cell, output_dir, efel_settings):
346+
"""Plots the impedance."""
347+
from scipy.ndimage.filters import gaussian_filter1d
348+
349+
dt = 0.1
350+
Z_max_freq = 50.0
351+
if efel_settings is not None:
352+
dt = efel_settings.get("interp_step", dt)
353+
Z_max_freq = efel_settings.get("impedance_max_freq", Z_max_freq)
354+
355+
for protocol_name in cell.get_protocol_names():
356+
if "sinespec" in protocol_name.lower():
357+
recordings = cell.get_recordings_by_protocol_name(protocol_name)
358+
for i, rec in enumerate(recordings):
359+
voltage = rec.voltage
360+
current = rec.current
361+
362+
efel_vals = rec.call_efel(
363+
[
364+
"voltage_base",
365+
"steady_state_voltage_stimend",
366+
"current_base",
367+
"steady_state_current_stimend",
368+
],
369+
efel_settings
370+
)
371+
if efel_vals[0]["voltage_base"] is not None:
372+
holding_voltage = efel_vals[0]["voltage_base"][0]
373+
else:
374+
holding_voltage = efel_vals[0]["steady_state_voltage_stimend"][0]
375+
if efel_vals[0]["current_base"] is not None:
376+
holding_current = efel_vals[0]["current_base"][0]
377+
else:
378+
holding_current = efel_vals[0]["steady_state_current_stimend"][0]
379+
380+
normalized_voltage = voltage - holding_voltage
381+
normalized_current = current - holding_current
382+
383+
fft_volt = numpy.fft.fft(normalized_voltage)
384+
fft_cur = numpy.fft.fft(normalized_current)
385+
if any(fft_cur) == 0:
386+
return None
387+
# convert dt from ms to s to have freq in Hz
388+
freq = numpy.fft.fftfreq(len(normalized_voltage), d=dt / 1000.)
389+
Z = fft_volt / fft_cur
390+
norm_Z = abs(Z) / max(abs(Z))
391+
select_idxs = numpy.swapaxes(
392+
numpy.argwhere((freq > 0) & (freq <= Z_max_freq)), 0, 1
393+
)[0]
394+
smooth_Z = gaussian_filter1d(norm_Z[select_idxs], 10)
395+
396+
filename = "{}_{}_{}_{}_impedance.pdf".format(
397+
cell.name, protocol_name, i, rec.name
398+
)
399+
dirname = pathlib.Path(output_dir) / cell.name / "impedance"
400+
401+
fig = plt.figure()
402+
ax = fig.add_subplot(1, 1, 1)
403+
ax.plot(freq[:len(smooth_Z)], smooth_Z)
404+
ax.set_xlabel("Frequency (Hz)")
405+
ax.set_ylabel("normalized Z")
406+
fig .suptitle(f"Impedance for {rec.name}\nfor cell {cell.name}")
407+
_save_fig(dirname, filename)
408+
409+
410+
def plot_all_impedances(cells, output_dir, mapper=map, efel_settings=None):
411+
"""Plot recordings for all cells and all protocols"""
412+
if mapper == map:
413+
# For the built-in map(), ensure immediate evaluation as it returns a lazy iterator
414+
# which won't execute the function until iterated over. Converting to a list forces this iteration.
415+
list(mapper(partial(plot_impedance, output_dir=output_dir, efel_settings=efel_settings), cells))
416+
else:
417+
mapper(partial(plot_impedance, output_dir=output_dir, efel_settings=efel_settings), cells)
418+
419+
345420
def plot_all_recordings_efeatures(
346-
cells, protocols, output_dir=None, show=False, mapper=map
421+
cells, protocols, output_dir=None, show=False, mapper=map, efel_settings=None
347422
):
348423
"""Generate plots for all recordings and efeatures both for individual
349424
cells and across all cells."""
350425

351426
colors, markers = _get_colors_markers_wheels(cells)
352427

353428
plot_all_recordings(cells, output_dir, mapper=mapper)
429+
plot_all_impedances(cells, output_dir, mapper=map, efel_settings=efel_settings)
354430

355431
for key_amp in ["amp", "amp_rel"]:
356432
plot_individual_efeatures(

bluepyefe/recording.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import numpy
2626
import efel
2727
import matplotlib.pyplot as plt
28+
from pathlib import Path
2829

2930
from .tools import to_ms, to_mV, to_nA, set_efel_settings
3031

@@ -81,6 +82,17 @@ def __init__(self, config_data, reader_data, protocol_name):
8182
self.auto_threshold = None
8283
self.peak_time = None
8384

85+
@property
86+
def name(self):
87+
"""Proxy that can be used to name the recording."""
88+
if self.id:
89+
return self.id
90+
if "filepath" in self.config_data and self.config_data["filepath"] is not None:
91+
return str(Path(self.config_data["filepath"]).stem)
92+
if "v_file" in self.config_data:
93+
return str(Path(self.config_data["v_file"]).stem)
94+
return ""
95+
8496
@property
8597
def time(self):
8698
"""Alias of the time attribute"""

0 commit comments

Comments
 (0)