Measuring Electrophysiological Activity in Acute Brain Slices, Spheroids, and Organoids Using 3D High-Density Multielectrode Arrays.
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Python · 1,693 lines · 58 KB · no license
- # -*- coding: utf-8 -*-
- """
- Created on Mon Jan 30 16:16:17 2023
- @author: Francesco Mainardi
- """
- from scipy import stats
- import customtkinter
- import numpy as np
- import pandas as pd
- import h5py
- import matplotlib.pyplot as plt
- import os
- from tkinter import PhotoImage
- import glob
- from scipy.signal import butter, filtfilt
- import json
- from tkinter import filedialog
- from tkinter import Tk
- from tkinter.filedialog import askdirectory
- import subprocess
- import pickle
- import matplotlib.cm as cm
- df=pd.read_csv(r'channels ids table.csv', header=None,sep=';')
- # df2=pd.DataFrame(np.zeros((1,64)))
- # df=pd.concat([df, df2])
- # df.insert(64, int(64), np.zeros(65))
- # df=df.shift(periods=1)
- # df=df.shift(periods=1, axis='columns')
- conditions='Y'
- fpathDATA =''
- fnBRW =''
- fnBXR=''
- saving_path=''
- dfPSTH=0
- time_window_total=0
- blind_time_ms=0
- lfpduration_ms=0
- N2A_N2B_resolution_ms=0
- stim_duration_ms=0
- precisionvalue_ms=1
- lowpassvalue=0
- min_peak=0
- max_peak=0
- min_peak_distance_ms=0
- time_sec=0
- check_time_ms=0
- time_window_division_sec=1
- i=0
- ROI=[]
- ROIstr=[]
- ID=0
- freqvalue=0
- stimtimes=0
- map_mea = np.zeros((64, 64))
- b_map=[]
- c_map=[]
- bin_size=0
- ISI_bin_size=0
- bin_max=0
- pre_stimulus=0
- post_stimulus=0
- num_permutations=0
- total_time=0
- tart_frames=np.zeros(10000)
- P_Values=np.zeros(len(ROI))
- BRpath=""
- ROIpath=""
- save_folder=""
- BXR=""
- BRW=""
- localize_array=np.zeros(4096)
- customtkinter.set_appearance_mode("dark")
- customtkinter.set_default_color_theme("dark-blue")
- root= customtkinter.CTk(className="Python Analysis software for HD-MEA", fg_color="black")
- root.geometry("1890x920")
- root.title("Python Analysis software for HD-MEA")
- root.resizable(width=False, height=False)
- #img_bg=PhotoImage(file="python.png")
- #labelb= customtkinter.CTkLabel(master=root, width=250, height=250,text="", image=img_bg, fg_color="transparent")
- #labelb.place(x=725, y=315)
- def set_nan_in_lfp_matrix(lfp_matrix, lfp_N2A):
- for i in range(lfp_matrix.shape[0]):
- if np.isnan(lfp_N2A[i]):
- lfp_matrix[i, :] = np.nan
- return lfp_matrix
- def testprint():
- print(localize_array)
- def apri_image_processing():
- global localize_array
- subprocess.call(["python", "ImageProcessing.py"])
- with open('array.dat', 'rb') as file:
- tag_array = pickle.load(file)
- localize_array=tag_array
- df
- def apri_concatenator():
- subprocess.call(["python", "concatenator_new_1.3_GUI.py"])
- def on_closing():
- root.destroy()
- root.quit()
- def browse_ROI():
- global ROIpath
- loadROIpath.delete(0,1000)
- ROIpath = filedialog.askopenfilename()
- loadROIpath.insert(0, ROIpath)
- def browse_BRW():
- global BRW
- pacefname.delete(0,1000)
- BRW = filedialog.askopenfilename()
- pacefname.insert(0, BRW)
- def browse_BXR():
- global BXR
- fnameBXR.delete(0,1000)
- BXR = filedialog.askopenfilename()
- fnameBXR.insert(0, BXR)
- def browse_save():
- global save_folder
- savepacepath.delete(0,1000)
- root = Tk()
- root.withdraw()
- save_folder = askdirectory()
- savepacepath.insert(0, save_folder)
- def converti_coordinate_in_id(riga, colonna, num_colonne):
- return (riga - 1) * num_colonne + (colonna - 1)
- def estrai_riga_colonna_da_json(file_json):
- righe = []
- colonne = []
- # Carica il file JSON
- with open(file_json) as f:
- data = json.load(f)
- # Estrai le righe e le colonne di ogni elemento
- for group in data['ChsGroups']:
- for elemento in group['Chs']:
- righe.append(elemento['Row'])
- colonne.append(elemento['Col'])
- return righe, colonne
- def ROI_from_ROI():
- righe, colonne = estrai_riga_colonna_da_json(loadROIpath.get())
- global ROI
- for i in range(len(righe)):
- ROI.append(converti_coordinate_in_id(righe[i], colonne[i], 64))
- ids.delete(0,1000)
- global ROIstr
- stringID=str(ROI[i])
- ROIstr.append('['+stringID+']')
- ids.insert(0, ROIstr)
- global map_mea
- global b_map
- global c_map
- num1=righe[i]
- num2=colonne[i]
- b_map.append(num1-1)
- c_map.append(num2-1)
- map_mea[b_map, c_map]=100
- plt.imshow(map_mea, cmap='brg', interpolation='nearest')
- plt.savefig('img.png', bbox_inches='tight', transparent=True, dpi=60)
- img_map=PhotoImage(file="img.png")
- frame = customtkinter.CTkFrame(master=root, width=120, height=120, fg_color="transparent")
- frame.place(x=1040, y=0)
- label= customtkinter.CTkLabel(master=frame, width=120, height=120, text="", image=img_map)
- label.pack(pady=2, padx=1)
- def add_to_list ():
- ids.delete(0,1000)
- global ROI
- global ROIstr
- ROI.append(ID)
- stringID=str(ID)
- ROIstr.append('['+stringID+']')
- ids.insert(0, ROIstr)
- global map_mea
- global b_map
- global c_map
- num1=int(entry1.get())
- num2=int(entry2.get())
- b_map.append(num1-1)
- c_map.append(num2-1)
- map_mea[b_map, c_map]=100
- plt.imshow(map_mea, cmap='brg', interpolation='nearest')
- plt.savefig('img.png', bbox_inches='tight', transparent=True, dpi=60)
- img_map=PhotoImage(file="img.png")
- frame = customtkinter.CTkFrame(master=root, width=120, height=120, fg_color="transparent")
- frame.place(x=1040, y=0)
- label= customtkinter.CTkLabel(master=frame, width=120, height=120, text="", image=img_map)
- label.pack(pady=2, padx=1)
- def ROI_clear ():
- global ROI
- ROI=[]
- global ROIstr
- ROIstr=[]
- global b_map
- global c_map
- global map_mea
- b_map=[]
- c_map=[]
- map_mea=np.zeros((64, 64))
- plt.imshow(map_mea, cmap='brg', interpolation='nearest')
- plt.savefig('img.png', bbox_inches='tight', transparent=True, dpi=60)
- img_map=PhotoImage(file="img.png")
- frame = customtkinter.CTkFrame(master=root, width=120, height=120, fg_color="transparent")
- frame.place(x=1040, y=0)
- label= customtkinter.CTkLabel(master=frame, width=120, height=120, text="", image=img_map)
- label.pack(pady=2, padx=1)
- ids.delete(0,1000)
- def assign_path_pace ():
- global fnBRW
- global saving_path
- global fnBXR
- fnBRW = str(pacefname.get())
- fnBXR = str(fnameBXR.get())
- saving_path = str(savepacepath.get())
- def conversion ():
- global ID
- RESULTS.delete(0, 10)
- num1=int(entry1.get())
- num2=int(entry2.get())
- #b_map.append(num1-1)
- #c_map.append(num2-1)
- ID=int(df.loc[num1, num2])
- RESULTS.insert(0, ID)
- def lfp_duration(value):
- VALUE.delete(0,25)
- global lfpduration_ms
- lfpduration_ms=value
- VALUE.insert(0, value)
- def N2resolution(value):
- VALUE1.delete(0,25)
- global N2A_N2B_resolution_ms
- N2A_N2B_resolution_ms=value
- VALUE1.insert(0, value)
- def blind_time(value):
- VALUE2.delete(0,25)
- global blind_time_ms
- blind_time_ms=round(value, 1)
- VALUE2.insert(0, round(value, 1))
- def lowpassfilter(value):
- VALUE3.delete(0,25)
- global lowpassvalue
- lowpassvalue=value
- VALUE3.insert(0, value)
- def set_freqvalue01():
- global freqvalue
- freqvalue=0.1
- def set_freqvalue6():
- global freqvalue
- freqvalue=6
- def set_freqvalue10():
- global freqvalue
- freqvalue=10
- def set_freqvalue20():
- global freqvalue
- freqvalue=20
- def set_freqvalue50():
- global freqvalue
- freqvalue=50
- def set_freqvalue100():
- global freqvalue
- freqvalue=100
- def set_stimvalue1():
- global stimtimes
- stimtimes=0
- def set_stimvalue5():
- global stimtimes
- stimtimes=5
- def set_stimvalue10():
- global stimtimes
- stimtimes=10
- def binvalue(value):
- VALUE5.delete(0,25)
- global bin_size
- bin_size=int(value)
- VALUE5.insert(0, value)
- def binvalue2(value):
- VALUE52.delete(0,25)
- global ISI_bin_size
- ISI_bin_size=int(value)
- VALUE52.insert(0, value)
- def bin_max_value(value):
- VALUE53.delete(0,25)
- global bin_max
- bin_max=int(value)
- VALUE53.insert(0, value)
- def total_time(value):
- VALUE54.delete(0,25)
- global total_time
- total_time=int(value)
- VALUE54.insert(0, value)
- def prevalue(value):
- VALUE6.delete(0,25)
- global pre_stimulus
- pre_stimulus=int(value)
- VALUE6.insert(0, value)
- def postvalue(value):
- VALUE7.delete(0,25)
- global post_stimulus
- post_stimulus=int(value)
- VALUE7.insert(0, value)
- def permvalue(value):
- VALUE8.delete(0,25)
- global num_permutations
- num_permutations=int(value)
- VALUE8.insert(0, value)
- def spont_activity():
- assign_path_pace()
- global fpathDATA
- global fnBRW
- global fnBXR
- global saving_path
- global ISI_bin_size
- global pre_stimulus
- global post_stimulus
- global num_permutations
- global dfPSTH
- global c
- global P_Values
- global time_window_total
- global localize_array
- global total_time_s
- global bin_max
- ROI.sort()
- plotfolder="ISI plots"
- plotpath=os.path.join(saving_path, plotfolder)
- os.mkdir(plotpath)
- f = h5py.File(fnBXR,mode='r')
- SpikeTimes=np.array(f['3BResults']['3BChEvents']['SpikeTimes'])
- SpikeChIDs=np.array(f['3BResults']['3BChEvents']['SpikeChIDs'])
- SpikeUnits=np.array(f['3BResults']['3BChEvents']['SpikeUnits'])
- ChEvents=[SpikeChIDs, SpikeTimes, SpikeUnits]
- df=pd.DataFrame(data=ChEvents)
- c=0
- limit = int(bin_max/ISI_bin_size)
- ISI3d = np.zeros((limit, int(len(ROI))))
- isi_bins = np.arange(0, bin_max, ISI_bin_size)
- ISIdf=pd.DataFrame(index=isi_bins)
- ISIdf.index.name='BIN (ms)'
- features_list=['AV. FREQ', 'MODE FREQ.', 'SEM', 'CV', 'CV2']
- ISIfeatures=pd.DataFrame(index=features_list)
- for _ in range(len(ROI)):
- valore_cercato = ROI[c]
- # Seleziona la prima riga
- prima_riga = df.iloc[0]
- # Filtra colonne mantenendo solo quella in cui compare il valore cercato
- colonna_mantenuta = prima_riga[prima_riga == valore_cercato]
- # Seleziona solo le colonne desiderate dal DataFrame originale
- df_filtrato = df[colonna_mantenuta.index]
- ultima_riga = df_filtrato.iloc[-1]
- # Filtra colonne mantenendo solo quella in cui compare il valore cercato
- colonna_mantenuta = ultima_riga[ultima_riga > 0]
- # Seleziona solo le colonne desiderate dal DataFrame originale
- df_filtrato = df_filtrato[colonna_mantenuta.index]
- ISI_values, features = calculate_isi_histogram(df_filtrato.iloc[1], ISI_bin_size, bin_max, valore_cercato, plotpath)
- dfISI=pd.DataFrame(data=ISI_values)
- dfISI = dfISI.rename(columns={0: 'bin', 1: 'probability'})
- dfISI.to_csv('%s/ISI_Channel_%s.csv'%(saving_path, ROI[c]), decimal=',', sep =';')
- array= ISI_values[:,1]
- if len(array) < (limit) :
- array=np.pad(array, (0, limit - len(array)), 'constant')
- else:
- array=array[:limit]
- ISI3d[:, c] = array
- ISIdf[ROI[c]]=array
- ISIfeatures[ROI[c]]=features
- c=c+1
- ISIdf.to_csv('%s/ISI_Master.csv'%(saving_path), decimal=',', sep =';')
- ISIfeatures.to_csv('%s/Units_Features.csv'%(saving_path), decimal=',', sep =';')
- i=0
- fig = plt.figure(figsize=(8, 6))
- ax = fig.add_subplot(111, projection='3d')
- #colors = np.tile(['r', 'g', 'b', 'c', 'm', 'y', 'k', 'w', 'orange', 'purple'], 10)[:100]
- #colors = plt.cm.tab20.colors[:100]
- num_colors = 300
- colors = [cm.hsv(i/num_colors) for i in range(num_colors)]
- color_hex = [cm.colors.to_hex(color) for color in colors]
- set_lim=len(ROI)
- #x = np.arange(0, limit)
- x=isi_bins
- ys = np.arange(len(ROI))
- z=np.zeros(len(x))
- dx=ISI_bin_size
- ax.set_ylim([0, set_lim])
- # Plottaggio delle barre
- for i in range(len(ys)):
- y=ys[i]
- dz = ISI3d[:, i]
- ax.bar3d(x, y, z, dx, 0.1, dz, color=colors[i])
- i=i+1
- ax.xaxis.pane.fill = False
- ax.yaxis.pane.fill = False
- ax.zaxis.pane.fill = False
- ax.set_zlim(0, 0.5)
- ax.set_zlabel('Probability density')
- plt.xlabel('ISI (ms)')
- plt.ylabel('Channel ID')
- plt.yticks(np.arange(len(ROI)), ROI, fontsize=3, rotation=-45)
- filename = "ISI_3D.png" # Il nome del file del grafico
- output_path = os.path.join(plotpath, filename) # Percorso completo del file di output
- plt.savefig(output_path)
- plt.close()
- #plt.show()
- fig = plt.figure(figsize=(8, 6))
- matrice = np.arange(64*64).reshape(64, 64)
- coordinate_x, coordinate_y = trova_coordinate(matrice, ROI)
- plt.gca().invert_yaxis()
- for id, x, y, colore in zip(ROI, coordinate_x, coordinate_y, colors):
- plt.scatter(y, x, color=colore, label=f'ID: {id}', s=8)
- plt.xlim(0, 63) # Imposta i limiti dell'asse x
- plt.ylim(0, 63) # Imposta i limiti dell'asse y
- plt.gca().set_aspect('equal', adjustable='box')
- plt.gca().invert_yaxis() # Inverti l'orientamento dell'asse Y
- plt.title('ISI map')
- filename = "ISI_map.png" # Il nome del file del grafico
- output_path = os.path.join(plotpath, filename) # Percorso completo del file di output
- plt.savefig(output_path)
- plt.close()
- #plt.show()
- def trova_coordinate(matrice, id_casuali):
- coordinate_x = []
- coordinate_y = []
- for id in id_casuali:
- indice = np.where(matrice == id)
- if len(indice[0]) > 0: # Verifica se l'ID è presente nella matrice
- coordinate_x.append(indice[0][0])
- coordinate_y.append(indice[1][0])
- return coordinate_x, coordinate_y
- def calculate_cv(data):
- # Calcola la deviazione standard dei dati
- std_dev = np.std(data)
- # Calcola la media dei dati
- mean = np.mean(data)
- # Calcola il coefficiente di variazione
- cv = std_dev / mean
- return cv
- def calculate_cv2(data):
- cv2_values = []
- for i in range(len(data) - 1):
- ISIn = data[i]
- ISIn_plus_1 = data[i + 1]
- numerator = 2 * abs(ISIn_plus_1 - ISIn)
- denominator = ISIn + ISIn_plus_1
- cv2_values.append(numerator / denominator)
- return np.average(cv2_values)
- def calculate_isi_histogram(spikes, bin_size, bin_max, channel_id, plotpath):
- global total_time
- spikes = spikes/17.8
- isi = np.diff(spikes)
- isi = [x for x in isi if x <= bin_max]
- sem=np.std(isi)
- cv=calculate_cv(isi)
- cv2=calculate_cv2(isi)
- # Crea un istogramma degli intervalli tra gli spike
- isi_bins = np.arange(0, np.max(isi), bin_size)
- isi_hist, _ = np.histogram(isi, bins=isi_bins)
- # Calcola la densità di probabilità dell'istogramma
- isi_pdf = isi_hist / (len(spikes)) #* bin_size)
- ind_mode=np.where(isi_pdf == max(isi_pdf))[0][0]
- isi_mode = isi_bins[ind_mode]
- modelimit=isi_mode+sem
- moda=1/((isi_bins[ind_mode])/1000)
- error=abs((1000/modelimit)-moda)
- moda=round(moda, 2)
- #calcola la frequenza di firing in modo brutale
- av_freq=len(spikes)/total_time
- av_freq=round(av_freq, 2)
- text_freq=str(moda)
- text_av= str (av_freq)
- text_cv=str(round(cv,2))
- text_cv2=str(round(cv2,2))
- text_sem=str(round(error, 2))
- # Crea un array di valori x per il grafico
- x = isi_bins[:-1] + bin_size / 2
- # Plotta l'istogramma
- plt.figure(figsize=(8, 6))
- plt.bar(x, isi_pdf, width=bin_size, color='black')
- plt.xlabel('ISI (ms)')
- plt.ylabel('Probability Density')
- plt.title('ISI ch. %s | Av. Freq. = %s ± %s Hz | Mode = %s Hz | CV = %s | CV2 = %s' % (channel_id,text_av, text_sem, text_freq, text_cv, text_cv2))
- #plt.text(120, 0.05, f'Freq: {av_freq:.3f} Hz', color='black')
- plt.xlim(0, bin_max)
- filename = "ISI_channel_%s.png" % (channel_id) # Il nome del file del grafico
- output_path = os.path.join(plotpath, filename) # Percorso completo del file di output
- plt.savefig(output_path)
- plt.locator_params(axis='x', nbins=15)
- #plt.show()
- plt.close()
- feature=[av_freq, moda, error, cv, cv2]
- return np.column_stack((x, isi_pdf)), feature
- def pacemakers():
- assign_path_pace()
- global fpathDATA
- global fnBRW
- global fnBXR
- global saving_path
- global bin_size
- global pre_stimulus
- global post_stimulus
- global num_permutations
- global dfPSTH
- global c
- global P_Values
- global ROI
- global localize_array
- bin_edges = np.arange(-pre_stimulus, post_stimulus + bin_size/2, bin_size)
- median_index=int(len(bin_edges)/2)
- plotfolder="PSTH plots"
- plotpath=os.path.join(saving_path, plotfolder)
- os.mkdir(plotpath)
- f = h5py.File(fnBXR,mode='r')
- SpikeTimes=np.array(f['3BResults']['3BChEvents']['SpikeTimes'])
- SpikeChIDs=np.array(f['3BResults']['3BChEvents']['SpikeChIDs'])
- SpikeUnits=np.array(f['3BResults']['3BChEvents']['SpikeUnits'])
- ChEvents=[SpikeChIDs, SpikeTimes, SpikeUnits]
- df=pd.DataFrame(data=ChEvents)
- num_frequenze=int((pre_stimulus+post_stimulus)/bin_size)
- SR=f['3BRecInfo']['3BRecVars']['SamplingRate'][0]
- index=[]
- i=0
- ST=np.delete(SpikeTimes, index)
- SCID=np.delete(SpikeChIDs, index)
- timeArt()
- abs_tart_frames=((tart_frames-100)/4096)/int(SR/1000)
- ROI.sort()
- colonne=np.array(ROI)
- dfPSTH=pd.DataFrame(columns=colonne)
- P_Values=np.zeros(len(ROI))
- c=0
- # Inizializza la matrice 3D vuota
- psth3d = np.zeros((num_frequenze, int(len(ROI))))
- # Loop sui canali nella ROI
- for _ in range(len(ROI)):
- valore_cercato = ROI[c]
- # Seleziona la prima riga
- prima_riga = df.iloc[0]
- # Filtra colonne mantenendo solo quella in cui compare il valore cercato
- colonna_mantenuta = prima_riga[prima_riga == valore_cercato]
- # Seleziona solo le colonne desiderate dal DataFrame originale
- df_filtrato = df[colonna_mantenuta.index]
- ultima_riga = df_filtrato.iloc[-1]
- # Filtra colonne mantenendo solo quella in cui compare il valore cercato
- colonna_mantenuta = ultima_riga[ultima_riga > 0]
- # Seleziona solo le colonne desiderate dal DataFrame originale
- df_filtrato = df_filtrato[colonna_mantenuta.index]
- indexID = df_filtrato.iloc[1]
- indexID = indexID / int(SR/1000)
- indexID=delete_stim_spike(indexID, abs_tart_frames, bin_size)
- psth = PSTH4(indexID, abs_tart_frames, ROI[c], bin_size, pre_stimulus, post_stimulus, num_permutations, plotpath, median_index)
- psth3d[:, c] = psth
- c=c+1
- i=0
- i=0
- fig = plt.figure()
- ax = fig.add_subplot(111, projection='3d')
- colors = np.tile(['r', 'g', 'b', 'c', 'm', 'y', 'k', 'w', 'orange', 'purple'], 10)[:100]
- set_lim=len(ROI)
- x = bin_edges[:-1]
- ys = np.arange(len(ROI))
- z=np.zeros(len(bin_edges[:-1]))
- dx=bin_size
- ax.set_ylim([0, set_lim])
- # Plottaggio delle barre
- for i in range(len(ys)):
- y=ys[i]
- dz = psth3d[:, i]
- ax.bar3d(x, y, z, dx, 0.1, dz, color=colors[i])
- i=i+1
- plt.xlabel('Bins')
- plt.ylabel('Channel ID')
- plt.yticks(np.arange(len(ROI)), ROI, fontsize=7, rotation=-45)
- filename = "PSTH_3D.png" # Il nome del file del grafico
- output_path = os.path.join(plotpath, filename) # Percorso completo del file di output
- plt.savefig(output_path)
- plt.show()
- repeatimes=2
- psth3dNx=np.repeat(psth3d, repeatimes, axis=1)
- i=0
- ticks = []
- tick_positions = []
- for i in range(len(ROI)):
- ticks.append(ROI[i])
- tick_positions.append((i*(repeatimes)+int(repeatimes/2)))
- plt.figure(figsize=(8, 6))
- plt.imshow(np.transpose(psth3dNx, (1, 0)), cmap='viridis', origin='upper', vmin=0, vmax=100)
- plt.xlabel('Time (ms)')
- labels=np.arange(len(psth3dNx[:,0])+1)
- labels=labels[:len(psth3dNx[:,0])+1:5]
- labels_bin = bin_edges[::5]
- plt.xticks(labels, labels_bin, fontsize=7)
- plt.ylabel('Channnel ID')
- plt.yticks(tick_positions, ticks, fontsize=7)
- plt.colorbar()
- filename = "PSTH_Heatmap.png" # Il nome del file del grafico
- output_path = os.path.join(plotpath, filename) # Percorso completo del file di output
- plt.savefig(output_path)
- plt.show()
- #dfPSTH.loc[len(dfPSTH)] = P_Values
- dfPSTH.to_csv('%s/PSTH_P-values.csv'%(saving_path), index = True, decimal=',', sep=';')
- dftag = pd.DataFrame(columns=ROI)
- dftag.loc[0] = localize_array[ROI]
- dftag.to_csv('%s/ROI tagged.csv'%(saving_path), index = True, decimal=',', sep=';')
- def delete_stim_spike(spike_times, stim_times, bin_size):
- global stimtimes
- global freqvalue
- n_pulses=stimtimes
- stim_distance_ms=1000/freqvalue
- if n_pulses == 1:
- indices_to_remove = []
- for stim_time in stim_times:
- start_time = stim_time - 1
- end_time = stim_time + 1.3
- indices = np.where((spike_times >= start_time) & (spike_times <= end_time))[0]
- indices_to_remove.extend(indices)
- else:
- indices_to_remove = []
- for stim_time in stim_times:
- i=0
- for _ in range(n_pulses):
- start_time = stim_time + i*stim_distance_ms - 1
- end_time = stim_time + i * stim_distance_ms + 1.3
- indices = np.where((spike_times >= start_time) & (spike_times <= end_time))[0]
- indices_to_remove.extend(indices)
- i=i+1
- # rimuovi gli elementi di spike_times indicizzati dagli indici trovati
- spike_times = spike_times.drop(spike_times.index[indices_to_remove])
- return spike_times
- def PSTH4(ST, abs_tart_frames, channel_id, bin_size, pre_stimulus, post_stimulus, num_permutations, plotpath, median_index):
- global bin_edges
- global dfPSTH
- global c
- global P_Values
- global bin_edges
- global localize_array
- # Crea un array di bin
- bin_edges = np.arange(-pre_stimulus, post_stimulus + bin_size/2, bin_size)
- # Inizia un loop sugli stimoli
- num_stimuli = len(abs_tart_frames)
- psth = np.zeros_like(bin_edges[:-1])
- for i in range(num_stimuli):
- # Seleziona il periodo di tempo da analizzare
- start_time = abs_tart_frames[i] - pre_stimulus
- end_time = abs_tart_frames[i] + post_stimulus
- # Conta quanti spike ci sono in ogni bin
- data_to_analyze = ST[(ST >= start_time) & (ST <= end_time)]
- counts, _ = np.histogram(data_to_analyze - abs_tart_frames[i], bins=bin_edges)
- psth += counts
- # Normalizza il PSTH dividendo per il numero di stimoli
- #psth = psth / num_stimuli
- psth = psth / num_stimuli / bin_size * 1000
- # Calcola la media della frequenza di scarica pre e post stimolo
- bin_removed_index = int(len(bin_edges) / 2)
- pre_stimulus_fr = np.mean(psth[:bin_removed_index])
- pre_stimulus_std = np.std(psth[:bin_removed_index])
- post_stimulus_fr = np.mean(psth[bin_removed_index+1:])
- post_stimulus_z_scores = (psth[bin_removed_index+1:] - pre_stimulus_fr) / pre_stimulus_std
- sig_bars = np.where(post_stimulus_z_scores > 2)[0]
- # Concatena i dati per il test delle permutazioni
- data = np.concatenate([psth[:bin_removed_index], psth[bin_removed_index+1:]])
- psth[median_index]=psth[median_index]*(bin_size/(bin_size-1.3))
- # Esegui il test delle permutazioni
- perm_fr_diffs = np.zeros(num_permutations)
- for i in range(num_permutations):
- np.random.shuffle(data)
- perm_pre_fr = np.mean(data[:bin_removed_index])
- perm_post_fr = np.mean(data[bin_removed_index:])
- perm_fr_diffs[i] = perm_post_fr - perm_pre_fr
- # Calcola il p-value
- observed_fr_diff = post_stimulus_fr - pre_stimulus_fr
- p_value = (np.sum(perm_fr_diffs >= observed_fr_diff) + 1) / (num_permutations + 1)
- print(f'p-value: {p_value}')
- # Imposta il colore di base di tutti i bin a blu
- bar_color = 'black'
- colors = [bar_color] * len(bin_edges[:-1])
- # Imposta il colore del bin dello stimolo su giallo
- stim_color = 'black'
- stim_bin = int(pre_stimulus / bin_size) # indice del bin corrispondente allo stimolo
- colors[stim_bin] = stim_color
- #psth[stim_bin]=0 #riduzione valore per migliorare l'impatto visivo
- plt.figure(figsize=(8, 6))
- # Visualizza il PSTH
- plt.bar(bin_edges[:-1], psth, width=bin_size, color=colors, align='edge')
- # Calcola la media e la deviazione standard del pre-stimolo
- pre_stimulus_data = data[:bin_removed_index]
- pre_stimulus_mean = np.mean(pre_stimulus_data)
- pre_stimulus_std = np.std(pre_stimulus_data)
- # Aggiungi l'asterisco sopra le barre significative del post-stimolo
- sig_bars = []
- for i, post_stimulus_fr in enumerate(psth[bin_removed_index:]):
- z_score = (post_stimulus_fr - pre_stimulus_mean) / pre_stimulus_std
- if z_score > 1.645 or z_score < -1.645: # 95% di confidenza a 2 code
- sig_bars.append(i)
- plt.text(bin_edges[bin_removed_index+1+i]-bin_size/2, post_stimulus_fr, '*', color='red', ha='center', va='bottom')
- # Imposta l'etichetta delle barre significative
- plt.xlabel('Time (ms)')
- plt.ylabel('Firing rate (Hz)')
- plt.title('Peri-Stimulus Time Histogram channel %s [ %s ]' % (channel_id, localize_array[channel_id]))
- #plt.text(10, 120, f'permutational test p-value: {p_value:.3f}', color='black')
- # Imposta i limiti dell'asse x e y in modo che lo stimolo sia al centro del grafico e l'asse y sia congruo
- plt.xlim([-pre_stimulus, post_stimulus])
- plt.ylim(bottom=0, top=150)
- plt.locator_params(axis='x', nbins=20)
- plt.axvline(x=0, color='yellow', linestyle='--')
- plt.tight_layout()
- filename = "PSTH_channel_%s.png" % (ROI[c]) # Il nome del file del grafico
- output_path = os.path.join(plotpath, filename) # Percorso completo del file di output
- plt.savefig(output_path)
- plt.show()
- dfPSTH[ROI[c]] = psth
- P_Values[c] = p_value
- return psth
- def timeArt():
- global stimtimes
- global freqvalue
- #directory of .brw file
- global fnBRW
- global precision
- f = h5py.File(fnBRW,mode='r')
- MinVolt=float(f['3BRecInfo']['3BRecVars']['MinVolt'][0])
- MaxVolt=float(f['3BRecInfo']['3BRecVars']['MaxVolt'][0])
- BitDepth=f['3BRecInfo']['3BRecVars']['BitDepth'][0]
- SignalInversion=float(f['3BRecInfo']['3BRecVars']['SignalInversion'][0])
- SR=f['3BRecInfo']['3BRecVars']['SamplingRate'][0]
- rawdata = (f['3BData']['Raw'])
- chid=100
- stim_duration_ms=1
- precision=int(stim_duration_ms)
- precision_frames=int(((200)*SR)/1000)
- frame=np.zeros(100000000)
- n_artifacts=10000
- tart=np.zeros(n_artifacts)
- global tart_frames
- k=0
- j=1
- t=0
- for i in range(chid, len(rawdata), 4096):
- ADCCountsToMV = SignalInversion * ((MaxVolt-MinVolt)/2**BitDepth)
- offset=SignalInversion*MinVolt
- AnalogValue = offset + rawdata[i] *ADCCountsToMV
- if AnalogValue > 1500:
- frame[j]=(i-chid)/4096
- if (frame[j]-frame [j-1])>precision_frames:
- sec = ((i-chid)/4096)/SR
- msec = sec
- print(k,') ', 'sec=', round(msec, 2) ,'peak=', AnalogValue)
- tart[t]=msec
- tart_frames[t]=i
- k=k+1
- j=j+1
- t=t+1
- else:
- j=j+1
- resize_factor=t
- tart_frames=np.resize(tart_frames, resize_factor)
- def lowpass_filter(trace, SR):
- global lowpassvalue
- freq_sampling = SR
- # Applicazione del filtro lowpass a 2000 Hz
- cutoff_hz = lowpassvalue # frequenza di taglio in Hz
- order = 4 # ordine del filtro
- nyquist_hz = 0.5 * freq_sampling # frequenza di Nyquist
- cutoff_norm = cutoff_hz / nyquist_hz # frequenza di taglio normalizzata
- b, a = butter(order, cutoff_norm, btype='lowpass', analog=False)
- filtered_trace = filtfilt(b, a, trace)
- return filtered_trace
- def LFP_analysis():
- directory1='N2A'
- directory2='N2B'
- plotfolder='LFPs Plots'
- #directories of .brw file
- assign_path_pace()
- global fpathDATA
- global fnBRW
- global saving_path
- global stimtimes
- global freqvalue
- global localize_array
- precisionvalue_ms=5
- global localize_array
- path1 = os.path.join(saving_path, directory1)
- path2 = os.path.join(saving_path, directory2)
- path3 = os.path.join(saving_path, plotfolder)
- os.mkdir(path1)
- os.mkdir(path2)
- os.mkdir(path3)
- #data extraction from .brw file
- f = h5py.File(fnBRW,mode='r')
- MinVolt=float(f['3BRecInfo']['3BRecVars']['MinVolt'][0])
- MaxVolt=float(f['3BRecInfo']['3BRecVars']['MaxVolt'][0])
- BitDepth=f['3BRecInfo']['3BRecVars']['BitDepth'][0]
- SignalInversion=float(f['3BRecInfo']['3BRecVars']['SignalInversion'][0])
- SR=f['3BRecInfo']['3BRecVars']['SamplingRate'][0]
- rawdata = (f['3BData']['Raw'])
- #params and initializatio (k,j,h, are counters), chid is channel id for detection and the precision is the
- #frame interval that the script use to detect the same time artifact in differents frames
- frame=np.zeros(100000)
- n_artifacts=10000
- tart=np.zeros(n_artifacts)
- tart_frames=np.zeros(n_artifacts)
- blind_time_ms
- lfpduration_ms
- N2A_N2B_resolution_ms
- # if stimtimes == 0 :
- # stim_duration_ms=0
- # else:
- # stimduration_tot=((stimtimes/freqvalue)*1000)
- # stim_duration_ms=stimduration_tot - stimduration_tot/stimtimes
- k=0
- j=1
- t=0
- c=0
- a=0
- precision_frames=int((precisionvalue_ms*SR)/1000)
- blind_frames=int((blind_time_ms*SR)/1000)
- lfpduration_frames=int((lfpduration_ms*SR)/1000)
- N2A_N2B_resolution=int((N2A_N2B_resolution_ms*SR)/1000)
- ROI
- ROI.sort()
- limit=len(ROI)
- chid=ROI[0]
- #the script extract 1 values every 4096 (due to the matrix organization)
- #to extract one single trace corresponding for a single channel
- #then convert the digital data in analog data (mV) using the 3Brain formula
- #for conversion and when the script find a maxima print the value and the time in ms.
- #to avoid to detect the same time artifact in two adjecent frames the script compare each
- #values with the previous one, and if the difference in frames is under the precision value does not consider it
- #for stimulation different by the 0.1 Hz (6 Hz, 20 Hz, ...), change the precision value (calculate how many frames
- #correspond to ms between two stimuli) to extract the last stimuli
- #stim artifact detection
- for i in range(chid, len(rawdata), 4096):
- ADCCountsToMV = SignalInversion * ((MaxVolt-MinVolt)/2**BitDepth)
- offset=SignalInversion*MinVolt
- AnalogValue = offset + rawdata[i] *ADCCountsToMV
- if AnalogValue > 1500:
- frame[j]=(i-chid)/4096
- if (frame[j]-frame [j-1])>precision_frames:
- sec = ((i-chid)/4096)/SR
- msec = sec
- print(k,') ', 'sec=', round(msec, 2) ,'peak=', AnalogValue)
- tart[t]=msec
- tart_frames[t]=i
- k=k+1
- j=j+1
- t=t+1
- else:
- j=j+1
- resize_factor=t
- lfptraces=np.zeros(resize_factor)
- lfp_N2A=np.zeros(resize_factor)
- lfp_N2B=np.zeros(resize_factor)
- tart=np.resize(tart, resize_factor)
- tart_frames=np.resize(tart_frames, resize_factor)
- posizioniN2A=np.zeros(resize_factor)
- posizioniN2B=np.zeros(resize_factor)
- lfp_array=np.zeros(lfpduration_frames)
- lfp_matrix=np.zeros((len(lfptraces),lfpduration_frames))
- #peaks_array=[]
- k=0
- #peaks
- #N2peaks=np.zeros((resize_factor,2))
- k=0
- j=1
- t=0
- g=0
- i=0
- for _ in ROI:
- chid=ROI[c]
- for i in tart_frames:
- t=0
- g=0
- for _ in range(lfpduration_frames):
- ADCCountsToMV = SignalInversion * ((MaxVolt-MinVolt)/2**BitDepth)
- offset=SignalInversion*MinVolt
- index=int(tart_frames[k]+(blind_frames+g)*4096)
- AnalogValue = offset + rawdata[index] *ADCCountsToMV
- lfp_array[t]= AnalogValue
- t=t+1
- g=g+1
- #lfp_matrix[k,:]=lowpass_filter(lfp_array, SR)
- lfp_matrix[k,:]=lfp_array
- k=k+1
- i=0
- #std_noise= [np.std(arr) for arr in lfp_matrix]
- for _ in range(len(lfp_N2A)):
- if len(lfp_matrix[i] > 0):
- lfp_N2A[i]=min(lfp_matrix[i,0:N2A_N2B_resolution])
- posizioniN2A[i] = np.where(lfp_matrix[i]==min(lfp_matrix[i,0:N2A_N2B_resolution]))[0][0]
- i=i+1
- else:
- lfp_N2A[i] = None
- posizioniN2A[i]=None
- i=0
- for i in range(len(lfp_N2A)):
- if lfp_N2A[i] > (np.mean(lfp_matrix[i,-100:])-3*np.std(lfp_matrix[i,-100:])):
- lfp_N2A[i] = np.nan
- print(c , ')', ROI[c])
- lfp_N2A=np.resize(lfp_N2A, resize_factor)
- print('lfp N2A values:', lfp_N2A)
- mean=np.nanmean(lfp_N2A)
- sem=np.nanstd(lfp_N2A) / np.sqrt(np.sum(~np.isnan(lfp_N2A)))
- print('lfp N2A avarege:', mean, '+- ', sem, 'pA')
- #N2peaks=np.zeros((resize_factor,2))
- i=0
- j=0
- for _ in range(len(lfp_N2B)):
- start= int (posizioniN2A[i])+int(N2A_N2B_resolution)
- if len(lfp_matrix[i, start:start+N2A_N2B_resolution]):
- lfp_N2B[i]=min(lfp_matrix[i, start:start+N2A_N2B_resolution])
- position = (np.where(lfp_matrix[i, start:start+N2A_N2B_resolution]== lfp_N2B[i])[0])+start
- posizioniN2B[i]=position[0]
- i=i+1
- else:
- lfp_N2B[i]=None
- i=i+1
- for i in range(len(lfp_N2B)):
- if lfp_N2B[i] > (np.mean(lfp_matrix[i,-100:])-2*np.std(lfp_matrix[i,-100:])):
- lfp_N2B[i] = np.nan
- lfp_N2B=np.resize(lfp_N2B, resize_factor)
- print('lfp N2B values:', lfp_N2B)
- mean=np.nanmean(lfp_N2B)
- sem=np.nanstd(lfp_N2B) / np.sqrt(np.sum(~np.isnan(lfp_N2B)))
- print('lfp N2B avarege:', mean, '+- ', sem, 'pA')
- for i in range(len(posizioniN2A)):
- mean_value = np.nanmean(posizioniN2A)
- if not (mean_value - 18 <= posizioniN2A[i] <= mean_value + 18):
- posizioniN2A[i] = np.nan
- posizioniN2B[i] = np.nan
- lfp_N2A[i] = np.nan
- lfp_N2B[i] = np.nan
- consecutive_threshold = 5
- value_threshold = 1000
- i=0
- # Iterare lungo l'asse 0 di lfp_matrix
- for i in range(lfp_matrix.shape[0]):
- consecutive_count = 0
- # Iterare lungo l'asse 1 di lfp_matrix
- for j in range(lfp_matrix.shape[1]):
- if lfp_matrix[i, j] > value_threshold:
- consecutive_count += 1
- if consecutive_count >= consecutive_threshold:
- # Impostare a NaN i valori corrispondenti in lfp_N2A e lfp_N2B
- lfp_N2A[i] = np.nan
- lfp_N2B[i] = np.nan
- posizioniN2A[i]= np.nan
- posizioniN2B[i]= np.nan
- else:
- consecutive_count = 0
- i=0
- for i in range(lfp_matrix.shape[0]):
- if np.isnan(lfp_N2A[i]):
- lfp_matrix[i, :] = np.nan
- i=0
- #plotting LFP
- plt.figure(figsize=(8, 6))
- for _ in range(len(lfp_matrix[:, 0:resize_factor])):
- plt.plot(lfp_matrix[i, :], "black", alpha=0.1)
- if not np.isnan(posizioniN2A[i]):
- plt.plot(int(posizioniN2A[i]), lfp_matrix[i, int(posizioniN2A[i])], "o", color= "#686AAD", markersize=2)
- if not np.isnan(posizioniN2B[i]):
- plt.plot(int(posizioniN2B[i]), lfp_matrix[i, int(posizioniN2B[i])], "o", color= "orange", markersize=2)
- i = i + 1
- lfp_matrixav = set_nan_in_lfp_matrix(lfp_matrix, lfp_N2A)
- lfp_mean=np.zeros(len(lfp_matrixav[1,:]))
- i=0
- #avarege lfp and avarage N2A and N2B finded, the last check
- for _ in range(len(lfp_matrixav[1,:])):
- lfp_mean[i]=np.nanmean(lfp_matrixav[:,i])
- i=i+1
- N2Amean_frame = np.nanmean(posizioniN2A)
- if np.isnan(N2Amean_frame):
- N2Amean_frame = 0
- else:
- N2Amean_frame = int(N2Amean_frame)
- N2Bmean_frame = np.nanmean(posizioniN2B)
- if np.isnan(N2Bmean_frame):
- N2Bmean_frame = 0
- else:
- N2Bmean_frame = int(N2Bmean_frame)
- plt.plot(lfp_mean, "black", alpha = 0.8)
- if N2Amean_frame != 0:
- plt.plot(N2Amean_frame, lfp_mean[N2Amean_frame], "o", color="#686AAD", markersize = 8)
- if N2Bmean_frame != 0:
- plt.plot(N2Bmean_frame, lfp_mean[N2Bmean_frame], "o", color = "orange", markersize = 8)
- current_lower, current_upper = plt.gca().get_ylim()
- plt.ylim(current_lower, 250)
- plt.title('LFPs channel %s [%s]' % (ROI[c], localize_array[ROI[c]]))
- plt.xlabel("Time (ms)")
- plt.ylabel("Amplitude (μV)")
- #x = range(len(lfp_matrix[0, :]))
- #indices = [i // 18 for i in range(len(x))]
- #[labels = [str(index) if i % 18 == 0 else '' for i, index in enumerate(indices)]
- x = np.arange(0, len(lfp_matrix[0, :]), 18)
- labels = np.arange(0,len(x))
- labels = [str(val) for val in labels]
- plt.xticks(x, labels)
- filename = "LFPs_channel_%s.png" % (ROI[c]) # Il nome del file del grafico
- output_path = os.path.join(path3, filename) # Percorso completo del file di output
- plt.savefig(output_path)
- #plt.show()
- #savings
- timeArt=pd.DataFrame(data=tart)
- timeArt.to_csv('%s/timeArtifact_master.csv'%(saving_path), index = False, header=False, decimal=',')
- N2A=pd.DataFrame(data=lfp_N2A)
- N2ind=str(chid)
- N2A.to_csv('%s/%s/N2A_amplitudes_%s.csv'%(saving_path, directory1, N2ind), index = False, header=False, decimal=',')
- N2B=pd.DataFrame(data=lfp_N2B)
- N2B.to_csv('%s/%s/N2B_amplitudes_%s.csv'%(saving_path,directory2, N2ind), index = False, header=False, decimal=',')
- k=0
- j=1
- t=0
- g=0
- i=0
- c=c+1
- if a == (limit-1):
- break
- else:
- tart_frames=tart_frames+(ROI[a+1]-ROI[a])
- a=a+1
- os.chdir(saving_path+'/'+directory1)
- filenameG=[i for i in glob.glob('*.{}'.format("csv"))]
- filenameG.sort(key=len)
- dfTOTN2A = pd.concat([pd.read_csv(f, header=None) for f in filenameG], axis = 'columns')
- dfTOTN2A.to_csv('%s/%s/N2A_master.csv'%(saving_path, directory1), index = False, header=ROI, decimal=',', sep =';')
- os.chdir(saving_path+'/'+directory2)
- filenameG2=[i for i in glob.glob('*.{}'.format("csv"))]
- filenameG2.sort(key=len)
- dfTOTN2B = pd.concat([pd.read_csv(f, header=None) for f in filenameG2], axis='columns')
- dfTOTN2B.to_csv('%s/%s/N2B_master.csv'%(saving_path, directory2), index = False, header=ROI, decimal=',', sep=';')
- prefix="N2A_amplitudes"
- for filename in os.listdir(saving_path+"/N2A"):
- if filename.startswith(prefix): # se il file inizia con il prefisso desiderato
- file_path = os.path.join(saving_path +"/N2A", filename) # costruisci il percorso del file
- os.remove(file_path) # elimina il file
- prefix2="N2B_amplitudes"
- for filename in os.listdir(saving_path+"/N2B"):
- if filename.startswith(prefix2): # se il file inizia con il prefisso desiderato
- file_path = os.path.join(saving_path +"/N2B", filename) # costruisci il percorso del file
- os.remove(file_path) # elimina il file
- os.chdir(saving_path)
- #channel conversion GUI, ready
- frame = customtkinter.CTkFrame(master=root, fg_color="transparent")
- frame.place(x=10, y=0)
- label= customtkinter.CTkLabel(master=frame, width=250, text="ID Conversion", font=("Arial Rounded MT Bold", 25), text_color='white')
- label.pack(pady=2, padx=1)
- entry1= customtkinter.CTkEntry(master=frame, width=250, placeholder_text="first value", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- entry1.pack(pady=2, padx=1, expand=True)
- entry2= customtkinter.CTkEntry(master=frame, width=250, placeholder_text="second Value", font=("Arial Rounded MT Bold", 15), corner_radius=15)
- entry2.pack(pady=2, padx=1)
- button = customtkinter.CTkButton(master=frame, width=250, text="Conversion",font=("Arial Rounded MT Bold", 15), command=conversion,corner_radius=15 )
- button.pack(pady=2, padx=1)
- RESULTS = customtkinter.CTkEntry(master=frame, width=250, placeholder_text="", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- RESULTS.pack(pady=2, padx=1)
- button_add = customtkinter.CTkButton(master=frame, width=250, text="Add to list",font=("Arial Rounded MT Bold", 15), command=add_to_list,corner_radius=15)
- button_add.pack(pady=2, padx=1)
- button_clear = customtkinter.CTkButton(master=frame, width=250, text="Clear ROI",font=("Arial Rounded MT Bold", 15), command=ROI_clear,corner_radius=15)
- button_clear.pack(pady=2, padx=1)
- #LFP analysis GUI, add parameters, function and ROI get
- frame = customtkinter.CTkFrame(master=root, width=600, fg_color="transparent")
- frame.place(x=10, y=280)
- label= customtkinter.CTkLabel(master=frame, width=600, text="LFP Analysis", font=("Arial Rounded MT Bold", 25), text_color='white')
- label.pack(pady=2, padx=1)
- ######sliders##########
- label= customtkinter.CTkLabel(master=frame, width=600, text="LFP extraction time (ms)", font=("Arial Rounded MT Bold", 15), text_color='white')
- label.pack(pady=2, padx=1)
- VALUE= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- VALUE.pack(pady=2, padx=1)
- LFPduration= customtkinter.CTkSlider(master=frame, width=600, from_=0, to=25, number_of_steps=25, command=lfp_duration)
- LFPduration.pack(pady=2, padx=2)
- ####################
- label1= customtkinter.CTkLabel(master=frame, width=600, text="N2A N2B time window (ms)", font=("Arial Rounded MT Bold", 15), text_color='white')
- label1.pack(pady=2, padx=1)
- VALUE1= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- VALUE1.pack(pady=2, padx=1)
- resolution= customtkinter.CTkSlider(master=frame, width=600, from_=0, to=5, number_of_steps=10, command=N2resolution)
- resolution.pack(pady=2, padx=1)
- label2= customtkinter.CTkLabel(master=frame, width=600, text="blind time (ms)", font=("Arial Rounded MT Bold", 15), text_color='white')
- label2.pack(pady=2, padx=1)
- VALUE2= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- VALUE2.pack(pady=2, padx=1)
- blind= customtkinter.CTkSlider(master=frame, width=600, from_=0, to=5, number_of_steps=50, command=blind_time)
- blind.pack(pady=2, padx=1)
- label3= customtkinter.CTkLabel(master=frame, width=600, text="low pass filter frequency, for noise (Hz)", font=("Arial Rounded MT Bold", 15), text_color='white')
- label3.pack(pady=2, padx=1)
- VALUE3= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- VALUE3.pack(pady=2, padx=1)
- lowpass= customtkinter.CTkSlider(master=frame, width=600, from_=1000, to=10000, number_of_steps=9, command=lowpassfilter)
- lowpass.pack(pady=2, padx=1)
- frame = customtkinter.CTkFrame(master=root, width=600, fg_color="transparent")
- frame.place(x=10, y=740)
- button = customtkinter.CTkButton(master=frame, width=600, height=70, text="START LFP ANALYSIS", font=("Arial Rounded MT Bold", 25), command=LFP_analysis,corner_radius=30)
- button.pack(padx=1, pady=1)
- frame = customtkinter.CTkFrame(master=root, fg_color='transparent')
- frame.place(x=335, y=0)
- label= customtkinter.CTkLabel(master=frame, width=600, text="File Path", font=("Arial Rounded MT Bold", 25), text_color='white')
- label.pack(pady=2, padx=1)
- pacefname= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="BRW file name with extension", font=("Arial Rounded MT Bold", 15), corner_radius=15)
- pacefname.pack(pady=2, padx=1)
- fnameBXR= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="BXR file name with extension", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- fnameBXR.pack(pady=2, padx=1)
- savepacepath= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="save path", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- savepacepath.pack(pady=2, padx=1)
- loadROIpath= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="ROI file path and name", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- loadROIpath.pack(pady=2, padx=1)
- button_ROI = customtkinter.CTkButton(master=frame, width=600, text="Load ROI",font=("Arial Rounded MT Bold", 15), command=ROI_from_ROI,corner_radius=15)
- button_ROI.pack(pady=2, padx=1)
- # segmbutton = customtkinter.CTkButton(master=frame, width=600, text="Start image segmentation",font=("Arial Rounded MT Bold", 15), command=apri_image_processing,corner_radius=30)
- # segmbutton.pack(pady=2, padx=1)
- # segmbutton = customtkinter.CTkButton(master=frame, width=600, text="Concatenate multiple files",font=("Arial Rounded MT Bold", 15), command=apri_concatenator,corner_radius=30)
- # segmbutton.pack(pady=2, padx=1)
- frame = customtkinter.CTkFrame(master=root, width=1890)
- frame.place(x=0, y=260)
- progressbar_1 = customtkinter.CTkProgressBar(master=frame, width=1890, progress_color="#00FF00")
- progressbar_1.pack(pady=2, padx=1)
- progressbar_1.configure(mode="indeterminnate")
- progressbar_1.start()
- #pacemakers GUI, insert parameters, function and ROI get
- frame = customtkinter.CTkFrame(master=root, fg_color='transparent')
- frame.place(x=645, y=280)
- label= customtkinter.CTkLabel(master=frame, width=600, text="Autorhythmic Cells Evoked Activity Analysis", font=("Arial Rounded MT Bold", 25), text_color='white')
- label.pack(pady=2, padx=1)
- savepacepath.pack(pady=2, padx=1)
- label5= customtkinter.CTkLabel(master=frame, width=600, text="bin size (ms)", font=("Arial Rounded MT Bold", 15), text_color='white')
- label5.pack(pady=2, padx=1)
- VALUE5= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- VALUE5.pack(pady=2, padx=1)
- binslider= customtkinter.CTkSlider(master=frame, width=600, from_=1, to=50, number_of_steps=49, command=binvalue)
- binslider.pack(pady=2, padx=1)
- label6= customtkinter.CTkLabel(master=frame, width=600, text="pre-stimulus time window (ms)", font=("Arial Rounded MT Bold", 15), text_color='white')
- label6.pack(pady=2, padx=1)
- VALUE6= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- VALUE6.pack(pady=2, padx=1)
- preslider= customtkinter.CTkSlider(master=frame, width=600, from_=50, to=1200, number_of_steps=23, command=prevalue)
- preslider.pack(pady=2, padx=1)
- label7= customtkinter.CTkLabel(master=frame, width=600, text="post-stimulus time window (ms)", font=("Arial Rounded MT Bold", 15), text_color='white')
- label7.pack(pady=2, padx=1)
- VALUE7= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- VALUE7.pack(pady=2, padx=1)
- postslider= customtkinter.CTkSlider(master=frame, width=600, from_=50, to=1200, number_of_steps=23, command=postvalue)
- postslider.pack(pady=2, padx=1)
- label8= customtkinter.CTkLabel(master=frame, width=600, text="num permutation for significance test", font=("Arial Rounded MT Bold", 15), text_color='white')
- label8.pack(pady=2, padx=1)
- VALUE8= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- VALUE8.pack(pady=2, padx=1)
- permslider= customtkinter.CTkSlider(master=frame, width=600, from_=1000, to=10000, number_of_steps=9, command=permvalue)
- permslider.pack(pady=2, padx=1)
- label9= customtkinter.CTkLabel(master=frame, width=600, height=47, text="", font=("Arial Rounded MT Bold", 15), text_color='white')
- label9.pack(pady=2, padx=1)
- frameC = customtkinter.CTkFrame(master=root, width=600, fg_color="transparent")
- frameC.place(x=645, y=680)
- ################################ STRING 1000!!!!! ################################
- checkbox01Hz = customtkinter.CTkCheckBox(master=frameC, text="0.1 Hz", command=set_freqvalue01,
- onvalue="on", offvalue="off", font=("Arial Rounded MT Bold", 15), checkbox_width=10, checkbox_height=10, corner_radius=30)
- checkbox01Hz.grid(row=0, column=0)
- checkbox6Hz = customtkinter.CTkCheckBox(master=frameC, text="6 Hz", command=set_freqvalue6,
- onvalue="on", offvalue="off", font=("Arial Rounded MT Bold", 15), checkbox_width=10, checkbox_height=10, corner_radius=30)
- checkbox6Hz.grid(row=0, column=1)
- checkbox10Hz = customtkinter.CTkCheckBox(master=frameC, text="10 Hz", command=set_freqvalue10,
- onvalue="on", offvalue="off", font=("Arial Rounded MT Bold", 15), checkbox_width=10, checkbox_height=10, corner_radius=30)
- checkbox10Hz.grid(row=0, column=2)
- checkbox20Hz = customtkinter.CTkCheckBox(master=frameC, text="20 Hz", command=set_freqvalue20,
- onvalue="on", offvalue="off", font=("Arial Rounded MT Bold", 15), checkbox_width=10, checkbox_height=10, corner_radius=30)
- checkbox20Hz.grid(row=0, column=3)
- checkbox50Hz = customtkinter.CTkCheckBox(master=frameC, text="50 Hz", command=set_freqvalue50,
- onvalue="on", offvalue="off", font=("Arial Rounded MT Bold", 15), checkbox_width=10, checkbox_height=10, corner_radius=30)
- checkbox50Hz.grid(row=0, column=4)
- checkbox100Hz = customtkinter.CTkCheckBox(master=frameC, text="100 Hz", command=set_freqvalue100,
- onvalue="on", offvalue="off", font=("Arial Rounded MT Bold", 15), checkbox_width=10, checkbox_height=10, corner_radius=30)
- checkbox100Hz.grid(row=0, column=5)
- checkbox1pulse = customtkinter.CTkCheckBox(master=frameC, text="sing. pulse", command=set_stimvalue1,
- onvalue="on", offvalue="off", font=("Arial Rounded MT Bold", 15), checkbox_width=10, checkbox_height=10, corner_radius=30)
- checkbox1pulse.grid(row=1, column=0)
- checkbox5pulse = customtkinter.CTkCheckBox(master=frameC, text="5 pulses", command=set_stimvalue5,
- onvalue="on", offvalue="off", font=("Arial Rounded MT Bold", 15), checkbox_width=10, checkbox_height=10, corner_radius=30)
- checkbox5pulse.grid(row=1, column=1)
- checkbox10pulse = customtkinter.CTkCheckBox(master=frameC, text="10 pulses", command=set_stimvalue10,
- onvalue="on", offvalue="off", font=("Arial Rounded MT Bold", 15), checkbox_width=10, checkbox_height=10, corner_radius=30)
- checkbox10pulse.grid(row=1, column=2)
- frame = customtkinter.CTkFrame(master=root, width=600, fg_color="transparent")
- frame.place(x=645, y=740)
- button = customtkinter.CTkButton(master=frame, width=600, height=70, text="START EVOKED ACTIVITY ANALYSIS",font=("Arial Rounded MT Bold", 25), command=pacemakers,corner_radius=30)
- button.pack(pady=2, padx=1)
- frame = customtkinter.CTkFrame(master=root, fg_color='transparent')
- frame.place(x=10, y=820)
- label= customtkinter.CTkLabel(master=frame, width=1870, text="Selected Channels", font=("Arial Rounded MT Bold", 25), corner_radius=15, text_color='white')
- label.pack(pady=2, padx=1)
- ids= customtkinter.CTkEntry(master=frame, width=1870, height=50, placeholder_text=ROIstr, font=("Arial Rounded MT Bold", 15), corner_radius=15)
- ids.pack(pady=2, padx=1)
- img=PhotoImage(file="NeuroPulseLogo.png")
- frame = customtkinter.CTkFrame(master=root, width=120, height=120, fg_color="transparent")
- frame.place(x=1300, y=40)
- label= customtkinter.CTkLabel(master=frame, width=120, height=120,text="", image=img)
- label.pack(pady=2, padx=1)
- plt.imshow(map_mea, cmap='brg', interpolation='nearest')
- plt.savefig('img.png', bbox_inches='tight', transparent=True, dpi=60)
- img_map=PhotoImage(file="img.png")
- frame = customtkinter.CTkFrame(master=root, width=120, height=120, fg_color="transparent")
- frame.place(x=1040, y=0)
- label= customtkinter.CTkLabel(master=frame, width=120, height=120, text="", image=img_map)
- label.pack(pady=2, padx=1)
- ##spont activity
- frame = customtkinter.CTkFrame(master=root, fg_color='transparent')
- frame.place(x=1280, y=280)
- label= customtkinter.CTkLabel(master=frame, width=600, text="Autorhythmic Cells Spont. Activity Analysis", font=("Arial Rounded MT Bold", 25), text_color='white')
- label.pack(pady=2, padx=1)
- savepacepath.pack(pady=2, padx=1)
- label52= customtkinter.CTkLabel(master=frame, width=600, text="bin size (ms)", font=("Arial Rounded MT Bold", 15), text_color='white')
- label52.pack(pady=2, padx=1)
- VALUE52= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- VALUE52.pack(pady=2, padx=1)
- binslider2= customtkinter.CTkSlider(master=frame, width=600, from_=1, to=50, number_of_steps=49, command=binvalue2)
- binslider2.pack(pady=2, padx=1)
- label53= customtkinter.CTkLabel(master=frame, width=600, text="ISI bin limit (ms)", font=("Arial Rounded MT Bold", 15), text_color='white')
- label53.pack(pady=2, padx=1)
- VALUE53= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- VALUE53.pack(pady=2, padx=1)
- total_time_slider= customtkinter.CTkSlider(master=frame, width=600, from_=100, to=3000, number_of_steps=29, command=bin_max_value)
- total_time_slider.pack(pady=2, padx=1)
- label54= customtkinter.CTkLabel(master=frame, width=600, text="total time (s)", font=("Arial Rounded MT Bold", 15), text_color='white')
- label54.pack(pady=2, padx=1)
- VALUE54= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- VALUE54.pack(pady=2, padx=1)
- total_time_slider2= customtkinter.CTkSlider(master=frame, width=600, from_=0, to=1000, number_of_steps=100, command=total_time)
- total_time_slider2.pack(pady=2, padx=1)
- # label_tw= customtkinter.CTkLabel(master=frame, width=600, text="total time window (ms)", font=("Arial Rounded MT Bold", 15), text_color='white')
- # label_tw.pack(pady=2, padx=1)
- # VALUE_tw= customtkinter.CTkEntry(master=frame, width=600, placeholder_text="", font=("Arial Rounded MT Bold", 15),corner_radius=15)
- # VALUE_tw.pack(pady=2, padx=1)
- # twslider= customtkinter.CTkSlider(master=frame, width=600, from_=500, to=5000, number_of_steps=9, command=twvalue)
- # twslider.pack(pady=2, padx=1)
- frame = customtkinter.CTkFrame(master=root, width=600, fg_color="transparent")
- frame.place(x=1280, y=740)
- button = customtkinter.CTkButton(master=frame, width=600, height=70, text="START SPONT. ACTIVITY ANALYSIS",font=("Arial Rounded MT Bold", 25), command=spont_activity,corner_radius=30)
- button.pack(pady=2, padx=1)
- frame0 = customtkinter.CTkFrame(master=root, fg_color="transparent")
- folderico = PhotoImage(file="folderico.png")
- frame0.place(x=935, y=30)
- buttonBrfile = customtkinter.CTkButton(master=frame0, width=25, text="", command=browse_BRW ,corner_radius=15, image=folderico, fg_color="transparent")
- buttonBrfile.pack(pady=0, padx=0)
- frame0.place(x=935, y=30)
- buttonBrfile = customtkinter.CTkButton(master=frame0, width=25, text="", command=browse_BXR ,corner_radius=15, image=folderico, fg_color="transparent")
- buttonBrfile.pack(pady=0, padx=0)
- frame0 = customtkinter.CTkFrame(master=root, fg_color="transparent")
- frame0.place(x=935, y=128)
- buttonBrfile = customtkinter.CTkButton(master=frame0, width=25, text="", command=browse_ROI ,corner_radius=15, image=folderico, fg_color="transparent")
- buttonBrfile.pack(pady=0, padx=0)
- frame0 = customtkinter.CTkFrame(master=root, fg_color="transparent")
- frame0.place(x=935, y=95)
- buttonBrfile = customtkinter.CTkButton(master=frame0, width=25, text="", command=browse_save ,corner_radius=15, image=folderico, fg_color="transparent")
- buttonBrfile.pack(pady=0, padx=0)
- root.protocol("WM_DELETE_WINDOW", on_closing)
- root.mainloop()
HD-MEA NEUROPulse.py, no license · at the source
Overview
- Department of Brain and Behavioral Sciences, University of Pavia, Pavia, Italy
- Department of Human Genetics, Emory University School of Medicine, Atlanta, GA, USA
- Emory Brain Organoid Hub, Atlanta, GA, USA
- Brain AG, Pfäffikon, Switzerland
- Department of Pharmacology (DIFAR), University of Genoa, Genoa, Italy
- Interuniversity Center for the Promotion of the 3Rs Principles in Teaching and Research (Centro 3R), Italy
- Digital Neuroscience Center, IRCCS Mondino Foundation, Pavia, Italy
Abstract
Animal and human stem cell–derived three-dimensional models to study physio-pathological brain functioning are becoming a gold standard for in vitro electrophysiology, as they enable the recapitulation of complex network properties by accounting for spatial architectural features that better reflect in vivo conditions than simpler 2D models. Standard planar multielectrode arrays (MEAs), typically providing tens of recording electrodes, are commonly used to record activity from 2D neuronal cultures. However, when adapted for use with 3D models, planar 2D MEAs showed limited effectiveness. The main issues are limited specimen adhesion to the chip, a low number of sensing elements, inability to retrieve signals from within the tissue, and reduced perfusion and vitality of the tissue in contact with sensors. To overcome these limitations, a new generation of microchip-based 3D high-density MEAs (3D HD-MEA) has been developed and validated in recent years. This technological advancement has improved the sensing capabilities and the vitality of 3D models, providing a tool tailored to maximize their potential. Here, we present an optimized protocol for neural network activity recordings in 3D models (including acute slices, brain spheroids, and organoids) from various brain regions using 3D HD-MEAs. First, we summarize the critical steps for 1) obtaining viable acute slices from the mouse cerebellum, cortico-hippocampal circuit, and prefrontal cortex, 2) establishing efficient coupling of the slices with the chip, and 3) performing recordings and analyses. We then describe the main procedures required to obtain human and animal brain spheroids and neural organoids, as well as standardized routines to perform effective recordings and analyses. For each section, we highlight the crucial steps, identify tips for specific applications, and propose troubleshooting procedures. For example, the same type of preparation (e.g., acute slices) requires different adjustments when working with different brain areas. The specific information provided here is intended to assist researchers in their daily efforts to obtain efficient and reproducible functional recordings from 3D models by using the cutting-edge technique of 3D HD-MEA.
Key features
• Comprehensive all-in-one guide covering the complete workflow for acquiring electrophysiological data from brain slices, neural region-specific organoids, and brain spheroids.
• Intuitive, step-by-step protocol for brain slice preparation, enriched with practical tips and expert recommendations to ensure high-quality tissue viability.
• Detailed instructions for optimal use of 3D HD-MEA technology, including proper handling of the sample holder for recordings from brain slices, neural organoids, and spheroids.
• In-depth guidance on BrainWave6 software, providing clear procedures for data acquisition, signal detection, and advanced electrophysiological analysis across all sample types.
Reproduced under the paper's license (CC BY-NC), from the paper cited above.
Repository
Its files are read in the Code ↔ Paper reader above.
Zenodo 13908319
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
- 27 September 2026: the link answers (HTTP 200)
1 file
- HD-MEA NEUROPulse.py, Python, 1,693 lines
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 1 script, each with its path and the digest of its content;
- no match between paragraphs and code yet;
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 17 authors, 7 keywords, 38 references.
Cite
This paper
Pali, E., Pellavio, G., Conforti, M., Sarkissian, A. A., Aliya, B., Sciacca, G., Wariyar, S. S., Mainardi, F., Tedesco, M., Verduci, I., Cadiao, G., Cervetto, C., Andersen, J., Birey, F., Maccione, A., D’Angelo, E., & Mapelli, L. (2026). Measuring Electrophysiological Activity in Acute Brain Slices, Spheroids, and Organoids Using 3D High-Density Multielectrode Arrays. Bio-protocol, 16(11), e5708. https://
BibTeX
@article{pali2026measuri
author = {Pali, Eleonora and Pellavio, Giorgia and Conforti, Maria and Sarkissian, Arvin A. and Aliya, Berna and Sciacca, Giacomo and Wariyar, Supriya S. and Mainardi, Francesco and Tedesco, Mariateresa and Verduci, Ivan and Cadiao, Gendenver and Cervetto, Chiara and Andersen, Jimena and Birey, Fikri and Maccione, Alessandro and D’Angelo, Egidio and Mapelli, Lisa},
title = {{Measuring Electrophysiological Activity in Acute Brain Slices, Spheroids, and Organoids Using 3D High-Density Multielectrode Arrays}},
journal = {Bio-protocol},
year = {2026},
month = jun,
volume = {16},
number = {11},
pages = {e5708},
publisher = {Bio-protocol, LLC},
issn = {2331-8325},
doi = {10.21769/
url = {https://
pmid = {42305135},
pmcid = {PMC13266469}
}
RIS
TY - JOUR
AU - Pali, Eleonora
AU - Pellavio, Giorgia
AU - Conforti, Maria
AU - Sarkissian, Arvin A.
AU - Aliya, Berna
AU - Sciacca, Giacomo
AU - Wariyar, Supriya S.
AU - Mainardi, Francesco
AU - Tedesco, Mariateresa
AU - Verduci, Ivan
AU - Cadiao, Gendenver
AU - Cervetto, Chiara
AU - Andersen, Jimena
AU - Birey, Fikri
AU - Maccione, Alessandro
AU - D’Angelo, Egidio
AU - Mapelli, Lisa
TI - Measuring Electrophysiological Activity in Acute Brain Slices, Spheroids, and Organoids Using 3D High-Density Multielectrode Arrays
T2 - Bio-protocol
J2 - Bio Protoc
PY - 2026
DA - 2026/
VL - 16
IS - 11
SP - e5708
SN - 2331-8325
PB - Bio-protocol, LLC
DO - 10.21769/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.21769/
"type": "article-journal",
"title": "Measuring Electrophysiological Activity in Acute Brain Slices, Spheroids, and Organoids Using 3D High-Density Multielectrode Arrays",
"container-title": "Bio-protocol",
"author": [
{
"family": "Pali",
"given": "Eleonora"
},
{
"family": "Pellavio",
"given": "Giorgia"
},
{
"family": "Conforti",
"given": "Maria"
},
{
"family": "Sarkissian",
"given": "Arvin A."
},
{
"family": "Aliya",
"given": "Berna"
},
{
"family": "Sciacca",
"given": "Giacomo"
},
{
"family": "Wariyar",
"given": "Supriya S."
},
{
"family": "Mainardi",
"given": "Francesco"
},
{
"family": "Tedesco",
"given": "Mariateresa"
},
{
"family": "Verduci",
"given": "Ivan"
},
{
"family": "Cadiao",
"given": "Gendenver"
},
{
"family": "Cervetto",
"given": "Chiara"
},
{
"family": "Andersen",
"given": "Jimena"
},
{
"family": "Birey",
"given": "Fikri"
},
{
"family": "Maccione",
"given": "Alessandro"
},
{
"family": "D’Angelo",
"given": "Egidio"
},
{
"family": "Mapelli",
"given": "Lisa"
}
],
"container-title-short":
"volume": "16",
"issue": "11",
"page": "e5708",
"DOI": "10.21769/
"PMID": "42305135",
"PMCID": "PMC13266469",
"ISSN": "2331-8325",
"publisher": "Bio-protocol, LLC",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
5
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1002/adhm.202504889 [code]
- Mapping the Cerebral Organoid Landscape: A Systematic Review of Preclinical 3D Models in Neuroscience.Journal: Advanced healthcare materialsIn common: pandas, SciPy, Matplotlib, 1 other tool, 6 references
- [2] doi:10.1371/journal.pbio.3003757 [code]
- Cell type-agnostic transcriptomic signatures enable uniform comparisons of neural maturation.Journal: PLoS biologyIn common: h5py, pandas, SciPy, 2 other tools, 3 references
- [3] doi:10.1038/s41467-026-71458-0 [code]
- Early differential impact of MeCP2 mutations on functional networks in Rett syndrome patient-derived human cortical organoids.Journal: Nature communicationsIn common: h5py, SciPy, Matplotlib, 1 other tool, 2 references
- [4] doi:10.7554/elife.110588 [code]
- Opening the black box toward a modular approach to spike sorting.Journal: eLifeIn common: h5py, pandas, SciPy, 2 other tools, extracellular electrophysiology (units, LFP), methods / tools
- [5] doi:10.1093/bioinformatics/btag570 [code]
- CASCADE: criticality avalanche spike cross-platform analysis detection engine, a multi-manufacturer MEA bash analysis pipeline.Journal: Bioinformatics (Oxford, England)In common: h5py, pandas, SciPy, 2 other tools, extracellular electrophysiology (units, LFP), methods / tools
- [6] doi:10.1002/advs.202522762 [code]
- Enhancing Maturation of Human Neuromuscular Organoids via Electrical Stimulation.Journal: Advanced science (Weinheim, Baden-Wurttemberg, Germany)In common: h5py, pandas, SciPy, 2 other tools, 1 reference
- [7] doi:10.1038/s41467-026-74320-5 [code]
- Spatial architecture of autism pathogenesis reveals mosaic structural disarray during early development.Journal: Nature communicationsIn common: h5py, pandas, SciPy, 2 other tools, 1 reference
- [8] doi:10.1038/s41531-026-01531-4 [code]
- Inconsistent subthalamic local field potential beta activity amid in- and antiphasic neuronal bursts.Journal: NPJ Parkinson's diseaseIn common: h5py, pandas, SciPy, 2 other tools, extracellular electrophysiology (units, LFP)
- [9] doi:10.1016/j.patter.2026.101590 [code]
- Density-based longitudinal neuron tracking in high-density electrophysiological recordings.Journal: Patterns (New York, N.Y.)In common: h5py, pandas, SciPy, 2 other tools, extracellular electrophysiology (units, LFP)
- [10] doi:10.1038/s41592-026-03076-z [code]
- Neuropixels Opto: combining high-resolution electrophysiology and optogenetics.Journal: Nature methodsIn common: h5py, pandas, SciPy, 2 other tools, extracellular electrophysiology (units, LFP)
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 1 repository of the authors' code, each at its verified commit and with its license, 1 script, and 0 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:da565deb6aad1403…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[.
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
