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

use all available cores in feature extraction #176

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
68 changes: 34 additions & 34 deletions bluepyefe/cell.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,30 @@
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
from collections import defaultdict
import logging
from multiprocessing import Pool
import numpy
import matplotlib.pyplot as plt
import pathlib

from bluepyefe.ecode import eCodes
from bluepyefe.reader import *
from bluepyefe.plotting import _save_fig
from matplotlib.backends.backend_pdf import PdfPages

from bluepyefe.recording import Recording

logger = logging.getLogger(__name__)


class Cell(object):
def extract_efeatures_helper(recording, efeatures, efeature_names, efel_settings):
"""Helper function to compute efeatures for a single recording."""
recording.compute_efeatures(
efeatures, efeature_names, efel_settings)
return recording


class Cell:

"""Contains the metadata related to a cell as well as the
electrophysiological recordings once they are read"""
Expand All @@ -46,9 +56,14 @@ def __init__(self, name):

self.name = name

self.recordings = []
self.recordings: dict[str, list[Recording]] = defaultdict(list)
self.rheobase = None

@property
def recordings_as_list(self):
"""Return all the recordings as a list."""
return [rec for recordings_list in self.recordings.values() for rec in recordings_list]

def reader(self, config_data, recording_reader=None):
"""Define the reader method used to read the ephys data for the
present recording and returns the data contained in the file.
Expand Down Expand Up @@ -90,9 +105,8 @@ def reader(self, config_data, recording_reader=None):
)

def get_protocol_names(self):
"""List of all the protocols available for the present cell."""

return list(set([rec.protocol_name for rec in self.recordings]))
"""List of all the protocol names available for the present cell."""
return list(self.recordings.keys())

def get_recordings_by_protocol_name(self, protocol_name):
"""List of all the recordings available for the present cell for a
Expand All @@ -102,27 +116,7 @@ def get_recordings_by_protocol_name(self, protocol_name):
protocol_name (str): name of the protocol for which to get
the recordings.
"""

return [
rec
for rec in self.recordings
if rec.protocol_name == protocol_name
]

def get_recordings_id_by_protocol_name(self, protocol_name):
"""List of the indexes of the recordings available for the present
cell for a given protocol.

Args:
protocol_name (str): name of the protocol for which to get
the recordings.
"""

return [
i
for i, trace in enumerate(self.recordings)
if trace.protocol_name == protocol_name
]
return self.recordings.get(protocol_name)

def read_recordings(
self,
Expand Down Expand Up @@ -163,7 +157,7 @@ def read_recordings(
protocol_name,
efel_settings
)
self.recordings.append(rec)
self.recordings[protocol_name].append(rec)
break
else:
raise KeyError(
Expand Down Expand Up @@ -192,19 +186,25 @@ def extract_efeatures(
is to be extracted several time on different sections
of the same recording.
"""
recordings_of_protocol: list[Recording] = self.recordings.get(protocol_name)

# Run in parallel via multiprocessing
with Pool(maxtasksperchild=1) as pool:
tasks = [
(recording, efeatures, efeature_names, efel_settings)
for recording in recordings_of_protocol
]
results = pool.starmap(extract_efeatures_helper, tasks)

for i in self.get_recordings_id_by_protocol_name(protocol_name):
self.recordings[i].compute_efeatures(
efeatures, efeature_names, efel_settings)
self.recordings[protocol_name] = results

def compute_relative_amp(self):
"""Compute the relative current amplitude for all the recordings as a
percentage of the rheobase."""

if self.rheobase not in (0.0, None, False, numpy.nan):

for i in range(len(self.recordings)):
self.recordings[i].compute_relative_amp(self.rheobase)
for recording in self.recordings_as_list:
recording.compute_relative_amp(self.rheobase)

else:

Expand Down
17 changes: 7 additions & 10 deletions bluepyefe/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
import os
import pickle
import functools
import logging
Expand Down Expand Up @@ -448,10 +447,8 @@ def _build_current_dict(cells, default_std_value):
threshold = {}

for cell in cells:

holding[cell.name] = numpy.nanmean(
[t.hypamp for t in cell.recordings]
)
holding_currents = [rec.hypamp for rec in cell.recordings_as_list]
holding[cell.name] = numpy.nanmean(holding_currents)

if cell.rheobase is not None:
threshold[cell.name] = cell.rheobase
Expand Down Expand Up @@ -612,10 +609,10 @@ def _read_extract_low_memory(

# clean traces voltage and time
for i in range(len(cell.recordings)):
cell.recordings[i].t = None
cell.recordings[i].voltage = None
cell.recordings[i].current = None
cell.recordings[i].reader_data = None
cell.recordings_as_list[i].t = None
cell.recordings_as_list[i].voltage = None
cell.recordings_as_list[i].current = None
cell.recordings_as_list[i].reader_data = None

cells.append(cell)
gc.collect()
Expand Down Expand Up @@ -762,7 +759,7 @@ def _extract_auto_targets(

recordings = []
for c in cells:
recordings += c.recordings
recordings += c.recordings_as_list

for i in range(len(auto_targets)):
auto_targets[i].select_ecode_and_amplitude(recordings)
Expand Down
2 changes: 1 addition & 1 deletion bluepyefe/recording.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ def call_efel(self, efeatures, efel_settings=None):
}

try:
return efel.getFeatureValues(
return efel.get_feature_values(
[efel_trace], efeatures, raise_warnings=False
)
except TypeError as e:
Expand Down
13 changes: 8 additions & 5 deletions bluepyefe/rheobase.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@


def _get_list_spiking_amplitude(cell, protocols_rheobase):
"""Return the list of sorted list of amplitude that triggered at least
one spike"""
"""Return the list of sorted amplitudes that triggered at least
one spike, along with their corresponding spike counts."""

amps = []
spike_counts = []

for i, rec in enumerate(cell.recordings):
for rec in cell.recordings_as_list:
if rec.protocol_name in protocols_rheobase:
if rec.spikecount is not None:

Expand All @@ -42,13 +42,16 @@ def _get_list_spiking_amplitude(cell, protocols_rheobase):
logger.warning(
f"A recording of cell {cell.name} protocol "
f"{rec.protocol_name} shows spikes at a "
"suspiciously low current in a trace from file"
f" {rec.files}. Check that the ton and toff are"
"suspiciously low current in a trace from file "
f"{rec.files}. Check that the ton and toff are "
"correct or for the presence of unwanted spikes."
)

# Sort amplitudes and their corresponding spike counts
if amps:
amps, spike_counts = zip(*sorted(zip(amps, spike_counts)))
else:
amps, spike_counts = (), ()

return amps, spike_counts

Expand Down
9 changes: 8 additions & 1 deletion bluepyefe/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,14 @@ def set_efel_settings(efeature_settings):
if setting in ['stim_start', 'stim_end']:
value = float(value)

efel.set_setting(setting, value)
if setting == 'Threshold':
efel.set_threshold(value)

elif isinstance(value, bool) or isinstance(value, int):
efel.set_setting(setting, int(value))

elif isinstance(value, (float, str)):
efel.set_setting(setting, value)


def dict_to_json(data, path):
Expand Down
Loading
Loading