def getExperiment(cell_id, stimtypes): ctc = CellTypesCache() ephys_sweeps = ctc.get_ephys_sweeps(specimen_id=cell_id) sweeps_by_type = defaultdict(list) sweeps = [] for sweep in ephys_sweeps: if sweep['stimulus_name'] in stimtypes: sweeps.append(sweep['sweep_number']) sweeps_by_type[sweep['stimulus_name']].append( sweep['sweep_number']) ephys_filename = f'{cell_id}/{cell_id}_ephys.nwb' ctc.get_ephys_data(cell_id, ephys_filename) swc_filename = f'{cell_id}/{cell_id}.swc' ctc.get_reconstruction(cell_id, swc_filename) return ephys_filename, swc_filename, sweeps, sweeps_by_type
class AllenMorphology(Paths): def __init__(self, *args, **kwargs): if not connected_to_internet(): raise ConnectionError("You will need to be connected to the internet to use the AllenMorphology class") Paths.__init__(self, *args, **kwargs) # Create a Cache for the Cell Types Cache API self.ctc = CellTypesCache(manifest_file=os.path.join(self.morphology_allen, 'manifest.json')) # Get a list of cell metadata for neurons with reconstructions, download if necessary self.neurons = pd.DataFrame(self.ctc.get_cells(species=[CellTypesApi.MOUSE], require_reconstruction = True)) self.n_neurons = len(self.neurons) if not self.n_neurons: raise ValueError("Something went wrong and couldn't get neurons metadata from Allen") self.downloaded_neurons = self.get_downloaded_neurons() def get_downloaded_neurons(self): return [os.path.join(self.morphology_allen, f) for f in os.listdir(self.morphology_allen) if ".swc" in f] def download_neurons(self, ids): if isinstance(ids, np.ndarray): ids = list(ids) if not isinstance(ids, (list)): ids = [ids] for neuron_id in ids: neuron_file = os.path.join(self.morphology_allen, "{}.swc".format(neuron_id)) neuron = self.ctc.get_reconstruction(neuron_id, file_name=neuron_file)
def get_cell_morphXYZ(cell_id): from allensdk.core.cell_types_cache import CellTypesCache from allensdk.core.swc import Marker import pprint ctc = CellTypesCache(manifest_file='cell_types/manifest.json') morph = ctc.get_reconstruction(cell_id) markers = ctc.get_reconstruction_markers(cell_id) x = [] y = [] z = [] for n in morph.compartment_list: #print(n['type']) #type=1, soma; type=2, axon; type=3, dendrite; type=4,apical dendrite if n['type'] == 4 or n['type'] == 3 or n['type'] == 1: x.append(n['x'] - morph.soma["x"]) y.append(n['y'] - morph.soma["y"]) z.append(n['z'] - morph.soma["z"]) morph_data = np.array(np.column_stack((x, y, z))) morph_soma = [morph.soma["x"], morph.soma["y"], morph.soma["z"]] return (morph_data, morph_soma)
def load_realdata_morphology(cell_id = 464212183, desample_tree_factor=30): from allensdk.core.cell_types_cache import CellTypesCache # load real data ctc = CellTypesCache(manifest_file='cell_types/manifest.json') # load 3D morphology data morphology = ctc.get_reconstruction(cell_id) # extract compartments = morphology.compartment_list compartments_byid = { c['id']: c for c in compartments} num_children = np.array([len(c['children']) for c in compartments]) branch_indexes = np.where(num_children>1)[0] # branch compartment index # find all paths (terminate at branches) paths = find_branch_paths(compartments_byid, branch_indexes) # de-sample tree and paths select_compartments = [] # index selected path_compartments = [] # every connections between selected compartments (not limited to branches) path_filtered = [] #per branch, only keep selected one for k, path in enumerate(paths): n = len(path) if n < desample_tree_factor: select_compartments.extend([path[0], path[-1]]) path_compartments.append([path[0], path[-1]]) path_filtered.append([path[0], path[-1]]) else: use = [path[int(i)] for i in np.linspace(0, n - 1, n//desample_tree_factor+1)] path_filtered.append(use) select_compartments.extend(use) for j in range(len(use) - 1): path_compartments.append([use[j], use[j+1]]) select_compartments = np.unique(select_compartments) assert len(np.setdiff1d(branch_indexes, select_compartments)) == 0 # map old id to new id new_id = {select_compartments[i]:i for i in range(len(select_compartments))} # calculate distance between selected compartments from numpy import linalg as LA C = len(select_compartments) dist = np.full((C, C), np.inf) for path in path_compartments: ID1, xyz1, _ = get_info(compartments_byid[path[0]]) ID2, xyz2, _ = get_info(compartments_byid[path[1]]) dist[new_id[path[0]], new_id[path[1]]] = LA.norm(xyz1 - xyz2, 2) dist[new_id[path[1]], new_id[path[0]]] = dist[new_id[path[0]], new_id[path[1]]] network = 1/dist return network, new_id, compartments_byid, paths, select_compartments, path_compartments, path_filtered, compartments, morphology
def cell_morphology_rot(cell_id, x_soma, y_soma, z_soma, theta): theta_z = theta[2] theta_y = theta[1] theta_x = theta[0] # download and open an SWC file ctc = CellTypesCache(manifest_file='cell_types/manifest.json') morph = ctc.get_reconstruction(cell_id) #First applying a rotation angle around z axis tr_rot_z = [ math.cos(theta_z), -math.sin(theta_z), 0, math.sin(theta_z), math.cos(theta_z), 0, 0, 0, 1, 0, 0, 0 ] #Second applying a rotation angle around y axis tr_rot_y = [ math.cos(theta_y), 0, math.sin(theta_y), 0, 1, 0, -math.sin(theta_y), 0, math.cos(theta_y), 0, 0, 0 ] #Third applying a rotation angle around x axis tr_rot_x = [ 1, 0, 0, 0, math.cos(theta_x), -math.sin(theta_x), 0, math.sin(theta_x), math.cos(theta_x), 0, 0, 0 ] morph.apply_affine(tr_rot_z) morph.apply_affine(tr_rot_y) morph.apply_affine(tr_rot_x) # translate the soma location tr_soma = [ 1, 0, 0, 0, 1, 0, 0, 0, 1, -morph.soma["x"] + x_soma, -morph.soma["y"] + y_soma, -morph.soma["z"] + z_soma ] morph.apply_affine(tr_soma) # plot xy and xz views # fig, axes = plt.subplots(1, 2, sharey=True, sharex=True) # vm.plot_morph_swc(morph,axes,color='r') # fig, axes = plt.subplots(1, 2, sharey=True, sharex=True) # Make a line drawing of x-y and y-z views return morph
def main_specimen(): from allensdk.core.cell_types_cache import CellTypesCache specimen_id = 485880739 ctc = CellTypesCache() morphology = ctc.get_reconstruction(specimen_id) for c in morphology.compartment_list: c['z'] *= 3 tube_pd = vtkmorph.generate_tube(morphology.compartment_index, morphology.root, color_by_type, 6, radius=1.5) vtkmorph.write_ply(tube_pd, '%d.ply' % specimen_id) vtkmorph.write_vtk(tube_pd, '%d.vtk' % specimen_id)
#=============================================================================== # example 1 #=============================================================================== from allensdk.core.cell_types_cache import CellTypesCache ctc = CellTypesCache(manifest_file='cell_types/manifest.json') # a list of cell metadata for cells with reconstructions, download if necessary cells = ctc.get_cells(require_reconstruction=True) # open the electrophysiology data of one cell, download if necessary data_set = ctc.get_ephys_data(cells[0]['id']) # read the reconstruction, download if necessary reconstruction = ctc.get_reconstruction(cells[0]['id']) #=============================================================================== # example 2 #=============================================================================== from allensdk.core.cell_types_cache import CellTypesCache from allensdk.ephys.extract_cell_features import extract_cell_features from collections import defaultdict # initialize the cache ctc = CellTypesCache(manifest_file='cell_types/manifest.json') # pick a cell to analyze specimen_id = 324257146
# In[6]: meta_and_morph_df = all_cells_df.join(morphology_df) meta_and_morph_df.head() # In[7]: meta_and_morph_df.columns # You can also acesss morphology of a single cell by exectuing the `get_recontruction()` method on your instance of the cells type cache. To do so, you must specify what cell you want to recontruct by inputing a `specimen_id`. This method returns a class instance with methods for accessing morphology compartments. # In[8]: cell_id = 478107198 single_cell_morphology = ctc.get_reconstruction(specimen_id=cell_id) #help(single_cell_morphology) # In[9]: import matplotlib.pyplot as plt # In[10]: single_cell_morphology.soma # Note that the type field refers to the type of neuronal compartment. The values can be 1 for the soma, 2 for the axon, 3 for dendrites, and 4 for apical dendrites (if present). # Morphologies now also come with marker files, which contains points of interest in the reconstruction. The marker file contains locations where dendrites have been truncated due to slicing and when axons were not reconstructed. The name field indicates the type of marker (10 for dendrite truncation, 20 for no reconstruction).
class AllenMorphology(Paths): """ Handles the download and visualisation of neuronal morphology data from the Allen database. """ def __init__(self, *args, scene_kwargs={}, **kwargs): """ Initialise API interaction and fetch metadata of neurons in the Allen Database. """ if not connected_to_internet(): raise ConnectionError( "You will need to be connected to the internet to use the AllenMorphology class" ) Paths.__init__(self, *args, **kwargs) self.scene = Scene(add_root=False, display_inset=False, **scene_kwargs) # Create a Cache for the Cell Types Cache API self.ctc = CellTypesCache( manifest_file=os.path.join(self.morphology_allen, 'manifest.json')) # Get a list of cell metadata for neurons with reconstructions, download if necessary self.neurons = pd.DataFrame( self.ctc.get_cells(species=[CellTypesApi.MOUSE], require_reconstruction=True)) self.n_neurons = len(self.neurons) if not self.n_neurons: raise ValueError( "Something went wrong and couldn't get neurons metadata from Allen" ) self.downloaded_neurons = self.get_downloaded_neurons() def get_downloaded_neurons(self): """ Get's the path to files of downloaded neurons """ return [ os.path.join(self.morphology_allen, f) for f in os.listdir(self.morphology_allen) if ".swc" in f ] def download_neurons(self, ids): """ Download neurons :param ids: list of integers with neurons IDs """ if isinstance(ids, np.ndarray): ids = list(ids) if not isinstance(ids, (list)): ids = [ids] neurons = [] for neuron_id in ids: neuron_file = os.path.join(self.morphology_allen, "{}.swc".format(neuron_id)) neurons.append( self.ctc.get_reconstruction(neuron_id, file_name=neuron_file)) return neurons def parse_neurons_swc_allen(self, morphology, color='blackboard', alpha=1): """ SWC parser for Allen neuron's morphology data, they're a bit different from the Mouse Light SWC :param morphology: data with morphology :param neuron_number: int, number of the neuron being rendered. """ # Create soma actor radius = 1 neuron_actors = [ shapes.Sphere(pos=get_coords(morphology.soma)[::-1], c=color, r=radius * 3) ] # loop over trees for tree in morphology._tree_list: tree = pd.DataFrame(tree) branching_points = [ t.id for i, t in tree.iterrows() if len(t.children) > 2 and t.id < len(tree) ] branch_starts = [] for bp in branching_points: branch_starts.extend(tree.iloc[bp].children) for bp in branch_starts: parent = tree.iloc[tree.iloc[bp].parent] branch = [(parent.x, parent.y, parent.z)] point = tree.iloc[bp] while True: branch.append((point.x, point.y, point.z)) if not point.children: break else: try: point = tree.iloc[point.children[0]] except: break # Create actor neuron_actors.append( shapes.Tube(branch, r=radius, c='red', alpha=1, res=24)) actor = merge(*neuron_actors) actor.color(color) actor.alpha(alpha) return actor # # Todo load/save neurons?? def load_save_neuron(self, neuron_file, neuron=None): neuron_name = os.path.split(neuron_file)[-1].split('.swc')[0] savepath = os.path.join(self.morphology_cache, neuron_name + '.vtk') if neuron is None and os.path.isfile(savepath): return load(savepath) elif neuron is None: return None elif neuron is not None: neuron.write(savepath) def parse_neuron_swc(self, filepath, color='blackboard', alpha=1, radius_multiplier=.1, overwrite=False): """ Given an swc file, render the neuron :param filepath: str with path to swc file :param neuron_number: numnber of neuron being rendered """ # See if we rendered this neuron already if not overwrite: loaded = self.load_save_neuron(filepath) if loaded is not None: return loaded.color(color) print(f"Parsing swc file: {filepath}") # details on swc files: http://www.neuronland.org/NLMorphologyConverter/MorphologyFormats/SWC/Spec.html _sample = namedtuple("sample", "sampleN structureID x y z r parent" ) # sampleN structureID x y z r parent if not os.path.isfile(filepath) or not ".swc" in filepath.lower(): raise ValueError("unrecognized file path: {}".format(filepath)) try: return self.parse_neurons_swc_allen(filepath) except: pass # the .swc file fas not generate with by allen f = open(filepath) content = f.readlines() f.close() content = [ sample.replace("\n", "") for sample in content if sample[0] != '#' ] content = [sample for sample in content if len(sample) > 3] # crate empty dicts for soma axon and dendrites data = dict(id=[], parentNumber=[], radius=[], sampleNumber=[], x=[], y=[], z=[]) # start looping around samples for sample in content: s = _sample( *[float(samp) for samp in sample.lstrip().rstrip().split(" ")]) # append data to dictionary data['id'] = s.structureID data['parentNumber'].append(int(s.parent)) data['radius'].append(s.r) data['x'].append(s.x) data['y'].append(s.y) data['z'].append(s.z) data['sampleNumber'].append(int(s.sampleN)) # Get branches and soma print(" reconstructing neurites trees") data = pd.DataFrame(data) radius = data['radius'].values[0] * radius_multiplier soma = data.iloc[0] soma = shapes.Sphere(pos=[soma.x, soma.y, soma.z], c=color, r=radius * 4) neuron_actors = [soma] branches_end, branches_start = [], [] # Get branches start and end for parent in data.parentNumber.values: sons = data.loc[data.parentNumber == parent] if len(sons) > 1: branches_end.append(parent) for i, son in sons.iterrows(): branches_start.append(son.sampleNumber) print(" creating actors") for start in branches_start: node = data.loc[data.sampleNumber == start] parent = data.loc[data.sampleNumber == node.parentNumber.values[0]] branch = [(parent.x.values[0], parent.y.values[0], parent.z.values[0])] while True: branch.append( (node.x.values[0], node.y.values[0], node.z.values[0])) node = data.loc[data.parentNumber == node.sampleNumber.values[0]] if not len(node): break if node.sampleNumber.values[0] in branches_end: branch.append( (node.x.values[0], node.y.values[0], node.z.values[0])) break neuron_actors.append( shapes.Tube(branch, r=radius, c='red', alpha=1, res=24)) # Merge actors and save actor = merge(*neuron_actors) actor.color(color) actor.alpha(alpha) self.load_save_neuron(filepath, neuron=actor) return actor def add_neuron(self, neuron, shadow_axis=None, shadow_offset=-20, **kwargs): if isinstance(neuron, list): neurons = neuron else: if isinstance(neuron, str): if os.path.isdir(neuron): neurons = listdir(neuron) else: neurons = [neuron] actors = [] for neuron in neurons: if isinstance(neuron, str): neuron = self.parse_neuron_swc(neuron, **kwargs) elif isinstance(neuron, Morphology): neuron = self.parse_neurons_swc_allen(neuron, **kwargs) actor = self.scene.add_vtkactor(neuron) # scals = actor.points()[:, 1] # alphas = np.linspace(0.82, .83, 250) # actor.pointColors(scals, alpha=alphas, cmap="Greens_r") # actor.points()[:, 0] += np.random.normal(0, 2000) # actor.points()[:, 2] += np.random.normal(0, 2000) if shadow_axis == 'x': actor.addShadow(x=shadow_offset) elif shadow_axis == 'y': actor.addShadow(y=shadow_offset) elif shadow_axis == 'z': actor.addShadow(z=shadow_offset) actors.append(neuron) return actors def render(self, **kwargs): self.scene.render(**kwargs)
from allensdk.core.cell_types_cache import CellTypesCache ctc = CellTypesCache() # a list of cell metadata for cells with reconstructions, download if necessary cells = ctc.get_cells(require_reconstruction=True) # open the electrophysiology data of one cell, download if necessary data_set = ctc.get_ephys_data(cells[0]['id']) # read the reconstruction, download if necessary reconstruction = ctc.get_reconstruction(cells[0]['id'])
def data_for_specimen_id(specimen_id, sweep_qc_option, data_source, ap_window_length=0.005, target_sampling_rate=50000): logging.debug("specimen_id: {}".format(specimen_id)) # Find or retrieve NWB file and ancillary info and construct an AibsDataSet object ontology = StimulusOntology( ju.read(StimulusOntology.DEFAULT_STIMULUS_ONTOLOGY_FILE)) if data_source == "lims": nwb_path, h5_path = lims_nwb_information(specimen_id) if type(nwb_path) is dict and "error" in nwb_path: logging.warning( "Problem getting NWB file for specimen {:d} from LIMS".format( specimen_id)) return nwb_path try: data_set = AibsDataSet(nwb_file=nwb_path, h5_file=h5_path, ontology=ontology) except Exception as detail: logging.warning( "Exception when loading specimen {:d} from LIMS".format( specimen_id)) logging.warning(detail) return { "error": { "type": "dataset", "details": traceback.format_exc(limit=None) } } elif data_source == "sdk": ctc = CellTypesCache() morph = ctc.get_reconstruction(specimen_id) morph_table = ctc.get_morphology_features(specimen_id) morph_table.to_csv('cell_types\\specimen_' + str(specimen_id) + '\\' + 'morphology_features.csv') print("morph dl failed") nwb_path, sweep_info = sdk_nwb_information(specimen_id) try: data_set = AibsDataSet(nwb_file=nwb_path, sweep_info=sweep_info, ontology=ontology) except Exception as detail: logging.warning( "Exception when loading specimen {:d} via Allen SDK".format( specimen_id)) logging.warning(detail) return { "error": { "type": "dataset", "details": traceback.format_exc(limit=None) } } else: logging.error("invalid data source specified ({})".format(data_source)) # Identify and preprocess long square sweeps try: lsq_sweep_numbers = categorize_iclamp_sweeps( data_set, ontology.long_square_names, sweep_qc_option=sweep_qc_option, specimen_id=specimen_id) (lsq_sweeps, lsq_features, lsq_start, lsq_end, lsq_spx) = preprocess_long_square_sweeps(data_set, lsq_sweep_numbers) except Exception as detail: logging.warning( "Exception when preprocessing long square sweeps from specimen {:d}" .format(specimen_id)) logging.warning(detail) return { "error": { "type": "sweep_table", "details": traceback.format_exc(limit=None) } } # Identify and preprocess short square sweeps try: ssq_sweep_numbers = categorize_iclamp_sweeps( data_set, ontology.short_square_names, sweep_qc_option=sweep_qc_option, specimen_id=specimen_id) ssq_sweeps, ssq_features = preprocess_short_square_sweeps( data_set, ssq_sweep_numbers) except Exception as detail: logging.warning( "Exception when preprocessing short square sweeps from specimen {:d}" .format(specimen_id)) logging.warning(detail) return { "error": { "type": "sweep_table", "details": traceback.format_exc(limit=None) } } # Identify and preprocess ramp sweeps try: ramp_sweep_numbers = categorize_iclamp_sweeps( data_set, ontology.ramp_names, sweep_qc_option=sweep_qc_option, specimen_id=specimen_id) ramp_sweeps, ramp_features = preprocess_ramp_sweeps( data_set, ramp_sweep_numbers) except Exception as detail: logging.warning( "Exception when preprocessing ramp sweeps from specimen {:d}". format(specimen_id)) logging.warning(detail) return { "error": { "type": "sweep_table", "details": traceback.format_exc(limit=None) } } # Calculate desired feature vectors result = {} (subthresh_hyperpol_dict, hyperpol_deflect_dict ) = fv.identify_subthreshold_hyperpol_with_amplitudes( lsq_features, lsq_sweeps) target_amps_for_step_subthresh = [-90, -70, -50, -30, -10] result["step_subthresh"] = fv.step_subthreshold( subthresh_hyperpol_dict, target_amps_for_step_subthresh, lsq_start, lsq_end, amp_tolerance=5) result["subthresh_norm"] = fv.subthresh_norm(subthresh_hyperpol_dict, hyperpol_deflect_dict, lsq_start, lsq_end) (subthresh_depol_dict, depol_deflect_dict) = fv.identify_subthreshold_depol_with_amplitudes( lsq_features, lsq_sweeps) result["subthresh_depol_norm"] = fv.subthresh_depol_norm( subthresh_depol_dict, depol_deflect_dict, lsq_start, lsq_end) isi_sweep, isi_sweep_spike_info = fv.identify_sweep_for_isi_shape( lsq_sweeps, lsq_features, lsq_end - lsq_start) result["isi_shape"] = fv.isi_shape(isi_sweep, isi_sweep_spike_info, lsq_end) # Calculate waveforms from each type of sweep spiking_ssq_sweep_list = [ ssq_sweeps.sweeps[swp_ind] for swp_ind in ssq_features["common_amp_sweeps"].index ] spiking_ssq_info_list = [ ssq_features["spikes_set"][swp_ind] for swp_ind in ssq_features["common_amp_sweeps"].index ] ssq_ap_v, ssq_ap_dv = fv.first_ap_vectors( spiking_ssq_sweep_list, spiking_ssq_info_list, target_sampling_rate=target_sampling_rate, window_length=ap_window_length, skip_clipped=True) rheo_ind = lsq_features["rheobase_sweep"].name sweep = lsq_sweeps.sweeps[rheo_ind] lsq_ap_v, lsq_ap_dv = fv.first_ap_vectors( [sweep], [lsq_features["spikes_set"][rheo_ind]], target_sampling_rate=target_sampling_rate, window_length=ap_window_length) spiking_ramp_sweep_list = [ ramp_sweeps.sweeps[swp_ind] for swp_ind in ramp_features["spiking_sweeps"].index ] spiking_ramp_info_list = [ ramp_features["spikes_set"][swp_ind] for swp_ind in ramp_features["spiking_sweeps"].index ] ramp_ap_v, ramp_ap_dv = fv.first_ap_vectors( spiking_ramp_sweep_list, spiking_ramp_info_list, target_sampling_rate=target_sampling_rate, window_length=ap_window_length, skip_clipped=True) # Combine so that differences can be assessed by analyses like sPCA result["first_ap_v"] = np.hstack([ssq_ap_v, lsq_ap_v, ramp_ap_v]) result["first_ap_dv"] = np.hstack([ssq_ap_dv, lsq_ap_dv, ramp_ap_dv]) target_amplitudes = np.arange(0, 120, 20) supra_info_list = fv.identify_suprathreshold_spike_info(lsq_features, target_amplitudes, shift=10) result["psth"] = fv.psth_vector(supra_info_list, lsq_start, lsq_end) result["inst_freq"] = fv.inst_freq_vector(supra_info_list, lsq_start, lsq_end) spike_feature_list = [ "upstroke_downstroke_ratio", "peak_v", "fast_trough_v", "threshold_v", "width", ] for feature in spike_feature_list: result["spiking_" + feature] = fv.spike_feature_vector( feature, supra_info_list, lsq_start, lsq_end) return result
class AllenMorphology(Paths): """ Handles the download of neuronal morphology data from the Allen database. """ def __init__(self, *args, **kwargs): """ Initialise API interaction and fetch metadata of neurons in the Allen Database. """ if not connected_to_internet(): raise ConnectionError( "You will need to be connected to the internet to use the AllenMorphology class to download neurons" ) Paths.__init__(self, *args, **kwargs) # Create a Cache for the Cell Types Cache API self.ctc = CellTypesCache(manifest_file=os.path.join( self.allen_morphology_cache, "manifest.json")) # Get a list of cell metadata for neurons with reconstructions, download if necessary self.neurons = pd.DataFrame( self.ctc.get_cells(species=[CellTypesApi.MOUSE], require_reconstruction=True)) self.n_neurons = len(self.neurons) if not self.n_neurons: raise ValueError( "Something went wrong and couldn't get neurons metadata from Allen" ) self.downloaded_neurons = self.get_downloaded_neurons() def get_downloaded_neurons(self): """ Get's the path to files of downloaded neurons """ return [ os.path.join(self.allen_morphology_cache, f) for f in os.listdir(self.allen_morphology_cache) if ".swc" in f ] def download_neurons(self, ids, **kwargs): """ Download neurons and return neuron reconstructions (instances of Neuron class) :param ids: list of integers with neurons IDs """ if isinstance(ids, np.ndarray): ids = list(ids) if not isinstance(ids, (list)): ids = [ids] neurons = [] print("Downloading neurons") for neuron_id in track(ids): neuron_file = os.path.join(self.allen_morphology_cache, "{}.swc".format(neuron_id)) # Download file self.ctc.get_reconstruction(neuron_id, file_name=neuron_file) # Reconstruct neuron neurons.append( Neuron(neuron_file, neuron_name=str(neuron_id), **kwargs)) return neurons