Hippocampal skill memory expansion drives online performance dynamics during skill learning.
The 2 matches
- [1] § STAR★METHODS › METHOD DETAILS › Oscillatory bursting analysis ↔ spectralevents.py, lines 293–399 · score 0.79 · spectralEvents, full width, median power, Event duration, FWHM, detection
- [2] § STAR★METHODS › METHOD DETAILS › Oscillatory bursting analysis ↔ spectralevents_find.m, lines 282–404 · score 0.79 · spectralEvents, full width, median power, Event duration, FWHM, overlap
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 · 601 lines · 23 KB · BSD-3-Clause · 1 match
- '''Spectral Event Analysis functions'''
- # Authors: Tim Bardouille <[email hidden]>
- # Ryan Thorpe <[email hidden]>
- import numpy as np
- import scipy.signal as signal
- import scipy.ndimage as ndimage
- import scipy.ndimage.filters as filters
- import matplotlib.pyplot as plt
- def tfr(timeseries, freqs, samp_freq, width=7.):
- '''Calculate the time-frequency response by convolving with Morlet wavelets
- Parameters
- ----------
- timeseries : array-like, shape ([n_epochs,] n_times)
- The timeseries signal, one for each epoch of data.
- freqs : array-like, shape (n_freqs,)
- Frequency domain of the TFR (Hz).
- samp_freq : float
- Sampling frequency (Hz).
- width : int
- Number of cycles in each Morlet wavelet (>5 advisable).
- Returns
- -------
- tfr : array, shape (n_epochs, n_freqs, n_times)
- A collection of time-frequency responses (TFRs), one for each epoch.
- Notes
- -----
- Adapted from Ole Jensen's traces2tfr in the 4Dtools toolbox.
- '''
- ts = np.atleast_2d(timeseries)
- n_epochs = ts.shape[0]
- n_samps = ts.shape[1]
- n_freqs = len(freqs)
- # Validate freqs input
- f_nyquist = samp_freq / 2 # Nyquist frequency
- dt = 1 / samp_freq # Sampling time interval
- min_freq = 1 / (n_samps * dt) # Minimum resolvable frequency
- if freqs[0] < min_freq:
- raise ValueError('Frequency vector includes values outside the '
- 'resolvable/alias-free range.')
- elif freqs[-1] > f_nyquist:
- raise ValueError('Frequency vector includes values outside the '
- 'resolvable/alias-free range.')
- elif np.abs(freqs[1] - freqs[0]) < min_freq:
- raise ValueError('Frequency vector includes values outside the '
- 'resolvable/alias-free range.')
- tfr = np.zeros((n_epochs, n_freqs, n_samps))
- # Trial Loop
- for trial_idx in np.arange(n_epochs):
- ts_detrended = signal.detrend(ts[trial_idx, :])
- # Frequency loop
- for freq_idx in np.arange(n_freqs):
- tfr[trial_idx, freq_idx, :] = _energyvec(freqs[freq_idx],
- ts_detrended, samp_freq,
- width)
- return tfr
- def _get_power_thresholds(tfr, FOM_threshold=6.):
- '''Get the power threshold for each frequency band of a TFR'''
- med_powers = np.median(tfr, axis=1)
- return med_powers * FOM_threshold, med_powers
- def tfr_normalize(tfr):
- '''Normalize the power in each frequency band of a TFR
- Parameters
- ----------
- tfr : array, shape ([n_epochs,] n_freqs, n_times)
- The time-frequency response (TFR) to be normalized.
- Returns
- -------
- tfr_norm : array
- The normalized TFR calculated by dividing the power values in each
- frequency bin by the median power across all trials and time samples.
- '''
- if len(tfr.shape) == 3:
- n_epochs, _, n_times = tfr.shape
- med_powers = np.median(tfr, axis=(0, 2))
- med_powers_tiled = np.tile(med_powers, (n_epochs, n_times, 1))
- med_powers = np.transpose(med_powers_tiled, axes=(0, 2, 1))
- elif len(tfr.shape) == 2:
- _, n_times = tfr.shape
- med_powers = np.median(tfr, axis=1)
- med_powers_tiled = np.tile(med_powers, (n_times, 1))
- med_powers = np.transpose(med_powers_tiled, axes=(1, 0))
- else:
- raise ValueError(f'TFR must be an array of at least 2 dimensions. Got '
- f'{tfr.shape}.')
- return tfr / med_powers
- def find_events(tfr, times, freqs, event_band, thresholds=None,
- threshold_FOM=6.):
- '''Locate spectral events in a time-frequency response.
- Parameters
- ----------
- tfr : array, shape ([n_epochs,] n_freqs, n_times)
- The time-frequency response (TFR) in which to search for high power
- spectral events.
- times : array-like, shape (n_times,)
- Time domain of the TFR in seconds.
- freqs : array-like, shape (n_freqs,)
- Frequency domain of the TFR in Hertz.
- event_band : list
- Lower and upper bounds (inclusive, Hz) of the frequency band-of-
- interest in which to search for spectral events.
- thresholds : None | array-like, shape (n_freqs,)
- If not None, these frequency-specific threshold values (in units of
- spectral power) will be used to identify suprathreshold spectral
- events.
- threshold_FOM : float | int
- Factor-of-the-median threshold (a.u.) with which to identify
- suprathreshold spectral events (default: 6). This threshold value is
- applied across frequencies of the TFR when thresholds=None. See Shin et
- al. eLife 2017 for more details concerning this value.
- Returns
- -------
- events : list of list of dict
- A nested list with n_epochs elements in the outer list and n_events in
- the inner list. Each inner element comprises an event that is
- characterized by dictionary items including it's location in time,
- location in frequency, duration, and frequency span, and more.
- Notes
- -----
- This version only supports find-method #1 at the moment. As outlined in
- Shin et al. eLife (2017), it isolates spectral events by first retrieving
- all local maxima in un-normalized TFR using imregionalmax, then selecting
- suprathreshold peaks within the frequency band of interest. This method
- allows for multiple, overlapping events to occur in a given suprathreshold
- region and does not guarantee that the presence of within-band,
- suprathreshold activity in any given trial will render an event.
- '''
- # ensure tfr has 3 dimensions (epochs x freqs x time)
- if len(tfr.shape) < 3:
- tfr = tfr[np.newaxis, ...]
- n_epochs = tfr.shape[0]
- n_freqs = tfr.shape[1]
- n_times = tfr.shape[2]
- # some time steps might be slightly different due to rounding error, etc.
- samp_freq = 1 / np.unique(np.diff(times).round(10))
- if len(samp_freq) > 1:
- raise ValueError('Sampling rate is not consistent across time '
- 'samples.')
- samp_freq = samp_freq[0]
- # concatenate trials together to make one big spectrogram, then find thresh
- tfr_permute = np.transpose(tfr, [1, 2, 0]) # freq x time x trial
- tfr_concat_trials = np.reshape(tfr_permute, (n_freqs, n_times * n_epochs))
- thresholds, med_powers = _get_power_thresholds(tfr_concat_trials,
- FOM_threshold=threshold_FOM)
- # Validate consistency of parameter dimensions
- if n_freqs != len(freqs):
- raise ValueError('Mismatch in frequency dimensions!')
- if n_times != len(times):
- raise ValueError('Mismatch in time dimensions!')
- # Find spectral events using appropriate method
- # Implementing find_method=1 for now
- events = _find_localmax_method_1(tfr, freqs, times, event_band,
- thresholds, med_powers, samp_freq)
- return events
- def _energyvec(f, s, Fs, width=7.):
- '''Spectral energy as a function of frequency using Morlet wavelets.'''
- dt = 1 / Fs
- sf = f / width
- st = 1 / (2 * np.pi * sf)
- t_single_side = np.arange(0., 3.5 * st, dt)
- # mirror about 0; always an odd # of elements
- times = np.r_[-t_single_side[-1:0:-1], t_single_side]
- wavelet = _morlet(f, times, width)
- fourier_coeffs = np.convolve(s, wavelet)
- spec_energy = 2 * (dt * np.abs(fourier_coeffs)) ** 2
- lower_idx = int(len(wavelet) // 2)
- upper_idx = int(len(spec_energy) - (len(wavelet) // 2))
- return spec_energy[lower_idx:upper_idx]
- def _morlet(f, t, width):
- '''
- Morlet's wavelet for frequency f and time t. The wavelet will be normalized
- so the total energy is 1. width defines the ``width'' of the wavelet. A
- value >= 5 is suggested.
- Ref: Tallon-Baudry et al., J. Neurosci. 15, 722-734 (1997)
- '''
- sf = f / width
- st = 1 / (2 * np.pi * sf)
- A = 1 / (st * np.sqrt(2 * np.pi))
- y = A * np.exp(-t ** 2 / (2 * st ** 2)) * np.exp(1j * 2 * np.pi * f * t)
- return y
- def _fwhm_lower_upper_bound1(vec, peakInd, peakValue):
- '''
- Function to find the lower and upper indices within which the vector is
- less than the FWHM with some rather complicated boundary rules
- (Shin, eLife, 2017).
- '''
- halfMax = peakValue/2
- # Extract data before the peak only (data should be rising at the end of
- # the new array)
- vec1 = vec[0:peakInd]
- # Find indices less than half the max
- vec1_underThreshold = np.where(vec1 < halfMax)[0]
- if len(vec1_underThreshold) == 0:
- # There are no indices less than half the max, so we have to estimate
- # the lower edge
- estimateLowerEdge = True
- else:
- # There are indices less than half the max, take the last one under
- # halfMax as the lower edge
- estimateLowerEdge = False
- lowerEdgeIndex = vec1_underThreshold[-1]
- # Extract data following the peak only (data should be falling at the start
- # of the new array)
- vec2 = vec[peakInd:]
- # Find indices less than half the max
- vec2_underThreshold = np.where(vec2 < halfMax)[0]
- if len(vec2_underThreshold) == 0:
- # There are no indices less than half the max, so we have to estimate
- # the upper edge
- estimateUpperEdge = True
- else:
- # There are indices less than half the max, take the first one under
- # halfMax as the upper edge
- estimateUpperEdge = False
- upperEdgeIndex = vec2_underThreshold[0] + len(vec1)
- if not estimateLowerEdge:
- if not estimateUpperEdge:
- # FWHM fits in the range, so pick off the edges of the FWHM
- lowerInd = lowerEdgeIndex
- upperInd = upperEdgeIndex
- FWHM = upperInd - lowerInd
- if estimateUpperEdge:
- # FWHM fits in on the low end, but hits the edge on the high end
- lowerInd = lowerEdgeIndex
- upperInd = len(vec)-1
- FWHM = 2 * (peakInd - lowerInd + 1)
- else:
- if not estimateUpperEdge:
- # FWHM hits the edge on the low end, but fits on the high end
- lowerInd = 0
- upperInd = upperEdgeIndex
- FWHM = 2 * (upperInd - peakInd + 1)
- if estimateUpperEdge:
- # FWHM hits the edge on the low end and the high end
- lowerInd = 0
- upperInd = len(vec)-1
- FWHM = 2*len(vec)
- return lowerInd, upperInd, FWHM
- def _find_localmax_method_1(tfr, freqs, times, event_band,
- eventThresholdByFrequency,
- medianPower, Fs):
- '''
- 1st event-finding method (primary event detection method in Shin et
- al. eLife 2017): Find spectral events by first retrieving all local
- maxima in un-normalized TFR using imregionalmax, then selecting
- suprathreshold peaks within the frequency band of interest. This
- method allows for multiple, overlapping events to occur in a given
- suprathreshold region and does not guarantee the presence of
- within-band, suprathreshold activity in any given trial will render
- an event.
- spectralEvents: 12 column matrix for storing local max event metrics:
- hit/miss, maxima frequency,
- lowerbound frequency, upperbound frequency,
- frequency span, maxima timing, event onset timing,
- event offset timing, event duration, maxima power,
- maxima/median power
- '''
- n_epochs = tfr.shape[0]
- all_epochs_events = list()
- # Retrieve all local maxima in tfr using python equivalent of imregionalmax
- for trial_idx in range(n_epochs):
- epoch_events = list()
- # Get tfr data for this trial [frequency x time]
- thistfr = tfr[trial_idx, :, :]
- # Find local maxima in the tfr data
- data = thistfr
- # Find maximum amoung adjacent pixels (3x3 footprint) for each pixel
- data_max = filters.maximum_filter(data, size=(3, 3))
- maxima = (data == data_max)
- data_min = filters.minimum_filter(data, size=(3, 3))
- # Rule out pixels with footprints that have flatlined
- maxima[data_max == data_min] = False
- labeled, num_objects = ndimage.label(maxima)
- yx = ndimage.center_of_mass(data, labels=labeled,
- index=range(1, num_objects + 1))
- peakF = list()
- peakT = list()
- peakPower = list()
- for f_idx, t_idx in yx:
- f_idx = int(round(f_idx))
- t_idx = int(round(t_idx))
- event_freq = freqs[f_idx]
- if (event_freq >= event_band[0] and event_freq <= event_band[1] and
- thistfr[f_idx, t_idx] > eventThresholdByFrequency[f_idx]):
- peakF.append(f_idx)
- peakT.append(t_idx)
- peakPower.append(thistfr[f_idx, t_idx])
- numPeaks = len(peakF)
- # Find local maxima lowerbound, upperbound, and full width at half max
- # for both frequency and time
- for lmi in range(numPeaks):
- thisPeakF = peakF[lmi]
- thisPeakT = peakT[lmi]
- thisPeakPower = peakPower[lmi]
- # Indices of tfr frequencies < half max power at the time of a
- # given local peak
- tfrFrequencies = thistfr[:, thisPeakT]
- lowerInd, upperInd, FWHM = _fwhm_lower_upper_bound1(tfrFrequencies,
- thisPeakF,
- thisPeakPower)
- lowerEdgeFreq = freqs[lowerInd]
- upperEdgeFreq = freqs[upperInd]
- FWHMFreq = FWHM * (freqs[1] - freqs[0])
- # Indices of tfr times < half max power at the frequency of a given
- # local peak
- tfrTimes = thistfr[thisPeakF, :]
- lowerInd, upperInd, FWHM = _fwhm_lower_upper_bound1(tfrTimes,
- thisPeakT,
- thisPeakPower)
- lowerEdgeTime = times[lowerInd]
- upperEdgeTime = times[upperInd]
- FWHMTime = FWHM / Fs
- # Put peak characteristics to a dictionary
- peakParameters = {
- 'Peak Frequency': freqs[thisPeakF],
- 'Lower Frequency Bound': lowerEdgeFreq,
- 'Upper Frequency Bound': upperEdgeFreq,
- 'Frequency Span': FWHMFreq,
- 'Peak Time': times[thisPeakT],
- 'Event Onset Time': lowerEdgeTime,
- 'Event Offset Time': upperEdgeTime,
- 'Event Duration': FWHMTime,
- 'Peak Power': thisPeakPower,
- 'Normalized Peak Power': thisPeakPower / medianPower[thisPeakF]
- }
- # Build a list of dictionaries
- epoch_events.append(peakParameters)
- all_epochs_events.append(epoch_events)
- return all_epochs_events
- def plot_events(tfr, times, freqs, event_band, spec_events=None,
- timeseries=None, ax=None, vlim=None, ylim_ts=None, label=None):
- '''Plot a single TFR spectrogram overlayed with spectral events.
- Parameters
- ----------
- tfr : array, shape (n_freqs, n_times)
- A time-frequency response (TFR).
- times : array-like, shape (n_times,)
- Time domain of the TFR in seconds.
- freqs : array-like, shape (n_freqs,)
- Frequency domain of the TFR in Hertz.
- event_band : list
- Lower and upper bounds (inclusive, Hz) of the frequency band-of-
- interest in which spectral events were detected.
- spec_events : list of dict
- A single-level list where each element comprises an event that is
- characterized by dictionary items including it's location in time,
- location in frequency, duration, and frequency span, and more. If None,
- no events are plotted.
- timeseries : array, shape (n_times,) | None
- The timeseries associated with the TFR.
- ax : None | Matplotlib.axes.Axes instance
- The Axes instance with wich to plot the spectrogram on. If None, a new
- Axes object is created.
- vlim : list, shape (2,) | None
- If not None, sets the colorbar lower and upper bounds for the
- spectrogram across all axes of the returned figure. If None, each
- spectrogram (including example epochs) sets its vlim independently.
- ylim_ts : list | None
- The lower and upper limits of the timeseries (if applicable) overlayed
- on the spectrogram. If None, these values are automatically assigned.
- label : str | None
- A label to overlay in the upper left corner of this plot.
- Return
- ------
- fig : Matplotlib.figure.Figure instance
- The Figure instance containing the spectrogram.
- '''
- if tfr.shape != (len(freqs), len(times)):
- raise ValueError(f'tfr must be an array of shape (n_freqs, n_times), '
- f'got tfr: {tfr.shape}, freqs: ({len(freqs)},), '
- f'times: ({len(times)},)')
- if vlim is None:
- vlim = [None, None]
- # convert to numpy array if not already
- freqs = np.array(freqs)
- # frequencies within the band of interest
- # band_mask = np.logical_and(freqs >= event_band[0],
- # freqs <= event_band[1])
- if ax is None:
- fig, ax = plt.subplots(1, 1)
- else:
- fig = ax.get_figure()
- # plot tfr
- im = ax.pcolormesh(times, freqs, tfr, cmap='jet', vmin=vlim[0],
- vmax=vlim[1], shading='nearest')
- fig.colorbar(im, ax=ax)
- ax.axhline(y=event_band[0], c='w', linewidth=2., linestyle=':', alpha=.7)
- ax.axhline(y=event_band[1], c='w', linewidth=2., linestyle=':', alpha=.7)
- ax.set_yticks([freqs[0], event_band[0], event_band[1], freqs[-1]])
- ax.set_xlim(times[0], times[-1])
- # overlay with timecourse
- # twin axis needs to be created and yticks set regardless of if a
- # timeseries is provided to ensure consistency with seaborn tick formatting
- ax_twin = ax.twinx()
- ax_twin.set_yticks([])
- if timeseries is not None:
- ax_twin.plot(times, timeseries, 'w', linewidth=1., alpha=0.8)
- if ylim_ts is not None:
- ax_twin.set_ylim(ylim_ts[0], ylim_ts[1])
- # plot event locations
- if spec_events is not None:
- event_times = [event['Peak Time'] for event in spec_events]
- event_freqs = [event['Peak Frequency'] for event in spec_events]
- # reverse sigmoid: make scatter markers more transparent when there are
- # more of them
- alpha = lambda x : (0.6 + 0.4 * np.exp(-0.2 * (x - 30)) # noqa
- / (1 + np.exp(-0.2 * (x - 30)))) # noqa
- ax.scatter(event_times, event_freqs, s=20, c='w', marker='x',
- alpha=alpha(len(spec_events)))
- # add label on top of spectrogram plot
- if label is not None:
- ax.annotate(label, xy=(0.01, 0.98), xycoords='axes fraction',
- va='top', ha='left', color='w', size=10, fontweight='bold')
- return fig
- def plot_avg_spectrogram(tfr, times, freqs, event_band, spec_events=None,
- timeseries=None, example_epochs=None, vlim=None,
- show_events=False):
- '''Plot the average spectrogram over all TFR epochs/trials.
- Parameters
- ----------
- tfr : array, shape (n_epochs, n_freqs, n_times)
- A stack of time-frequency response (TFR) epochs/trials.
- times : array-like, shape (n_times,)
- Time domain of each TFR in seconds.
- freqs : array-like, shape (n_freqs,)
- Frequency domain of each TFR in Hertz.
- event_band : list
- Lower and upper bounds (inclusive, Hz) of the frequency band-of-
- interest in which spectral events were detected.
- spec_events : list of list of dict
- A nested list with n_epochs elements in the outer list and n_events in
- the inner list. Each inner element comprises an event that is
- characterized by dictionary items including it's location in time,
- location in frequency, duration, and frequency span, and more. If None,
- no events are plotted.
- timeseries : array, shape (n_epochs, n_times) | None
- The stack of timeseries associated with the TFR epochs/trials.
- example_epochs : list | None
- The epoch/trial indices that will be used to plot example spectrogram
- trials alonside the average spectrogram.
- vlim : list, shape (2,) | None
- If not None, sets the colorbar lower and upper bounds for the
- spectrogram across all axes of the returned figure. If None, each
- spectrogram (including example epochs) sets its vlim independently.
- show_events : boolean
- Whether or not to overlay spectral event locations on the average
- spectrogram.
- Return
- ------
- fig : Matplotlib.figure.Figure instance
- The Figure instance containing the average spectrogram.
- '''
- if len(tfr.shape) != 3:
- raise ValueError(f'tfr should be a 3D array of shape (n_epochs, '
- f'n_freqs, n_times), got {tfr.shape}.')
- if example_epochs is not None:
- trial_idx_set = set(range(tfr.shape[0]))
- trial_idx_subset = set(example_epochs)
- if trial_idx_subset.intersection(trial_idx_set) != trial_idx_subset:
- raise ValueError('One or more of the specified example trial '
- 'indices does not exist in the provided tfr '
- 'array.')
- else:
- # set to empty list
- example_epochs = list()
- if show_events:
- spec_events_agg = sum(spec_events, [])
- else:
- spec_events_agg = None
- fig, axs = plt.subplots(nrows=len(example_epochs) + 1, ncols=1,
- sharex=True)
- # if example_epochs is None, ensure that axs is still subscriptable
- axs = np.atleast_1d(axs)
- # plot trial-average tfr
- tfr_avg = np.mean(tfr, axis=0).squeeze()
- plot_events(tfr=tfr_avg, times=times, freqs=freqs,
- event_band=event_band, spec_events=spec_events_agg,
- ax=axs[0], vlim=vlim, label='epoch avg.')
- # plot tfr + events for example trials
- if timeseries is not None and example_epochs is not None:
- max_ts_amplitude = np.max(timeseries[example_epochs])
- min_ts_amplitude = np.min(timeseries[example_epochs])
- ylim_ts = [max_ts_amplitude, min_ts_amplitude]
- for plot_idx, trial_idx in enumerate(example_epochs):
- # get spectral events for the current trial
- if spec_events is not None:
- trial_events = spec_events[trial_idx]
- else:
- trial_events = None
- # plot trial tfr
- tfr_trial = tfr[trial_idx, :, :].squeeze()
- timeseries_trial = timeseries[trial_idx, :]
- plot_events(tfr=tfr_trial, times=times, freqs=freqs,
- event_band=event_band, spec_events=trial_events,
- timeseries=timeseries_trial, ax=axs[plot_idx + 1],
- vlim=vlim, ylim_ts=ylim_ts, label=f'epoch {trial_idx}')
- axs[-1].set_xlabel('time (s)')
- axs[0].set_ylabel('freq. (Hz)')
- fig.tight_layout()
- return fig
spectralevents.py at commit cd0c83d, under BSD-3-Clause · at the source
Overview
Abstract
The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.
Repository
Its files are read in the Code ↔ Paper reader above, with 2 matches between paragraphs and lines of code.
jonescompneurolab/SpectralEvents
cd0c83d2492446e03f1e58b79ed31bc73c024f22, 22 July 2024Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
13 files
- __init__.py, Python, 2 lines
- example.m, MATLAB, 38 lines
- spectralevents.m, MATLAB, 115 lines
- spectralevents.py, Python, 601 lines, 1 match
- spectralevents_find.m, MATLAB, 528 lines, 1 match
- spectralevents_ts2tfr.m, MATLAB, 73 lines
- spectralevents_vis.m, MATLAB, 245 lines
- tests/
__init__.py , Python, 1 line - tests/
save_matlab_events.m , MATLAB, 23 lines - tests/
test_event_detection.py , Python, 122 lines - tutorial.ipynb, Jupyter, 306 lines
- LICENSE, License, 28 lines
- README.md, Text, 149 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;
- 11 scripts, each with its path and the digest of its content;
- 2 matches 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
Datasets cited
- doi:10.18112/
openneuro.ds006502.v1.0. , at OpenNeuro; found in the references0 - humanconnectome.org/
software/ , at Human Connectome Project; found in “KEY RESOURCES TABLE”connectome-workbench
Code and data availability statement
The paper has a code and data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:
- it points to a dataset: humanconnectome.org/
software/ connectome-workbench - it says that the data are available on request
- it says that the code is available on request
Read it in the paper: doi.org/10.1016/j.celrep.2026.117845.
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 2, 28 September 2026
- Publisher: n/a → Cell Press
- Authors: added Leonardo G Cohen (0000-0002-1705-8773); removed Leonardo G Cohen
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 5 authors, 12 keywords, 2 funders, 113 references.
Cite
This paper
Iwane, F., Hayward, W., Karunathilake, I. M. D., Buch, E. R., & Cohen, L. G. (2026). Hippocampal skill memory expansion drives online performance dynamics during skill learning. Cell reports, 45(8), 117845. https://
BibTeX
@article{iwane2026hippoc
author = {Iwane, Fumiaki and Hayward, William and Karunathilake, I M Dushyanthi and Buch, Ethan R and Cohen, Leonardo G},
title = {{Hippocampal skill memory expansion drives online performance dynamics during skill learning}},
journal = {Cell reports},
year = {2026},
month = aug,
volume = {45},
number = {8},
pages = {117845},
publisher = {Cell Press},
issn = {2211-1247},
doi = {10.1016/
url = {https://
pmid = {42599800},
pmcid = {PMC13573638}
}
RIS
TY - JOUR
AU - Iwane, Fumiaki
AU - Hayward, William
AU - Karunathilake, I M Dushyanthi
AU - Buch, Ethan R
AU - Cohen, Leonardo G
TI - Hippocampal skill memory expansion drives online performance dynamics during skill learning
T2 - Cell reports
J2 - Cell Rep
PY - 2026
DA - 2026/
VL - 45
IS - 8
SP - 117845
SN - 2211-1247
PB - Cell Press
DO - 10.1016/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1016/
"type": "article-journal",
"title": "Hippocampal skill memory expansion drives online performance dynamics during skill learning",
"container-title": "Cell reports",
"author": [
{
"family": "Iwane",
"given": "Fumiaki"
},
{
"family": "Hayward",
"given": "William"
},
{
"family": "Karunathilake",
"given": "I M Dushyanthi"
},
{
"family": "Buch",
"given": "Ethan R"
},
{
"family": "Cohen",
"given": "Leonardo G"
}
],
"container-title-short":
"volume": "45",
"issue": "8",
"page": "117845",
"DOI": "10.1016/
"PMID": "42599800",
"PMCID": "PMC13573638",
"ISSN": "2211-1247",
"publisher": "Cell Press",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
8,
14
]
]
}
}
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/s41467-026-75345-6 [code]
- Hippocampal ripples initiate cortical dimensionality expansion for memory retrieval.Journal: Nature communicationsIn common: Image Processing Toolbox, cognitive, 9 references
- [2] doi:10.1016/j.isci.2026.116586 [code]
- Condition-specific neural signatures of reactivation during post-retrieval rest: An EEG study.Journal: iScienceIn common: Image Processing Toolbox, SciPy, Matplotlib, 1 other tool, cognitive, 6 references
- [3] doi:10.1038/s41593-026-02362-5 [code]
- Replay of procedural memory is independent of the hippocampus.Journal: Nature neuroscienceIn common: Image Processing Toolbox, SciPy, Matplotlib, 1 other tool, cognitive, 4 references
- [4] doi:10.1002/hbm.70562 [code]
- Distinct Physiological Mechanisms Drive Grey Matter Plasticity in Complex Versus Simple Sequence Learning.Journal: Human brain mappingIn common: SciPy, Matplotlib, NumPy, 5 references
- [5] doi:10.1038/s41593-026-02285-1 [code]
- Fixation duration on natural scenes is explained by memory encoding not processing demand.Journal: Nature neuroscienceIn common: SciPy, Matplotlib, NumPy, MEG, cognitive, 5 references
- [6] doi:10.1162/imag.a.1203 [code]
- Motor cortical areas facilitate schema-mediated integration of new motor information into memory.Journal: Imaging neuroscience (Cambridge, Mass.)In common: cognitive, 6 references
- [7] doi:10.1038/s42003-026-10427-1 [code]
- Phase-tuned modulation during reward expectancy in human anterior insular cortex.Journal: Communications biologyIn common: SciPy, NumPy, 6 references
- [8] doi:10.1038/s41467-026-73818-2 [code]
- Prefrontal parvalbumin neurons mediate working memory in a task demand-dependent manner.Journal: Nature communicationsIn common: SciPy, Matplotlib, NumPy, cognitive, 4 references
- [9] doi:10.1162/imag.a.1359 [code]
- Distinct roles of brain network flexibility in motor learning across age.Journal: Imaging neuroscience (Cambridge, Mass.)In common: 6 references
- [10] doi:10.1002/hbm.70579
- Neural Correlates of Motor Sequence Learning and Enhanced Offline Consolidation in 7-11-Year-Old Children.Journal: Human brain mappingIn common: cognitive, 5 references
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, 11 scripts, and 2 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:963e0e1c285ee254…
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.
