Sequential visual stimuli increase high frequency power in the visual cortex.
The 1 match
- [1] § Materials and methods › Multi-unit and Single-unit extraction and analysis ↔ ecephys_spike_sorting/modules/quality_metrics/metrics.py, lines 20–157 · score 0.80 · quality metrics, isi violation, Isolation distance, firing rate, pre, clustering
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 · 997 lines · 35 KB · BSD-2-Clause · 1 match
- import numpy as np
- import pandas as pd
- from collections import OrderedDict
- import warnings
- from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
- from sklearn.neighbors import NearestNeighbors
- from sklearn.metrics import silhouette_score
- from scipy.spatial.distance import cdist
- from scipy.stats import chi2
- from scipy.ndimage.filters import gaussian_filter1d
- from ...common.epoch import Epoch
- from ...common.utils import printProgressBar, get_spike_depths
- def calculate_metrics(spike_times,
- spike_clusters,
- spike_templates,
- amplitudes,
- channel_map,
- pc_features,
- pc_feature_ind,
- params,
- epochs = None):
- """ Calculate metrics for all units on one probe
- Inputs:
- ------
- spike_times : numpy.ndarray (num_spikes x 0)
- Spike times in seconds (same timebase as epochs)
- spike_clusters : numpy.ndarray (num_spikes x 0)
- Cluster IDs for each spike
- spike_templates : numpy.ndarray (num_spikes x 0)
- Original template IDs for each spike time
- amplitudes : numpy.ndarray (num_spikes x 0)
- Amplitude value for each spike time
- channel_map : numpy.ndarray (num_units x 0)
- Original data channel for pc_feature_ind array
- pc_features : numpy.ndarray (num_spikes x num_pcs x num_channels)
- Pre-computed PCs for blocks of channels around each spike
- If 'None', PC-based metrics will not be computed
- pc_feature_ind : numpy.ndarray (num_units x num_channels)
- Channel indices of PCs for each unit
- epochs : list of Epoch objects
- contains information on Epoch start and stop times
- params : dict of parameters
- 'isi_threshold' : minimum time for isi violations
- Outputs:
- --------
- metrics : pandas.DataFrame
- one column for each metric
- one row per unit per epoch
- """
- metrics = pd.DataFrame()
- np.random.seed(9999)
- if epochs is None:
- epochs = [Epoch('complete_session', 0, np.inf)]
- total_units = len(np.unique(spike_clusters))
- total_epochs = len(epochs)
- for epoch in epochs:
- in_epoch = (spike_times > epoch.start_time) * (spike_times < epoch.end_time)
- print("Calculating isi violations")
- isi_viol = calculate_isi_violations(spike_times[in_epoch], spike_clusters[in_epoch], total_units, params['isi_threshold'], params['min_isi'])
- print("Calculating corrected isi violations")
- isi_viol_corrected = calculate_isi_violations_corrected(spike_times[in_epoch], spike_clusters[in_epoch], total_units, params['isi_threshold'], params['min_isi'])
- print("Calculating presence ratio")
- presence_ratio = calculate_presence_ratio(spike_times[in_epoch], spike_clusters[in_epoch], total_units)
- print("Calculating firing rate")
- firing_rate = calculate_firing_rate(spike_times[in_epoch], spike_clusters[in_epoch], total_units)
- print("Calculating amplitude cutoff")
- amplitude_cutoff = calculate_amplitude_cutoff(spike_clusters[in_epoch], amplitudes[in_epoch], total_units)
- if pc_features is not None:
- print("Calculating PC-based metrics")
- isolation_distance, l_ratio, d_prime, nn_hit_rate, nn_miss_rate = calculate_pc_metrics(spike_clusters[in_epoch],
- spike_templates[in_epoch],
- total_units,
- pc_features[in_epoch,:,:],
- pc_feature_ind,
- params['num_channels_to_compare'],
- params['max_spikes_for_unit'],
- params['max_spikes_for_nn'],
- params['n_neighbors'])
- print("Calculating silhouette score")
- the_silhouette_score = calculate_silhouette_score(spike_clusters[in_epoch],
- spike_templates[in_epoch],
- total_units,
- pc_features[in_epoch,:,:],
- pc_feature_ind,
- params['n_silhouette'])
- print("Calculating drift metrics")
- max_drift, cumulative_drift = calculate_drift_metrics(spike_times[in_epoch],
- spike_clusters[in_epoch],
- spike_templates[in_epoch],
- total_units,
- pc_features[in_epoch,:,:],
- pc_feature_ind,
- params['drift_metrics_interval_s'],
- params['drift_metrics_min_spikes_per_interval'])
- cluster_ids = np.unique(spike_clusters)
- epoch_name = [epoch.name] * len(cluster_ids)
- if pc_features is not None:
- metrics = pd.concat((metrics, pd.DataFrame(data= OrderedDict((('cluster_id', cluster_ids),
- ('firing_rate' , firing_rate),
- ('presence_ratio' , presence_ratio),
- ('isi_viol' , isi_viol),
- ('isi_viol_corrected' , isi_viol_corrected),
- ('amplitude_cutoff' , amplitude_cutoff),
- ('isolation_distance' , isolation_distance),
- ('l_ratio' , l_ratio),
- ('d_prime' , d_prime),
- ('nn_hit_rate' , nn_hit_rate),
- ('nn_miss_rate' , nn_miss_rate),
- ('silhouette_score', the_silhouette_score),
- ('max_drift', max_drift),
- ('cumulative_drift', cumulative_drift),
- ('epoch_name' , epoch_name),
- )))))
- else:
- metrics = pd.concat((metrics, pd.DataFrame(data= OrderedDict((('cluster_id', cluster_ids),
- ('firing_rate' , firing_rate),
- ('presence_ratio' , presence_ratio),
- ('isi_viol' , isi_viol),
- ('isi_viol_corrected' , isi_viol_corrected),
- ('amplitude_cutoff' , amplitude_cutoff),
- ('epoch_name' , epoch_name),
- )))))
- return metrics
- # ===============================================================
- # HELPER FUNCTIONS TO LOOP THROUGH CLUSTERS:
- # ===============================================================
- def calculate_isi_violations(spike_times, spike_clusters, total_units, isi_threshold, min_isi):
- cluster_ids = np.unique(spike_clusters)
- viol_rates = np.zeros((total_units,))
- for idx, cluster_id in enumerate(cluster_ids):
- printProgressBar(idx + 1, total_units)
- for_this_cluster = (spike_clusters == cluster_id)
- viol_rates[idx], num_violations = isi_violations(spike_times[for_this_cluster],
- min_time = np.min(spike_times),
- max_time = np.max(spike_times),
- isi_threshold=isi_threshold,
- min_isi = min_isi)
- return viol_rates
- def calculate_isi_violations_corrected(spike_times, spike_clusters, total_units, isi_threshold, min_isi):
- cluster_ids = np.unique(spike_clusters)
- viol_rates = np.zeros((total_units,))
- for idx, cluster_id in enumerate(cluster_ids):
- printProgressBar(idx + 1, total_units)
- for_this_cluster = (spike_clusters == cluster_id)
- viol_rates[idx], num_violations = isi_violations_corrected(spike_times[for_this_cluster],
- min_time = np.min(spike_times),
- max_time = np.max(spike_times),
- isi_threshold=isi_threshold,
- min_isi = min_isi)
- return viol_rates
- def calculate_presence_ratio(spike_times, spike_clusters, total_units):
- cluster_ids = np.unique(spike_clusters)
- ratios = np.zeros((total_units,))
- for idx, cluster_id in enumerate(cluster_ids):
- printProgressBar(idx + 1, total_units)
- for_this_cluster = (spike_clusters == cluster_id)
- ratios[idx] = presence_ratio(spike_times[for_this_cluster],
- min_time = np.min(spike_times),
- max_time = np.max(spike_times))
- return ratios
- def calculate_firing_rate(spike_times, spike_clusters, total_units):
- cluster_ids = np.unique(spike_clusters)
- firing_rates = np.zeros((total_units,))
- min_time = np.min(spike_times)
- max_time = np.max(spike_times)
- for idx, cluster_id in enumerate(cluster_ids):
- printProgressBar(idx + 1, total_units)
- for_this_cluster = (spike_clusters == cluster_id)
- firing_rates[idx] = firing_rate(spike_times[for_this_cluster],
- min_time = np.min(spike_times),
- max_time = np.max(spike_times))
- return firing_rates
- def calculate_amplitude_cutoff(spike_clusters, amplitudes, total_units):
- cluster_ids = np.unique(spike_clusters)
- amplitude_cutoffs = np.zeros((total_units,))
- for idx, cluster_id in enumerate(cluster_ids):
- printProgressBar(idx + 1, total_units)
- for_this_cluster = (spike_clusters == cluster_id)
- amplitude_cutoffs[idx] = amplitude_cutoff(amplitudes[for_this_cluster])
- return amplitude_cutoffs
- def calculate_pc_metrics_one_cluster(cluster_peak_channels, idx, cluster_id,cluster_ids,
- half_spread, pc_features, pc_feature_ind,
- spike_clusters, spike_templates,
- max_spikes_for_cluster, max_spikes_for_nn, n_neighbors):
- peak_channel = cluster_peak_channels[idx]
- num_spikes_in_cluster = np.sum(spike_clusters == cluster_id)
- half_spread_down = peak_channel \
- if peak_channel < half_spread \
- else half_spread
- half_spread_up = np.max(pc_feature_ind) - peak_channel \
- if peak_channel + half_spread > np.max(pc_feature_ind) \
- else half_spread
- channels_to_use = np.arange(peak_channel - half_spread_down, peak_channel + half_spread_up + 1)
- units_in_range = cluster_ids[np.isin(cluster_peak_channels, channels_to_use)]
- spike_counts = np.zeros(units_in_range.shape)
- for idx2, cluster_id2 in enumerate(units_in_range):
- spike_counts[idx2] = np.sum(spike_clusters == cluster_id2)
- if num_spikes_in_cluster > max_spikes_for_cluster:
- relative_counts = spike_counts / num_spikes_in_cluster * max_spikes_for_cluster
- else:
- relative_counts = spike_counts
- all_pcs = np.zeros((0, pc_features.shape[1], channels_to_use.size))
- all_labels = np.zeros((0,))
- for idx2, cluster_id2 in enumerate(units_in_range):
- subsample = int(relative_counts[idx2])
- pcs = get_unit_pcs(cluster_id2, spike_clusters, spike_templates,
- pc_feature_ind, pc_features, channels_to_use,
- subsample)
- if pcs is not None and len(pcs.shape) == 3:
- labels = np.ones((pcs.shape[0],)) * cluster_id2
- all_pcs = np.concatenate((all_pcs, pcs),0)
- all_labels = np.concatenate((all_labels, labels),0)
- all_pcs = np.reshape(all_pcs, (all_pcs.shape[0], pc_features.shape[1]*channels_to_use.size))
- if ((all_pcs.shape[0] > 10)
- and not (all_labels == cluster_id).all() # Not all labels are this cluster
- and (sum(all_labels == cluster_id) > 20) # No fewer than 20 spikes in this cluster
- and (len(channels_to_use) > 0)):
- isolation_distance, l_ratio = mahalanobis_metrics(all_pcs, all_labels, cluster_id)
- d_prime = lda_metrics(all_pcs, all_labels, cluster_id)
- nn_hit_rate, nn_miss_rate = nearest_neighbors_metrics(all_pcs, all_labels,
- cluster_id,
- max_spikes_for_nn,
- n_neighbors)
- else: # Too few spikes or cluster doesnt exist
- isolation_distance = np.nan
- d_prime = np.nan
- nn_miss_rate = np.nan
- nn_hit_rate = np.nan
- l_ratio = np.nan
- return isolation_distance, d_prime, nn_miss_rate, nn_hit_rate, l_ratio
- def calculate_pc_metrics(spike_clusters,
- spike_templates,
- total_units,
- pc_features,
- pc_feature_ind,
- num_channels_to_compare,
- max_spikes_for_cluster,
- max_spikes_for_nn,
- n_neighbors,
- do_parallel=True):
- """
- :param spike_clusters:
- :param total_units:
- :param pc_features:
- :param pc_feature_ind:
- :param num_channels_to_compare:
- :param max_spikes_for_cluster:
- :param max_spikes_for_nn:
- :param n_neighbors:
- :return:
- """
- assert (num_channels_to_compare % 2 == 1)
- half_spread = int((num_channels_to_compare - 1) / 2)
- cluster_ids = np.unique(spike_clusters)
- template_ids = np.unique(spike_templates)
- template_peak_channels = np.zeros((len(template_ids),), dtype='uint16')
- cluster_peak_channels = np.zeros((len(cluster_ids),), dtype='uint16')
- for idx, template_id in enumerate(template_ids):
- for_template = np.squeeze(spike_templates == template_id)
- pc_max = np.argmax(np.mean(pc_features[for_template, 0, :], 0))
- template_peak_channels[idx] = pc_feature_ind[template_id, pc_max]
- for idx, cluster_id in enumerate(cluster_ids):
- for_unit = np.squeeze(spike_clusters == cluster_id)
- templates_for_unit = np.unique(spike_templates[for_unit])
- template_positions = np.where(np.isin(template_ids, templates_for_unit))[0]
- cluster_peak_channels[idx] = np.median(template_peak_channels[template_positions])
- # Loop over clusters:
- if do_parallel:
- from joblib import Parallel, delayed
- meas = Parallel(n_jobs=-1, verbose=3)( # -1 means use all cores
- delayed(calculate_pc_metrics_one_cluster) # Function
- (cluster_peak_channels, idx, cluster_id, cluster_ids,
- half_spread, pc_features, pc_feature_ind,
- spike_clusters, spike_templates,
- max_spikes_for_cluster, max_spikes_for_nn, n_neighbors
- )
- for idx, cluster_id in enumerate(cluster_ids)) # Loop
- else:
- from tqdm import tqdm
- meas = []
- for idx, cluster_id in tqdm(enumerate(cluster_ids), total=cluster_ids.max(), desc='PC metrics'): # Loop
- meas.append(calculate_pc_metrics_one_cluster( # Function
- cluster_peak_channels, idx, cluster_id, cluster_ids,
- half_spread, pc_features, pc_feature_ind,
- spike_clusters, spike_templates,
- max_spikes_for_cluster, max_spikes_for_nn, n_neighbors))
- # Unpack:
- isolation_distances = []
- l_ratios = []
- d_primes = []
- nn_hit_rates = []
- nn_miss_rates = []
- for mea in meas:
- isolation_distance, d_prime, nn_miss_rate, nn_hit_rate, l_ratio = mea
- isolation_distances.append(isolation_distance)
- d_primes.append(d_prime)
- nn_miss_rates.append(nn_miss_rate)
- nn_hit_rates.append(nn_hit_rate)
- l_ratios.append(l_ratio)
- return (np.array(isolation_distances), np.array(l_ratios), np.array(d_primes),
- np.array(nn_hit_rates), np.array(nn_miss_rates))
- def calculate_silhouette_score(spike_clusters,
- spike_templates,
- total_units,
- pc_features,
- pc_feature_ind,
- total_spikes,
- do_parallel=True):
- def score_inner_loop(i, cluster_ids):
- """
- Helper to loop over cluster_ids in one dimension. We dont want to loop over both dimensions in parallel-
- that will create too much worker overhead
- Args:
- i: index of first dimension
- cluster_ids: iterable of cluster ids
- Returns: scores for dimension j
- """
- scores_1d = []
- for j in cluster_ids:
- if j > i:
- inds = np.in1d(cluster_labels, np.array([i, j]))
- X = all_pcs[inds, :]
- labels = cluster_labels[inds]
- # len(np.unique(labels))=1 Can happen if total_spikes is low:
- if (len(labels) > 2) and (len(np.unique(labels)) > 1):
- scores_1d.append(silhouette_score(X, labels))
- else:
- scores_1d.append(np.nan)
- else:
- scores_1d.append(np.nan)
- return scores_1d
- cluster_ids = np.unique(spike_clusters)
- random_spike_inds = np.random.permutation(spike_clusters.size)
- random_spike_inds = random_spike_inds[:total_spikes]
- num_pc_features = pc_features.shape[1]
- num_channels = np.max(pc_feature_ind) + 1
- all_pcs = np.zeros((total_spikes, num_channels * num_pc_features))
- for idx, i in enumerate(random_spike_inds):
- unit_id = spike_templates[i]
- channels = pc_feature_ind[unit_id,:]
- for j in range(0,num_pc_features):
- all_pcs[idx, channels + num_channels * j] = pc_features[i,j,:]
- cluster_labels = np.squeeze(spike_clusters[random_spike_inds])
- SS = np.empty((total_units, total_units))
- SS[:] = np.nan
- # Build lists
- if do_parallel:
- from joblib import Parallel, delayed
- scores = Parallel(n_jobs=-1, verbose=2)(delayed(score_inner_loop)(i, cluster_ids) for i in cluster_ids)
- else:
- scores = [score_inner_loop(i, cluster_ids) for i in cluster_ids]
- # Fill the 2d array
- for i, col_score in enumerate(scores):
- for j, one_score in enumerate(col_score):
- SS[i, j] = one_score
- with warnings.catch_warnings():
- warnings.simplefilter("ignore")
- a = np.nanmin(SS, 0)
- b = np.nanmin(SS, 1)
- return np.array([np.nanmin([a,b]) for a, b in zip(a,b)])
- def calculate_drift_metrics(spike_times,
- spike_clusters,
- spike_templates,
- total_units,
- pc_features,
- pc_feature_ind,
- interval_length,
- min_spikes_per_interval,
- do_parallel=True):
- def calc_one_cluster(cluster_id):
- """
- Helper to calculate drift for one cluster
- Args:
- cluster_id:
- Returns:
- max_drift, cumulative_drift
- """
- in_cluster = spike_clusters == cluster_id
- times_for_cluster = spike_times[in_cluster]
- depths_for_cluster = depths[in_cluster]
- median_depths = []
- for t1, t2 in zip(interval_starts, interval_ends):
- in_range = (times_for_cluster > t1) * (times_for_cluster < t2)
- if np.sum(in_range) >= min_spikes_per_interval:
- median_depths.append(np.median(depths_for_cluster[in_range]))
- else:
- median_depths.append(np.nan)
- median_depths = np.array(median_depths)
- max_drift = np.around(np.nanmax(median_depths) - np.nanmin(median_depths), 2)
- cumulative_drift = np.around(np.nansum(np.abs(np.diff(median_depths))), 2)
- return max_drift, cumulative_drift
- max_drifts = []
- cumulative_drifts = []
- depths = get_spike_depths(spike_templates, pc_features, pc_feature_ind)
- interval_starts = np.arange(np.min(spike_times), np.max(spike_times), interval_length)
- interval_ends = interval_starts + interval_length
- cluster_ids = np.unique(spike_clusters)
- if do_parallel:
- from joblib import Parallel, delayed
- meas = Parallel(n_jobs=-1, verbose=2)(delayed(calc_one_cluster)(cluster_id)
- for cluster_id in cluster_ids)
- else:
- meas = [calc_one_cluster(cluster_id) for cluster_id in cluster_ids]
- for max_drift, cumulative_drift in meas:
- max_drifts.append(max_drift)
- cumulative_drifts.append(cumulative_drift)
- return np.array(max_drifts), np.array(cumulative_drifts)
- # ==========================================================
- # IMPLEMENTATION OF ACTUAL METRICS:
- # ==========================================================
- def isi_violations(spike_train, min_time, max_time, isi_threshold, min_isi=0):
- """Calculate ISI violations for a spike train.
- Based on metric described in Hill et al. (2011) J Neurosci 31: 8699-8705
- modified by Dan Denman from cortex-lab/sortingQuality GitHub by Nick Steinmetz
- Inputs:
- -------
- spike_train : array of spike times
- min_time : minimum time for potential spikes
- max_time : maximum time for potential spikes
- isi_threshold : threshold for isi violation
- min_isi : threshold for duplicate spikes
- Outputs:
- --------
- fpRate : rate of contaminating spikes as a fraction of overall rate
- A perfect unit has a fpRate = 0
- A unit with some contamination has a fpRate < 0.5
- A unit with lots of contamination has a fpRate > 1.0
- num_violations : total number of violations
- """
- duplicate_spikes = np.where(np.diff(spike_train) <= min_isi)[0]
- spike_train = np.delete(spike_train, duplicate_spikes + 1)
- isis = np.diff(spike_train)
- num_spikes = len(spike_train)
- num_violations = sum(isis < isi_threshold)
- violation_time = 2*num_spikes*(isi_threshold - min_isi)
- total_rate = firing_rate(spike_train, min_time, max_time)
- violation_rate = num_violations/violation_time
- fpRate = violation_rate/total_rate
- return fpRate, num_violations
- def isi_violations_corrected(spike_train, min_time, max_time, isi_threshold, min_isi=0):
- """Calculate ISI violations for a spike train (with bias correction).
- This function was updated in September 2023 by Nick Steinmetz to correct
- two problems with the original implementation (text copied from
- https://github.com/cortex-lab/sortingQuality repo):
- 1) The approximation previously used, which was chosen to avoid getting
- imaginary results, wasn't accurate to the Hill et al paper on which this
- method was based, nor was it accurate to the correct solution to the problem.
- 2) Hill et al also did not have the correct solution to the problem. The Hill
- paper used an expression derived from an earlier work (Meunier et al
- 2003) which had assumed a special case: the "contamination" was itself
- only generated by a single neuron and therefore the contaminating spikes
- themselves had a refractory period. If instead the contaminating spikes
- are generated from a real Poisson process (as in the case of electrical
- noise or many nearby neurons generating the contamination), then the
- correct expression is different, as now calculated here. This expression
- is given in Llobet et al. bioRxiv 2022:
- https://www.biorxiv.org/content/10.1101/2022.02.08.479192v1.full.pdf
- In practice, the three methods (the real Hill equation, the original
- isi_violations calculation, and the correct equation implemented below)
- return almost identical values for contamination less than ~20%. They
- diverge strongly for 30% or more. For contamination levels above 50%
- (based on the original calculation), the corrected version is undefined
- due to a negative square root.
- Inputs:
- -------
- spike_train : array of spike times
- min_time : minimum time for potential spikes
- max_time : maximum time for potential spikes
- isi_threshold : threshold for isi violation
- min_isi : threshold for duplicate spikes
- Outputs:
- --------
- fpRate : rate of contaminating spikes as a fraction of overall rate
- A perfect unit has a fpRate = 0
- A unit with some contamination has a fpRate < 0.5
- A unit with lots of contamination has a fpRate > 1.0
- num_violations : total number of violations
- """
- duplicate_spikes = np.where(np.diff(spike_train) <= min_isi)[0]
- spike_train = np.delete(spike_train, duplicate_spikes + 1)
- isis = np.diff(spike_train)
- duration = max_time - min_time
- num_spikes = len(spike_train)
- num_violations = sum(isis < isi_threshold)
- fpRate = 1 - np.sqrt(1 - num_violations * duration /
- (pow(num_spikes, 2) * (isi_threshold - min_isi)))
- return fpRate, num_violations
- def presence_ratio(spike_train, min_time, max_time, num_bins=100):
- """Calculate fraction of time the unit is present within an epoch.
- Inputs:
- -------
- spike_train : array of spike times
- min_time : minimum time for potential spikes
- max_time : maximum time for potential spikes
- Outputs:
- --------
- presence_ratio : fraction of time bins in which this unit is spiking
- """
- h, b = np.histogram(spike_train, np.linspace(min_time, max_time, num_bins))
- return np.sum(h > 0) / (num_bins - 1)
- def firing_rate(spike_train, min_time = None, max_time = None):
- """Calculate firing rate for a spike train.
- If no temporal bounds are specified, the first and last spike time are used.
- Inputs:
- -------
- spike_train : numpy.ndarray
- Array of spike times in seconds
- min_time : float
- Time of first possible spike (optional)
- max_time : float
- Time of last possible spike (optional)
- Outputs:
- --------
- fr : float
- Firing rate in Hz
- """
- if min_time is not None and max_time is not None:
- duration = max_time - min_time
- else:
- duration = np.max(spike_train) - np.min(spike_train)
- fr = spike_train.size / duration
- return fr
- def amplitude_cutoff(amplitudes, num_histogram_bins = 500, histogram_smoothing_value = 3):
- """ Calculate approximate fraction of spikes missing from a distribution of amplitudes
- Assumes the amplitude histogram is symmetric (not valid in the presence of drift)
- Inspired by metric described in Hill et al. (2011) J Neurosci 31: 8699-8705
- Input:
- ------
- amplitudes : numpy.ndarray
- Array of amplitudes (don't need to be in physical units)
- Output:
- -------
- fraction_missing : float
- Fraction of missing spikes (0-0.5)
- If more than 50% of spikes are missing, an accurate estimate isn't possible
- """
- h,b = np.histogram(amplitudes, num_histogram_bins, density=True)
- pdf = gaussian_filter1d(h,histogram_smoothing_value)
- support = b[:-1]
- peak_index = np.argmax(pdf)
- G = np.argmin(np.abs(pdf[peak_index:] - pdf[0])) + peak_index
- bin_size = np.mean(np.diff(support))
- fraction_missing = np.sum(pdf[G:])*bin_size
- fraction_missing = np.min([fraction_missing, 0.5])
- return fraction_missing
- def mahalanobis_metrics(all_pcs, all_labels, this_unit_id):
- """ Calculates isolation distance and L-ratio (metrics computed from Mahalanobis distance)
- Based on metrics described in Schmitzer-Torbert et al. (2005) Neurosci 131: 1-11
- Inputs:
- -------
- all_pcs : numpy.ndarray (num_spikes x PCs)
- 2D array of PCs for all spikes
- all_labels : numpy.ndarray (num_spikes x 0)
- 1D array of cluster labels for all spikes
- this_unit_id : Int
- number corresponding to unit for which these metrics will be calculated
- Outputs:
- --------
- isolation_distance : float
- Isolation distance of this unit
- l_ratio : float
- L-ratio for this unit
- """
- pcs_for_this_unit = all_pcs[all_labels == this_unit_id,:]
- pcs_for_other_units = all_pcs[all_labels != this_unit_id, :]
- mean_value = np.expand_dims(np.mean(pcs_for_this_unit,0),0)
- try:
- VI = np.linalg.inv(np.cov(pcs_for_this_unit.T))
- except np.linalg.linalg.LinAlgError: # case of singular matrix
- return np.nan, np.nan
- mahalanobis_other = np.sort(cdist(mean_value,
- pcs_for_other_units,
- 'mahalanobis', VI = VI)[0])
- mahalanobis_self = np.sort(cdist(mean_value,
- pcs_for_this_unit,
- 'mahalanobis', VI = VI)[0])
- n = np.min([pcs_for_this_unit.shape[0], pcs_for_other_units.shape[0]]) # number of spikes
- if n >= 2:
- dof = pcs_for_this_unit.shape[1] # number of features
- l_ratio = np.sum(1 - chi2.cdf(pow(mahalanobis_other,2), dof)) / \
- mahalanobis_self.shape[0] # normalize by size of cluster, not number of other spikes
- isolation_distance = pow(mahalanobis_other[n-1],2)
- else:
- l_ratio = np.nan
- isolation_distance = np.nan
- return isolation_distance, l_ratio
- def lda_metrics(all_pcs, all_labels, this_unit_id):
- """ Calculates d-prime based on Linear Discriminant Analysis
- Based on metric described in Hill et al. (2011) J Neurosci 31: 8699-8705
- Inputs:
- -------
- all_pcs : numpy.ndarray (num_spikes x PCs)
- 2D array of PCs for all spikes
- all_labels : numpy.ndarray (num_spikes x 0)
- 1D array of cluster labels for all spikes
- this_unit_id : Int
- number corresponding to unit for which these metrics will be calculated
- Outputs:
- --------
- d_prime : float
- Isolation distance of this unit
- l_ratio : float
- L-ratio for this unit
- """
- X = all_pcs
- y = np.zeros((X.shape[0],),dtype='bool')
- y[all_labels == this_unit_id] = True
- lda = LDA(n_components=1)
- X_flda = lda.fit_transform(X, y)
- flda_this_cluster = X_flda[np.where(y)[0]]
- flda_other_cluster = X_flda[np.where(np.invert(y))[0]]
- d_prime = (np.mean(flda_this_cluster) - np.mean(flda_other_cluster))/np.sqrt(0.5*(np.std(flda_this_cluster)**2+np.std(flda_other_cluster)**2))
- return d_prime
- def nearest_neighbors_metrics(all_pcs, all_labels, this_unit_id, max_spikes_for_nn, n_neighbors):
- """ Calculates unit contamination based on NearestNeighbors search in PCA space
- Based on metrics described in Chung, Magland et al. (2017) Neuron 95: 1381-1394
- Inputs:
- -------
- all_pcs : numpy.ndarray (num_spikes x PCs)
- 2D array of PCs for all spikes
- all_labels : numpy.ndarray (num_spikes x 0)
- 1D array of cluster labels for all spikes
- this_unit_id : Int
- number corresponding to unit for which these metrics will be calculated
- max_spikes_for_nn : Int
- number of spikes to use (calculation can be very slow when this number is >20000)
- n_neighbors : Int
- number of neighbors to use
- Outputs:
- --------
- hit_rate : float
- Fraction of neighbors for target cluster that are also in target cluster
- miss_rate : float
- Fraction of neighbors outside target cluster that are in target cluster
- """
- total_spikes = all_pcs.shape[0]
- ratio = max_spikes_for_nn / total_spikes
- this_unit = all_labels == this_unit_id
- X = np.concatenate((all_pcs[this_unit,:], all_pcs[np.invert(this_unit),:]),0)
- n = np.sum(this_unit)
- if ratio < 1:
- inds = np.arange(0,X.shape[0]-1,1/ratio).astype('int')
- X = X[inds,:]
- n = int(n * ratio)
- nbrs = NearestNeighbors(n_neighbors=n_neighbors, algorithm='ball_tree').fit(X)
- distances, indices = nbrs.kneighbors(X)
- this_cluster_inds = np.arange(n)
- this_cluster_nearest = indices[:n,1:].flatten()
- other_cluster_nearest = indices[n:,1:].flatten()
- hit_rate = np.mean(this_cluster_nearest < n)
- miss_rate = np.mean(other_cluster_nearest < n)
- return hit_rate, miss_rate
- # ==========================================================
- # HELPER FUNCTIONS:
- # ==========================================================
- def features_intersect(pc_feature_ind, these_channels):
- """
- # Take only the channels that have calculated features out of the ones we are interested in:
- # This should reduce the occurence of 'except IndexError' below
- Args:
- pc_feature_ind
- these_channels: channels_to_use or units_for_channel
- Returns:
- channels_to_use: intersect of what's available in PCs and what's needed
- """
- intersect = set(pc_feature_ind[these_channels[0], :]) # Initialize
- for cluster_id2 in these_channels:
- # Make a running intersect of what is available and what is needed
- intersect = intersect & set(pc_feature_ind[cluster_id2, :])
- return np.array(list(intersect))
- def get_unit_pcs(unit_id,
- spike_clusters,
- spike_templates,
- pc_feature_ind,
- pc_features,
- channels_to_use,
- subsample):
- """ Return PC features for one unit
- Inputs:
- -------
- unit_id : Int
- ID for this unit
- spike_clusters : np.ndarray
- Cluster labels for each spike
- spike_templates : np.ndarry
- Template labels for each spike
- pc_feature_ind : np.ndarray
- Channels used for PC calculation for each unit
- pc_features : np.ndarray
- Array of all PC features
- channels_to_use : np.ndarray
- Channels to use for calculating metrics
- subsample : Int
- maximum number of spikes to return
- Output:
- -------
- unit_PCs : numpy.ndarray (float)
- PCs for one unit (num_spikes x num_PCs x num_channels)
- """
- inds_for_unit = np.where(spike_clusters == unit_id)[0]
- spikes_to_use = np.random.permutation(inds_for_unit)[:subsample]
- unique_template_ids = np.unique(spike_templates[spikes_to_use])
- unit_PCs = []
- for template_id in unique_template_ids:
- index_mask = spikes_to_use[np.squeeze(spike_templates[spikes_to_use]) == template_id]
- these_inds = pc_feature_ind[template_id, :]
- pc_array = []
- for i in channels_to_use:
- if np.isin(i, these_inds):
- channel_index = np.argwhere(these_inds == i)[0][0]
- pc_array.append(pc_features[index_mask, :, channel_index])
- else:
- return None
- unit_PCs.append(np.stack(pc_array, axis=-1))
- if len(unit_PCs) > 0:
- return np.concatenate(unit_PCs)
- else:
- return None
metrics.py at commit 9199927, under BSD-2-Clause · at the source
Overview
- Department of Cognitive Science, University of Potsdam, 14469 Potsdam, Germany
- Nuuron GmbH Berlin, Berlin, Germany
- Charité-Universitätsmedizin Berlin, Corporate Member of Freie Universität Berlin and Humboldt-Universität Berlin, Clinical Neurotechnology Laboratory, Department of Psychiatry and Neurosciences, 10117 Berlin, Germany
- German Center for Neurodegenerative Diseases (DZNE) Berlin, 10117 Berlin, Germany
- Charité-Universitätsmedizin Berlin, Corporate Member of Freie Universität Berlin and Humboldt-Universität Berlin, Neuroscience Research Center, 10117 Berlin, Germany
- Charité-Universitätsmedizin Berlin, Corporate Member of Freie Universität Berlin and Humboldt-Universität Berlin, Einstein Center for Neurosciences, 10117 Berlin, Germany
- Charité-Universitätsmedizin Berlin, Corporate Member of Freie Universität Berlin and Humboldt-Universität Berlin, Bernstein Center for Computational Neuroscience, 10115 Berlin, Germany
- Charité-Universitätsmedizin Berlin, Corporate Member of Freie Universität Berlin and Humboldt-Universität Berlin, NeuroCure Cluster of Excellence, 10117 Berlin, Germany
- Charité-Universitätsmedizin Berlin, Corporate Member of Freie Universität Berlin and Humboldt-Universität zu Berlin, Institute of Cell and Neurobiology, 10115 Berlin, Germany
- Institute of Biology, Otto von Guericke University Magdeburg, 39120 Magdeburg, Germany
Abstract
Today, flickering full-field visual stimulation is used to increase neuronal oscillations for a variety of research or therapeutic purposes. We propose spatially organized sequential visual flickering stimulation as a newer tool to circumvent the intrinsic low-pass filter nature of the vertebrate visual system, in order to increase the power of high frequency oscillations in the visual system. We show that spatially organized visual flickering can increase power in high frequencies (100 to 190 Hz) in the visual cortex of mice. Consequently, spatially organized sequential sensory stimulation should be regarded as a putative new way leading to power increases in high frequency domains.
Supplementary Information: The online version contains supplementary material available at 10.1038/
Reproduced under the paper's license (CC BY), from the paper cited above.
Repository
Its files are read in the Code ↔ Paper reader above, with 1 match between paragraphs and lines of code.
AllenInstitute/ecephys_spike_sorting
919992748a5324724ba87169ecdcf9eb6e3b9973, 18 August 2024Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
116 files
- .cookiecutter/
update.sh , Shell, 15 lines - .cookiecutter/
update_from_repo.py , Python, 12 lines - docs/
aibs_sphinx/ , Shell, 8 linesbuildPortalAssets.sh - docs/
aibs_sphinx/ , JavaScript, 1 linestatic/ external_assets/ bundled.js - docs/
aibs_sphinx/ , JavaScript, 292 linesstatic/ external_assets/ javascript/ AC_RunActiveContent.js - docs/
aibs_sphinx/ , JavaScript, 14 linesstatic/ external_assets/ javascript/ appConfig.js - docs/
aibs_sphinx/ , JavaScript, 28 linesstatic/ external_assets/ javascript/ browserVersions.js - docs/
aibs_sphinx/ , JavaScript, 5 linesstatic/ external_assets/ javascript/ relatedData.js - docs/
conf.py , Python, 295 lines - docs/
gallery/ , Python, 21 lineshelloworld.py - ecephys_spike_sorting/
__init__.py , Python, 1 line - ecephys_spike_sorting/
common/ , Python, 79 linesOEFileInfo.py - ecephys_spike_sorting/
common/ , Python, 1 line__init__.py - ecephys_spike_sorting/
common/ , Python, 84 linesepoch.py - ecephys_spike_sorting/
common/ , Python, 29 linesschemas.py - ecephys_spike_sorting/
common/ , Python, 451 linesutils.py - ecephys_spike_sorting/
common/ , Python, 404 linesvisualization.py - ecephys_spike_sorting/
modules/ , Python, 1 line__init__.py - ecephys_spike_sorting/
modules/ , Python, 1 lineautomerging/ __init__.py - ecephys_spike_sorting/
modules/ , Python, 52 linesautomerging/ __main__.py - ecephys_spike_sorting/
modules/ , Python, 27 linesautomerging/ _schemas.py - ecephys_spike_sorting/
modules/ , Python, 99 linesautomerging/ automerging.py - ecephys_spike_sorting/
modules/ , Python, 141 linesautomerging/ merges.py - ecephys_spike_sorting/
modules/ , Python, 160 linesautomerging/ metrics.py - ecephys_spike_sorting/
modules/ , Python, 266 linesautomerging/ spike_ISI.py - ecephys_spike_sorting/
modules/ , Python, 1 linedepth_estimation/ __init__.py - ecephys_spike_sorting/
modules/ , Python, 74 linesdepth_estimation/ __main__.py - ecephys_spike_sorting/
modules/ , Python, 48 linesdepth_estimation/ _schemas.py - ecephys_spike_sorting/
modules/ , Python, 210 linesdepth_estimation/ depth_estimation.py - ecephys_spike_sorting/
modules/ , C++, 69 linesextract_from_npx/ NpxExtractor/ NpxExtractor.cpp - ecephys_spike_sorting/
modules/ , C/C++, 92 linesextract_from_npx/ NpxExtractor/ NpxExtractor.h - ecephys_spike_sorting/
modules/ , C++, 328 linesextract_from_npx/ NpxExtractor/ NpxExtractor3a.cpp - ecephys_spike_sorting/
modules/ , C/C++, 43 linesextract_from_npx/ NpxExtractor/ NpxExtractor3a.h - ecephys_spike_sorting/
modules/ , C++, 307 linesextract_from_npx/ NpxExtractor/ NpxExtractorPXI.cpp - ecephys_spike_sorting/
modules/ , C/C++, 50 linesextract_from_npx/ NpxExtractor/ NpxExtractorPXI.h - ecephys_spike_sorting/
modules/ , C++, 115 linesextract_from_npx/ NpxExtractor/ main.cpp - ecephys_spike_sorting/
modules/ , C/C++, 1,023 linesextract_from_npx/ NpxExtractor/ neuropix-api/ NeuropixAPI.h - ecephys_spike_sorting/
modules/ , C/C++, 138 linesextract_from_npx/ NpxExtractor/ neuropix-api/ NeuropixAPI_private.h - ecephys_spike_sorting/
modules/ , C++, 218 linesextract_from_npx/ NpxExtractor/ npy-c++/ NpyFile.cpp - ecephys_spike_sorting/
modules/ , C/C++, 85 linesextract_from_npx/ NpxExtractor/ npy-c++/ NpyFile.h - ecephys_spike_sorting/
modules/ , C/C++, 15 linesextract_from_npx/ NpxExtractor/ stdafx.h - ecephys_spike_sorting/
modules/ , C/C++, 8 linesextract_from_npx/ NpxExtractor/ targetver.h - ecephys_spike_sorting/
modules/ , Python, 1 lineextract_from_npx/ __init__.py - ecephys_spike_sorting/
modules/ , Python, 71 linesextract_from_npx/ __main__.py - ecephys_spike_sorting/
modules/ , Python, 28 linesextract_from_npx/ _schemas.py - ecephys_spike_sorting/
modules/ , Python, 65 linesextract_from_npx/ create_settings_json.py - ecephys_spike_sorting/
modules/ , Python, 1 linekilosort_helper/ __init__.py - ecephys_spike_sorting/
modules/ , Python, 138 lineskilosort_helper/ __main__.py - ecephys_spike_sorting/
modules/ , Python, 110 lineskilosort_helper/ _schemas.py - ecephys_spike_sorting/
modules/ , MATLAB, 51 lineskilosort_helper/ kilosort2_master_file.m - ecephys_spike_sorting/
modules/ , Python, 151 lineskilosort_helper/ matlab_file_generator.py - ecephys_spike_sorting/
modules/ , Python, 1 linekilosort_postprocessing/ __init__.py - ecephys_spike_sorting/
modules/ , Python, 94 lineskilosort_postprocessing/ __main__.py - ecephys_spike_sorting/
modules/ , Python, 28 lineskilosort_postprocessing/ _schemas.py - ecephys_spike_sorting/
modules/ , Python, 230 lineskilosort_postprocessing/ postprocessing.py - ecephys_spike_sorting/
modules/ , Python, 1 linemean_waveforms/ __init__.py - ecephys_spike_sorting/
modules/ , Python, 70 linesmean_waveforms/ __main__.py - ecephys_spike_sorting/
modules/ , Python, 35 linesmean_waveforms/ _schemas.py - ecephys_spike_sorting/
modules/ , Python, 212 linesmean_waveforms/ extract_waveforms.py - ecephys_spike_sorting/
modules/ , Python, 433 linesmean_waveforms/ waveform_metrics.py - ecephys_spike_sorting/
modules/ , C/C++, 215 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ JuceLibraryCode/ AppConfig.h - ecephys_spike_sorting/
modules/ , C/C++, 48 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ JuceLibraryCode/ JuceHeader.h - ecephys_spike_sorting/
modules/ , C++, 9 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ JuceLibraryCode/ juce_audio_basics.cpp - ecephys_spike_sorting/
modules/ , C++, 9 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ JuceLibraryCode/ juce_audio_devices.cpp - ecephys_spike_sorting/
modules/ , C++, 9 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ JuceLibraryCode/ juce_audio_formats.cpp - ecephys_spike_sorting/
modules/ , C++, 9 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ JuceLibraryCode/ juce_audio_processors.cp p - ecephys_spike_sorting/
modules/ , C++, 9 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ JuceLibraryCode/ juce_core.cpp - ecephys_spike_sorting/
modules/ , C++, 9 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ JuceLibraryCode/ juce_cryptography.cpp - ecephys_spike_sorting/
modules/ , C++, 9 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ JuceLibraryCode/ juce_data_structures.cpp - ecephys_spike_sorting/
modules/ , C++, 9 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ JuceLibraryCode/ juce_events.cpp - ecephys_spike_sorting/
modules/ , C++, 9 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ JuceLibraryCode/ juce_graphics.cpp - ecephys_spike_sorting/
modules/ , C++, 9 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ JuceLibraryCode/ juce_gui_basics.cpp - ecephys_spike_sorting/
modules/ , C++, 9 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ JuceLibraryCode/ juce_gui_extra.cpp - ecephys_spike_sorting/
modules/ , C++, 9 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ JuceLibraryCode/ juce_opengl.cpp - ecephys_spike_sorting/
modules/ , C++, 9 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ JuceLibraryCode/ juce_video.cpp - ecephys_spike_sorting/
modules/ , C++, 186 linesmedian_subtraction/ SpikeBandMedianSubtracti on/ Source/ Main.cpp - ecephys_spike_sorting/
modules/ , Python, 1 linemedian_subtraction/ __init__.py - ecephys_spike_sorting/
modules/ , Python, 55 linesmedian_subtraction/ __main__.py - ecephys_spike_sorting/
modules/ , Python, 27 linesmedian_subtraction/ _schemas.py - ecephys_spike_sorting/
modules/ , Python, 1 linenoise_templates/ __init__.py - ecephys_spike_sorting/
modules/ , Python, 60 linesnoise_templates/ __main__.py - ecephys_spike_sorting/
modules/ , Python, 49 linesnoise_templates/ _schemas.py - ecephys_spike_sorting/
modules/ , Python, 369 linesnoise_templates/ id_noise_templates.py - ecephys_spike_sorting/
modules/ , Python, 289 linesnoise_templates/ template_classifier_app. py - ecephys_spike_sorting/
modules/ , Python, 136 linesnoise_templates/ train_classifier.py - ecephys_spike_sorting/
modules/ , Python, 1 linequality_metrics/ __init__.py - ecephys_spike_sorting/
modules/ , Python, 94 linesquality_metrics/ __main__.py - ecephys_spike_sorting/
modules/ , Python, 43 linesquality_metrics/ _schemas.py - ecephys_spike_sorting/
modules/ , Python, 997 lines, 1 matchquality_metrics/ metrics.py - ecephys_spike_sorting/
scripts/ , Python, 1 line__init__.py - ecephys_spike_sorting/
scripts/ , Python, 15 linesbatch_plotting.py - ecephys_spike_sorting/
scripts/ , Python, 44 linesbatch_processing.py - ecephys_spike_sorting/
scripts/ , Python, 73 linesbatch_processing_320rack .py - ecephys_spike_sorting/
scripts/ , Python, 77 linesbatch_processing_NP0.py - ecephys_spike_sorting/
scripts/ , Python, 128 linesbatch_processing_NP0_ks. py - ecephys_spike_sorting/
scripts/ , Python, 916 linesbatch_processing_gui.py - ecephys_spike_sorting/
scripts/ , Python, 819 linesbatch_processing_paralle l.py - ecephys_spike_sorting/
scripts/ , Python, 820 linesbatch_processing_paralle l_extract_from_network.p y - ecephys_spike_sorting/
scripts/ , Python, 101 linesbatch_processing_serial. py - ecephys_spike_sorting/
scripts/ , Python, 205 linescreate_input_json.py - ecephys_spike_sorting/
scripts/ , Python, 1 linehelpers/ __init__.py - ecephys_spike_sorting/
scripts/ , Python, 398 lineshelpers/ check_data_processing.py - ecephys_spike_sorting/
scripts/ , Python, 54 lineshelpers/ plot_raw_data.py - ecephys_spike_sorting/
scripts/ , Python, 1 linehelpers/ processing.py - setup.py, Python, 39 lines
- tests/
__init__.py , Python, 10 lines - tests/
integration/ , Python, 10 lines__init__.py - tests/
unit/ , Python, 58 linescommon/ test_utils.py - tests/
unit/ , Python, 26 linesmodules/ automerging/ test_automerging.py - tests/
unit/ , Python, 46 linesmodules/ depth_estimation/ test_depth_estimation.py - tests/
unit/ , Python, 16 linesmodules/ extract_from_npx/ test_extract_from_npx.py - tests/
unit/ , Python, 34 linesmodules/ mean_waveforms/ test_mean_waveforms.py - tests/
unit/ , Python, 23 linesmodules/ noise_templates/ test_noise_templates.py - tests/
unit/ , Python, 38 linesmodules/ quality_metrics/ test_quality_metrics.py - LICENSE.txt, License, 33 lines
- README.md, Text, 140 lines
The paper's code and data availability statement is in the Data section.
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;
- 114 scripts, each with its path and the digest of its content;
- 1 match between paragraphs of the paper and lines of the code (method lexical-v1);
- 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.
Data availability
Data and scripts will be provided freely upon reasonable request.
Reproduced under the paper's license (CC BY), from the paper cited above.
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, 28 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 7 authors, 2 keywords, 4 MeSH terms, 1 funder, 36 references.
Cite
This paper
Keil, J., Hernandez-Urbina, V., Vassiliou, C., Dean, C., Schmitz, D., Kremkow, J., & Sibille, J. (2026). Sequential visual stimuli increase high frequency power in the visual cortex. Scientific reports, 16(1), 15228. https://
BibTeX
@article{keil2026sequent
author = {Keil, Julian and Hernandez-Urbina, Victor and Vassiliou, Chrystalleni and Dean, Camin and Schmitz, Dietmar and Kremkow, Jens and Sibille, Jérémie},
title = {{Sequential visual stimuli increase high frequency power in the visual cortex}},
journal = {Scientific reports},
year = {2026},
month = may,
volume = {16},
number = {1},
pages = {15228},
publisher = {Nature Publishing Group},
issn = {2045-2322},
doi = {10.1038/
url = {https://
pmid = {42144416},
pmcid = {PMC13181131}
}
RIS
TY - JOUR
AU - Keil, Julian
AU - Hernandez-Urbina, Victor
AU - Vassiliou, Chrystalleni
AU - Dean, Camin
AU - Schmitz, Dietmar
AU - Kremkow, Jens
AU - Sibille, Jérémie
TI - Sequential visual stimuli increase high frequency power in the visual cortex
T2 - Scientific reports
J2 - Sci Rep
PY - 2026
DA - 2026/
VL - 16
IS - 1
SP - 15228
SN - 2045-2322
PB - Nature Publishing Group
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "Sequential visual stimuli increase high frequency power in the visual cortex",
"container-title": "Scientific reports",
"author": [
{
"family": "Keil",
"given": "Julian"
},
{
"family": "Hernandez-Urbina",
"given": "Victor"
},
{
"family": "Vassiliou",
"given": "Chrystalleni"
},
{
"family": "Dean",
"given": "Camin"
},
{
"family": "Schmitz",
"given": "Dietmar"
},
{
"family": "Kremkow",
"given": "Jens"
},
{
"family": "Sibille",
"given": "Jérémie"
}
],
"container-title-short":
"volume": "16",
"issue": "1",
"page": "15228",
"DOI": "10.1038/
"PMID": "42144416",
"PMCID": "PMC13181131",
"ISSN": "2045-2322",
"publisher": "Nature Publishing Group",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
17
]
]
}
}
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.1038/s41593-026-02232-0 [code]
- Entorhinal cortex represents task-relevant remote locations independently of CA1.Journal: Nature neuroscienceIn common: Kilosort, xarray, h5py, 6 other tools, systems, mouse, 1 reference
- [2] doi:10.1162/imag.a.1229 [code]
- 40 Hz audiovisual stimulation improves sustained attention and related brain oscillations.Journal: Imaging neuroscience (Cambridge, Mass.)In common: pandas, SciPy, Matplotlib, 1 other tool, 7 references
- [3] doi:10.1038/s41467-026-76581-6 [code]
- Thalamocortical bursts encode reward contingencies and drive associative learning.Journal: Nature communicationsIn common: Kilosort, xarray, h5py, 6 other tools, systems, mouse
- [4] doi:10.7554/elife.110588 [code]
- Opening the black box toward a modular approach to spike sorting.Journal: eLifeIn common: Kilosort, h5py, Pillow, 5 other tools, mouse, 2 references
- [5] doi:10.1038/s41593-026-02258-4 [code]
- Laminar organization of cellular microcircuits modulating human interictal epileptiform discharges.Journal: Nature neuroscienceIn common: Kilosort, scikit-learn, pandas, 3 other tools, systems, 3 references
- [6] 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: Kilosort, h5py, Pillow, 5 other tools, 1 reference
- [7] doi:10.7554/elife.109717 [code]
- Retrosplenial cortex enables context-dependent goal-directed sensorimotor transformation.Journal: eLifeIn common: Kilosort, h5py, Pillow, 5 other tools, systems, mouse
- [8] doi:10.1038/s41467-026-72057-9 [code]
- Sex-specific behavioral feedback modulates sensorimotor processing and drives flexible social behavior.Journal: Nature communicationsIn common: xarray, h5py, Pillow, 5 other tools, systems
- [9] doi:10.1038/s41586-026-10331-y [code]
- Active dissociation of intracortical spiking and high gamma activity.Journal: NatureIn common: h5py, Pillow, scikit-learn, 4 other tools, 2 references
- [10] doi:10.1098/rstb.2024.0461 [code]
- Shallow recurrent decoders for neural and behavioural dynamics.Journal: Philosophical transactions of the Royal Society of London. Series B, Biological sciencesIn common: xarray, h5py, Pillow, 5 other tools, mouse
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, 114 scripts, and 1 match 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:94de54a84aaec741…
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
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
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.
