Extracting features from SONATA Network simulations =================================================== This notebook shows how to extract features of a group of cells from a SONATA network, specifically focusing on a small portion of non-barrel primary somatosensory cortex circuit from juvenile rats, with the help of `BlueCellulLab `__. For those interested in conducting a more in-depth analysis, the entire circuit dataset is accessible on `Zenodo `__. For more details about the simulation and in-depth insights on the circuit, please refer to the Bluecellulab `SONATA Network example `__ and the related `paper `__, respectively. **Note**: The compiled mechanisms need to be provided before importing bluecellulab. .. code:: ipython3 !nrnivmodl ./mechanisms .. code:: ipython3 import json from pathlib import Path from matplotlib import pyplot as plt from bluecellulab import CircuitSimulation import efel In this example, a small sub-circuit has been extracted from the `sscx circuit `__. This sub-circuit specifically consists of a random selection of cells exhibiting delayed stuttering (dSTUT) etype. The simulation_config specifies the types of input stimuli to be applied to the cells. In this case, we have selected a ‘relative_linear’ stimulus of 70 ms and set the stimulus current at a level equivalent to 100 percent of the cell’s threshold current. .. code:: ipython3 simulation_config = Path("./") / "simulation_config.json" with open(simulation_config) as f: simulation_config_dict = json.load(f) print(json.dumps(simulation_config_dict, indent=4)) .. parsed-literal:: { "manifest": { "$OUTPUT_DIR": "." }, "run": { "tstop": 100.0, "dt": 0.025, "random_seed": 1 }, "conditions": { "v_init": -65 }, "target_simulator": "NEURON", "network": "./O1/circuit_config.json", "node_set": "dSTUT_mini", "output": { "output_dir": "$OUTPUT_DIR/output_sonata", "spikes_file": "out.h5", "spikes_sort_order": "by_time" }, "inputs": { "continuous_linear": { "input_type": "current_clamp", "module": "relative_linear", "delay": 20.0, "duration": 70.0, "percent_start": 100, "node_set": "dSTUT_mini" } }, "reports": { "soma": { "cells": "dSTUT_mini", "variable_name": "v", "type": "compartment", "dt": 1.0, "start_time": 0.0, "end_time": 20.0, "sections": "soma", "compartments": "center" } } } We use BlueCellulab for simulating smaller scale circuits, in contrast to the larger-scale simulations conducted with Neurodamus. .. code:: ipython3 simulation_config = Path("./") / "simulation_config.json" with open(simulation_config) as f: simulation_config_dict = json.load(f) sim = CircuitSimulation(simulation_config) .. code:: ipython3 from bluepysnap import Simulation as snap_sim snap_access = snap_sim(simulation_config) import pandas as pd from bluepysnap import Simulation as snap_sim all_nodes = pd.concat([x[1] for x in snap_access.circuit.nodes.get()]) dstut_cells = all_nodes[all_nodes["etype"] == "dSTUT"].index.to_list() .. code:: ipython3 sim.instantiate_gids(dstut_cells, add_stimuli=True) t_stop = 100.0 sim.run(t_stop) The plot displays the voltage traces simulated for each cell in our circuit. .. code:: ipython3 plt.figure(figsize=(18, 12)) for cell_id in sim.cells: time = sim.get_time_trace() voltage = sim.get_voltage_trace(cell_id) plt.plot(time, voltage, label=cell_id) plt.xlabel("Time (ms)") plt.ylabel("Voltage (mV)") .. image:: sonata-network_files/sonata-network_11_0.png Let’s focus on 3 cells for better visualization .. code:: ipython3 sim.cells = dict(list(sim.cells.items())[:3]) plt.figure(figsize=(18, 12)) for cell_id in sim.cells: time = sim.get_time_trace() voltage = sim.get_voltage_trace(cell_id) plt.plot(time, voltage, label=cell_id) plt.xlabel("Time (ms)") plt.ylabel("Voltage (mV)") plt.legend() .. image:: sonata-network_files/sonata-network_13_0.png We are now ready to extract features. First, we will build the data structure for eFEL .. code:: ipython3 traces = [] for cell_id in sim.cells: voltage = sim.get_voltage_trace(cell_id) trace = {} trace['T'] = time trace['V'] = voltage trace['stim_start'] = [20] trace['stim_end'] = [90] traces.append(trace) Next, we choose the specific features of interest .. code:: ipython3 features = ['peak_time', 'AHP_time_from_peak', 'AP_height', 'AHP_depth_abs', 'all_ISI_values'] Finally, we perform the feature extraction .. code:: ipython3 traces_results = efel.get_feature_values(traces, features) The plot below shows action potential (AP) height and depth of those 3 cells .. code:: ipython3 import pylab for trace, trace_result, cell_id in zip(traces, traces_results, sim.cells): time = trace['T'] voltage = trace['V'] peak_times = trace_result['peak_time'] ahp_time = trace_result['AHP_time_from_peak'] ap_heights = trace_result['AP_height'] AHP_depth_abss = trace_result['AHP_depth_abs'] pylab.figure(figsize=(10, 6)) pylab.title(cell_id) pylab.plot(time,voltage) pylab.plot(peak_times, ap_heights, 'o') pylab.plot(peak_times+ahp_time, AHP_depth_abss, 'o') pylab.xlabel('Time (ms)') pylab.ylabel('Vm (mV)') pylab.show() .. image:: sonata-network_files/sonata-network_21_0.png .. image:: sonata-network_files/sonata-network_21_1.png .. image:: sonata-network_files/sonata-network_21_2.png Now, let’s overlay the durations of the inter-spike intervals (ISIs) for a clearer visualization of the timing between spikes .. code:: ipython3 for trace, trace_result, cell_id in zip(traces, traces_results, sim.cells): time = trace['T'] voltage = trace['V'] peak_times = trace_result['peak_time'] ap_heights = trace_result['AP_height'] all_isi_values = trace_result['all_ISI_values'] pylab.figure(figsize=(10, 6)) pylab.title(cell_id) pylab.plot(time, voltage, label='Voltage Trace') pylab.plot(peak_times, ap_heights, 'o', label='Spike Peaks') for i in range(len(peak_times) - 1): start_spike_time = peak_times[i] end_spike_time = peak_times[i + 1] duration = round(all_isi_values[i], 2) y_position = max(ap_heights[i], ap_heights[i + 1]) + 1 # Check if it's the first ISI line to be drawn and add a label, otherwise draw without a label if i == 0: pylab.plot([start_spike_time, end_spike_time], [y_position, y_position], 'r-', lw=2, label='inter-spike-intervals (ISI)') else: pylab.plot([start_spike_time, end_spike_time], [y_position, y_position], 'r-', lw=2) # Adjust text position to be slightly lower midpoint = (start_spike_time + end_spike_time) / 2 pylab.text(midpoint, y_position - 3, f'{duration} ms', verticalalignment='bottom', horizontalalignment='center', color='red') pylab.xlabel('Time (ms)') pylab.ylabel('Vm (mV)') pylab.legend(loc='lower right') pylab.show() .. image:: sonata-network_files/sonata-network_23_0.png .. image:: sonata-network_files/sonata-network_23_1.png .. image:: sonata-network_files/sonata-network_23_2.png