OSCR

The retrieval of previously learned motor memories is facilitated by the reinstatement of default mode network manifold structures.

Code ↔ Paper

13 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 13 matches
  1. [1] § Materials and methods › fMRI preprocessing ↔ saveman/ventral_visual_cleanup.py, lines 1–15 · score 0.77 · defined ventral visual, nuisance regression, contamination, mitigating, signal, centroid
  2. [2] § Materials and methods › Behavioral data analysis ↔ saveman/analyses/behaviour.py, lines 675–812 · score 0.74 · baseline corrected eccentricity, Recall Ratio, angular error, permutation, behavioral, Score
  3. [3] § Materials and methods › Neuroimaging data analysis ↔ saveman/connectivity.py, lines 251–296 · score 0.63 · covariance matrix, connectivity matrices, transported, tangent, grand, Riemannian
  4. [4] § Materials and methods › fMRI preprocessing ↔ saveman/ventral_visual_cleanup.py, lines 152–244 · score 0.63 · ventral visual parcels, cerebellar parcels, regressed, PCA
  5. [5] § Materials and methods › fMRI preprocessing ↔ tutorials/plot_tutorial_02.py, lines 1–52 · score 0.62 · AFNI, FreeSurfer, FSL, fsaverage, surfaces, brain
  6. [6] § Results › Learner heterogeneity reveals different DMN-centric reinstatement profiles in fast versus slow learners ↔ saveman/analyses/behaviour.py, lines 530–564 · score 0.62 · slow learners, trial bins, learning curves, angular error, fast, Behavioral
  7. [7] § Results › Changes in manifold structure relate to individual differences in learning and relearning ↔ saveman/analyses/behaviour.py, lines 530–564 · score 0.61 · slow learner, trial bins, Learning curves, angular error, fast, behavioral
  8. [8] § Materials and methods › Behavioral data analysis ↔ saveman/analyses/fpca.py, lines 269–310 · score 0.59 · trial bins, learning curve, angular error, behavioral
  9. [9] § Materials and methods › fMRI preprocessing ↔ tutorials/plot_tutorial_02.py, lines 1–52 · score 0.58 · Brain surfaces, FreeSurfer, FSL, workflow
  10. [10] § Results › Changes in manifold structure relate to individual differences in learning and relearning ↔ saveman/analyses/behaviour.py, lines 675–812 · score 0.56 · Recall Ratio, behavioral metric, fPCA, Score, correlation, error
  11. [11] § Materials and methods › fMRI preprocessing ↔ tutorials/plot_tutorial_01.py, lines 49–95 · score 0.55 · FreeSurfer, FSL, variable, volume, surfaces
  12. [12] § Materials and methods › Behavioral data analysis ↔ saveman/analyses/fpca.py, lines 76–159 · score 0.55 · angular error, fPCs, spline, FPC1, fPCA, variance
  13. [13] § Results › Manifold structure during baseline trials ↔ saveman/analyses/reference.py, lines 44–63 · score 0.52 · cumulative variance explained, principal components, PCs, bars

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 · 815 lines · 31 KB · MIT · 4 matches

  1. """Behaviour analyses for the motor adaptation task."""
  2. from __future__ import annotations
  3. import os
  4. from typing import Optional, Tuple
  5. import numpy as np
  6. import pandas as pd
  7. import matplotlib.pyplot as plt
  8. import seaborn as sns
  9. from scipy.stats import pearsonr
  10. import pingouin as pg
  11. from matplotlib.colors import ListedColormap
  12. from matplotlib import gridspec
  13. from sklearn.decomposition import PCA
  14. from neuromaps import nulls
  15. from neuromaps.datasets import fetch_atlas
  16. from neuromaps import stats # for efficient_pearsonr (optional, used elsewhere)
  17. from saveman.config import Config
  18. from saveman.utils import get_surfaces, parse_roi_names
  19. from saveman.analyses import plotting
  20. from surfplot import Plot
  21. import cmasher as cmr
  22. # ----------------------------
  23. # Small helpers
  24. # ----------------------------
  25. def _ensure_dir(path: str) -> None:
  26. os.makedirs(path, exist_ok=True)
  27. def _savefig(fig: plt.Figure, path_no_ext: str, dpi: int = 300) -> None:
  28. """Save a figure robustly as PNG and close it."""
  29. out = path_no_ext if path_no_ext.lower().endswith("") else (path_no_ext + "")
  30. fig.savefig(out, dpi=dpi, bbox_inches="tight")
  31. plt.close(fig)
  32. def convert_pvalue_to_asterisks(pvalue: float) -> str:
  33. if pvalue <= 0.0001:
  34. return "****"
  35. if pvalue <= 0.001:
  36. return "***"
  37. if pvalue <= 0.01:
  38. return "**"
  39. if pvalue <= 0.05:
  40. return "*"
  41. return "ns"
  42. # ----------------------------
  43. # Core plotting
  44. # ----------------------------
  45. def task_behaviour_plot(data: pd.DataFrame, fig_dir: str) -> None:
  46. """Plot group-average error throughout the task (3 styles).
  47. Parameters
  48. ----------
  49. data
  50. Trial-wise behavioural data including columns: sub, ses, trial_bin, error.
  51. Units should already be in degrees if you want degree-scaled plots.
  52. fig_dir
  53. Output directory for figures.
  54. """
  55. _ensure_dir(fig_dir)
  56. color = ["darkcyan", "#FFA066"]
  57. max_trial_bin = int(data["trial_bin"].max())
  58. num_sub = int(len(data["sub"].unique()))
  59. data3 = data.copy()
  60. data3["trial_bin"] = np.tile(np.arange(1, 2 * max_trial_bin + 1), num_sub)
  61. fig, ax = plt.subplots(figsize=(12, 4))
  62. sns.lineplot(
  63. x="trial_bin",
  64. y="error",
  65. data=data3,
  66. hue="ses",
  67. palette=color,
  68. errorbar=("ci", 68),
  69. linewidth=1.5,
  70. ax=ax,
  71. )
  72. ax.set_yticks(np.arange(-20, 50, 10))
  73. ax.axhline(0, lw=1, c="k", ls="--")
  74. ax.set_xlabel("Trial Bin", fontsize=12, fontweight="bold")
  75. ax.set_ylabel("Angular error (°)", fontsize=12, fontweight="bold")
  76. sns.despine()
  77. fig.tight_layout()
  78. _savefig(fig, os.path.join(fig_dir, "task_plot"))
  79. def plot_behav_distribution(
  80. df: pd.DataFrame,
  81. fig_dir: str,
  82. name: str = "early",
  83. angle: str = "hitAngle_hand_good",
  84. auto_ticks: bool = False,
  85. seed: int = 1,
  86. ) -> None:
  87. """Show sample distribution (box + jittered points).
  88. Parameters
  89. ----------
  90. df
  91. DataFrame containing the column in `angle`.
  92. fig_dir
  93. Output directory for figures.
  94. name
  95. Label used for axis title and filename.
  96. angle
  97. Column name to plot.
  98. auto_ticks
  99. If True, suppress y-ticks.
  100. seed
  101. RNG seed for jitter reproducibility.
  102. """
  103. _ensure_dir(fig_dir)
  104. if angle not in df.columns:
  105. raise ValueError(f"Column '{angle}' not found in df.")
  106. plot_df = df.copy()
  107. plot_df["x"] = 1
  108. ymin = 15 * (np.nanmin(plot_df[angle]) // 15)
  109. ymax = 15 * (np.nanmax(plot_df[angle]) // 15)
  110. fig, ax = plt.subplots(figsize=(2, 4))
  111. box_line_color = "k"
  112. sns.boxplot(
  113. x="x",
  114. y=angle,
  115. data=plot_df,
  116. color="silver",
  117. boxprops=dict(edgecolor=box_line_color),
  118. medianprops=dict(color=box_line_color),
  119. whiskerprops=dict(color=box_line_color),
  120. capprops=dict(color=box_line_color),
  121. showfliers=False,
  122. width=0.5,
  123. ax=ax,
  124. )
  125. cmap = cmr.get_sub_cmap("RdYlGn", 0.05, 0.9)
  126. rng = np.random.default_rng(seed)
  127. jitter = rng.uniform(0.01, 0.4, len(plot_df))
  128. ax.scatter(
  129. x=plot_df["x"] + jitter,
  130. y=plot_df[angle],
  131. c=plot_df[angle],
  132. ec="k",
  133. linewidths=1,
  134. cmap=cmap,
  135. clip_on=False,
  136. )
  137. if auto_ticks:
  138. ax.set(xticks=[])
  139. else:
  140. ax.set(xticks=[], yticks=np.arange(ymin, ymax + 15, 15))
  141. ax.set_xlabel(" ")
  142. ax.set_ylabel(name, fontsize=12, fontweight="bold")
  143. sns.despine(bottom=True)
  144. fig.tight_layout()
  145. _savefig(fig, os.path.join(fig_dir, f"{name}_error_distribution"))
  146. def plot_early_error_change(df: pd.DataFrame, fig_dir: str) -> None:
  147. """Bar + paired lines for Day1 vs Day2 early error (paired test)."""
  148. _ensure_dir(fig_dir)
  149. data1 = df[["sub", "D1 early", "Savings"]].rename(columns={"D1 early": "error"})
  150. data1["day"] = 1
  151. data2 = df[["sub", "D2 early", "Savings"]].rename(columns={"D2 early": "error"})
  152. data2["day"] = 2
  153. data = pd.concat([data1, data2], axis=0)
  154. res = pg.pairwise_tests(data=data, dv="error", within="day", subject="sub")
  155. pval = float(res["p-unc"].values[0])
  156. pval_asterisks = convert_pvalue_to_asterisks(pval)
  157. fig, ax = plt.subplots(figsize=(4, 4))
  158. sns.barplot(
  159. data=data,
  160. x="day",
  161. y="error",
  162. hue="day",
  163. palette=["#80ffea", "#FFA066"],
  164. ax=ax,
  165. fill=True,
  166. alpha=0.4,
  167. lw=2,
  168. )
  169. sns.lineplot(
  170. data=data,
  171. x="day",
  172. y="error",
  173. units="sub",
  174. estimator=None,
  175. color="k",
  176. alpha=0.5,
  177. ax=ax,
  178. )
  179. ax.set_xticks([1, 2])
  180. ax.set_xticklabels(["Day1 Early", "Day2 Early"], fontsize=12, fontweight="bold")
  181. ax.set_ylabel("Angular error", fontsize=12, fontweight="bold")
  182. ax.set_xlabel("")
  183. ax.set_title(pval_asterisks, fontsize=16, fontweight="bold")
  184. sns.despine()
  185. for bar, c in zip(ax.patches, ["darkcyan", "#FFA066"]):
  186. bar.set_edgecolor(c)
  187. bar.set_linewidth(2)
  188. fig.tight_layout()
  189. _savefig(fig, os.path.join(fig_dir, "task_Day1Day2_early_error"))
  190. # ----------------------------
  191. # Eccentricity-behaviour correlation maps
  192. # ----------------------------
  193. def correlation_map(
  194. score: pd.DataFrame,
  195. gradients: pd.DataFrame,
  196. name: str,
  197. fig_dir: str,
  198. sig_style: Optional[str] = None,
  199. lateral_only: bool = False,
  200. angle: str = "hitAngle_hand_good",
  201. plot_maps: bool = True,
  202. ) -> pd.DataFrame:
  203. """Correlate a behavioural score with region eccentricity and plot a surface map.
  204. Parameters
  205. ----------
  206. score
  207. DataFrame with columns ['sub', <angle>], where <angle> is the score column.
  208. gradients
  209. DataFrame with columns ['sub', 'roi', 'roi_ix', 'distance', ...].
  210. name
  211. Prefix for outputs.
  212. fig_dir
  213. Output directory for maps.
  214. sig_style
  215. None (default): plot full r-map with FDR outline.
  216. 'uncorrected': outline uncorrected p<.05.
  217. 'corrected': show only FDR-significant r values.
  218. lateral_only
  219. If True, plot only lateral view for the main map panel.
  220. angle
  221. Column in `score` to correlate against.
  222. plot_maps
  223. If False, compute correlations but skip plotting.
  224. Returns
  225. -------
  226. pd.DataFrame
  227. ROI-wise correlation results with columns ['r', 'p', 'p_fdr'] indexed by ROI.
  228. """
  229. _ensure_dir(fig_dir)
  230. if angle not in score.columns:
  231. raise ValueError(f"Column '{angle}' not found in score DataFrame.")
  232. data = (
  233. gradients[["sub", "roi", "roi_ix", "distance"]]
  234. .pivot(index="sub", columns="roi", values="distance")
  235. )
  236. # preserve original ROI order
  237. data = data[gradients["roi"].unique().tolist()]
  238. # defensive alignment
  239. if not np.array_equal(score["sub"].values, data.index.values):
  240. raise ValueError("Subject ordering mismatch between score and gradients pivot.")
  241. res = data.apply(lambda x: pearsonr(score[angle], x), axis=0)
  242. res = res.T.rename(columns={0: "r", 1: "p"})
  243. _, res["p_fdr"] = pg.multicomp(res["p"].values, method="fdr_bh")
  244. if not plot_maps:
  245. return res
  246. # cortex-only (first 400 ROIs in this project’s ordering)
  247. res_cortex = res.iloc[:400, :].copy()
  248. config = Config()
  249. rvals = plotting.weights_to_vertices(res_cortex["r"], config.atlas)
  250. p_unc = plotting.weights_to_vertices(res_cortex["p"], config.atlas)
  251. p_unc = np.where(p_unc < 0.05, 1, 0)
  252. p_fdr = plotting.weights_to_vertices(res_cortex["p_fdr"], config.atlas)
  253. qvals = np.where(p_fdr < 0.05, 1, 0)
  254. surfaces = get_surfaces()
  255. sulc = plotting.get_sulc()
  256. sulc_params = dict(data=sulc, cmap="gray", cbar=False)
  257. vmax = float(np.nanmax(np.abs(res_cortex["r"])))
  258. cmap = ListedColormap(
  259. np.genfromtxt(os.path.join(config.resources, "colormap.csv"), delimiter=",")
  260. )
  261. if lateral_only:
  262. p1 = Plot(surfaces["lh"], surfaces["rh"], views="lateral", layout="column", size=(250, 350), zoom=1.5)
  263. else:
  264. p1 = Plot(surfaces["lh"], surfaces["rh"])
  265. p2 = Plot(surfaces["lh"], surfaces["rh"], views="dorsal", size=(150, 200), zoom=3.3)
  266. p3 = Plot(surfaces["lh"], surfaces["rh"], views="posterior", size=(150, 200), zoom=3.3)
  267. for p, suffix in zip([p1, p2, p3], ["", "_dorsal", "_posterior"]):
  268. p.add_layer(**sulc_params)
  269. cbar = True if suffix == "_dorsal" else False
  270. if sig_style is None:
  271. p.add_layer(rvals, cbar=cbar, cmap=cmap, color_range=(-vmax, vmax))
  272. p.add_layer((np.nan_to_num(rvals * qvals) != 0).astype(float), cbar=False, as_outline=True, cmap="viridis")
  273. elif sig_style == "uncorrected":
  274. p.add_layer(rvals, cbar=cbar, cmap=cmap, color_range=(-vmax, vmax))
  275. p.add_layer((np.nan_to_num(rvals * p_unc) != 0).astype(float), cbar=False, as_outline=True, cmap="binary")
  276. elif sig_style == "corrected":
  277. x = rvals * qvals
  278. vmin = float(np.nanmin(x[np.abs(x) > 0])) if np.any(np.abs(x) > 0) else -vmax
  279. p.add_layer(x, cbar=cbar, cmap=cmr.get_sub_cmap(cmap, 0.66, 1), color_range=(vmin, vmax))
  280. p.add_layer((np.nan_to_num(rvals * qvals) != 0).astype(float), cbar=False, as_outline=True, cmap="binary")
  281. else:
  282. raise ValueError("sig_style must be one of {None, 'uncorrected', 'corrected'}")
  283. if suffix == "_dorsal":
  284. cbar_kws = dict(location="bottom", decimals=2, fontsize=10, n_ticks=2, shrink=0.4, aspect=4, draw_border=False, pad=0.05)
  285. fig = p.build(cbar_kws=cbar_kws)
  286. else:
  287. fig = p.build()
  288. suffix_out = suffix + ("_corr" if sig_style is None else "")
  289. _savefig(fig, os.path.join(fig_dir, f"{name}_correlation_map{suffix_out}"))
  290. return res
  291. def network_correlation_analysis(gradients: pd.DataFrame, score: pd.DataFrame, angle: str = "hitAngle_hand_good") -> Tuple[pd.DataFrame, pd.DataFrame]:
  292. """Network-level correlation between average eccentricity and behaviour."""
  293. data = gradients.copy()
  294. network_ecc = data.groupby(["sub", "network"])["distance"].mean().reset_index()
  295. data = network_ecc.merge(score, on="sub", how="left")
  296. res = data.groupby(["network"]).apply(lambda x: pg.corr(x["distance"], x[angle]), include_groups=False)
  297. res["p_fdr"] = pg.multicomp(res["p-val"].values, method="fdr_bh")[1]
  298. return res, data
  299. def permute_maps(
  300. data: pd.DataFrame,
  301. parc,
  302. atlas: str = "fsLR",
  303. density: str = "32k",
  304. n_perm: int = 1000,
  305. seed: int = 1234,
  306. p_thresh: float = 0.05,
  307. ) -> Tuple[pd.DataFrame, pd.DataFrame]:
  308. """Perform spin permutations on parcellated correlation data and aggregate by network."""
  309. config = Config()
  310. lh_gii = os.path.join(config.resources, "lh_relabeled.gii")
  311. rh_gii = os.path.join(config.resources, "rh_relabeled.gii")
  312. data = data.reset_index().rename(columns={"index": "roi"})
  313. data["roi_ix"] = np.arange(1, 465).astype(int)
  314. data = parse_roi_names(data)
  315. data = data.iloc[:400, :] # cortex only
  316. surfaces = fetch_atlas(atlas, density)["sphere"]
  317. y = np.asarray(data["r"].values)
  318. spins = nulls.vasa(
  319. data=y,
  320. parcellation=(lh_gii, rh_gii),
  321. n_perm=n_perm,
  322. seed=seed,
  323. surfaces=surfaces,
  324. )
  325. spins_df = pd.concat([data, pd.DataFrame(spins)], axis=1)
  326. network_data = (
  327. spins_df.groupby(["network"]).agg("mean", numeric_only=True).reset_index().drop(columns="roi_ix")
  328. )
  329. nulls_dist = network_data.iloc[:, -n_perm:].values
  330. rvals = network_data["r"].values
  331. pvals = np.array([np.mean(np.abs(nulls_dist[i, :]) >= np.abs(rvals[i])) for i in range(len(rvals))])
  332. p_adj = pg.multicomp(pvals, method="fdr_bh")[1]
  333. significant_networks = p_adj < p_thresh
  334. network_data.insert(4, "pspin", pvals)
  335. network_data.insert(5, "pspin_fdr", p_adj)
  336. network_data.insert(6, "sig", significant_networks.astype(int))
  337. return spins_df, network_data
  338. def plot_behav_corr(
  339. data1: pd.DataFrame,
  340. data2: pd.DataFrame,
  341. prefix1: str,
  342. prefix2: str,
  343. out_dir: str,
  344. linecolor: str = "k",
  345. auto_xticks: bool = True,
  346. auto_yticks: bool = True,
  347. ) -> None:
  348. """Scatterplot correlation between two subject-level measures."""
  349. _ensure_dir(out_dir)
  350. data = pd.merge(data1, data2, on="sub", how="left")
  351. rval, pval = pearsonr(data.iloc[:, -2], data.iloc[:, -1])
  352. fig = sns.lmplot(
  353. x=data.columns[-2],
  354. y=data.columns[-1],
  355. data=data,
  356. scatter_kws={"color": "k", "clip_on": False},
  357. line_kws={"color": linecolor},
  358. facet_kws={"sharex": True},
  359. height=4,
  360. aspect=0.8,
  361. )
  362. if not auto_xticks:
  363. plt.xticks([0, 15, 30, 45])
  364. if not auto_yticks:
  365. plt.yticks([0, 15, 30, 45])
  366. plt.xlabel(prefix1, fontsize=12, fontweight="bold")
  367. plt.ylabel(prefix2, fontsize=12, fontweight="bold")
  368. plt.title(f"r = {rval: .2f}\np = {pval: .4f}", ha="left", fontsize=12, fontweight="bold")
  369. fig.tight_layout()
  370. out = os.path.join(out_dir, f"behavior_{prefix1}_{prefix2}_correlation")
  371. plt.savefig(out, dpi=300, bbox_inches="tight")
  372. plt.close()
  373. # ----------------------------
  374. # Behavioural metrics
  375. # ----------------------------
  376. def behav_metrics(sub_behav: pd.DataFrame, num_bins: int = 6) -> pd.DataFrame:
  377. """Compute epoch-level metrics and summary behavioural scores.
  378. Returns a wide subject table with columns:
  379. D1/D2 early/late, washout, Savings, SavingsRelative, PC1/PC2, etc.
  380. """
  381. epochs = ["early", "late", "washout-early", "washout-late"]
  382. data = sub_behav.copy()
  383. data["epoch"] = ""
  384. mask = data["trial_bin"] <= num_bins
  385. data.loc[mask, "epoch"] = "base"
  386. mask = (15 < data["trial_bin"]) & (data["trial_bin"] <= 15 + num_bins)
  387. data.loc[mask, "epoch"] = "early"
  388. mask = (56 - num_bins <= data["trial_bin"]) & (data["trial_bin"] < 56)
  389. data.loc[mask, "epoch"] = "late"
  390. mask = (56 <= data["trial_bin"]) & (data["trial_bin"] < 56 + num_bins)
  391. data.loc[mask, "epoch"] = "washout-early"
  392. mask = (70 - num_bins < data["trial_bin"]) & (data["trial_bin"] <= 70)
  393. data.loc[mask, "epoch"] = "washout-late"
  394. res = (
  395. data.groupby(["sub", "epoch", "ses"])["error"]
  396. .mean()
  397. .reset_index()
  398. .query("epoch in @epochs")
  399. )
  400. res = (
  401. res.pivot(index="sub", columns=["epoch", "ses"], values="error")
  402. .set_axis(
  403. ["D1 early", "D2 early", "D1 late", "D2 late", "WO1 early", "WO2 early", "WO1 late", "WO2 late"],
  404. axis=1,
  405. )
  406. )
  407. res["Savings"] = res["D1 early"] - res["D2 early"]
  408. res["SavingsRelative"] = (((res["D1 early"].abs()) - (res["D2 early"].abs())) / (res["D1 early"].abs())) * 100
  409. pca = PCA(n_components=2)
  410. PCs = pca.fit_transform(res)
  411. res["PC1"] = -PCs[:, 0]
  412. res["PC2"] = PCs[:, 1]
  413. # optional transformations used in downstream plots
  414. res["WO1 early"] = res["WO1 early"].abs()
  415. res["WO2 early"] = res["WO2 early"].abs()
  416. return res.reset_index()
  417. def add_recall_ratio(df: pd.DataFrame, rotation_deg: float = 45.0, eps: float = 1e-6, min_learned_deg: float = 5.0) -> pd.DataFrame:
  418. """Tsay-style recall ratio using adaptation magnitude rather than error."""
  419. e1 = df["D1 late"].abs()
  420. e2 = df["D2 early"].abs()
  421. adapt_d1_late = np.clip(rotation_deg - e1, 0.0, rotation_deg)
  422. adapt_d2_early = np.clip(rotation_deg - e2, 0.0, rotation_deg)
  423. df["Adapt_D1Late"] = adapt_d1_late
  424. df["Adapt_D2Early"] = adapt_d2_early
  425. rr = adapt_d2_early / (adapt_d1_late + eps)
  426. if min_learned_deg is not None:
  427. rr = rr.where(adapt_d1_late >= min_learned_deg, np.nan)
  428. df["RecallRatio"] = rr
  429. return df
  430. def add_learner_groups(df: pd.DataFrame, metric: str = "FPC1") -> pd.DataFrame:
  431. """Add fast vs slow learner grouping by median split on a behavioural metric."""
  432. if metric not in df.columns:
  433. raise ValueError(f"Metric '{metric}' not found in df.columns")
  434. median_val = df[metric].median()
  435. df["LearnerGroup"] = np.where(df[metric] >= median_val, "fast", "slow")
  436. return df
  437. def plot_learning_curves_by_group(sub_behav: pd.DataFrame, fig_dir: str, name: str = "task_plot_fast_vs_slow") -> None:
  438. """Plot group-average error across the task for fast vs slow learners."""
  439. _ensure_dir(fig_dir)
  440. fig, axs = plt.subplots(1, 2, figsize=(8, 4))
  441. data = sub_behav.query("ses == 'ses-01'")
  442. ax = axs[0]
  443. sns.lineplot(
  444. data=data,
  445. x="trial_bin",
  446. y="error",
  447. hue="LearnerGroup",
  448. errorbar=("ci", 68),
  449. linewidth=1.5,
  450. ax=ax,
  451. )
  452. ax.axhline(0, lw=1, c="k", ls="--")
  453. ax.set_xlabel("Trial bin", fontsize=12, fontweight="bold")
  454. ax.set_ylabel("Angular error (°)", fontsize=12, fontweight="bold")
  455. data = sub_behav.query("ses == 'ses-02'")
  456. ax = axs[1]
  457. sns.lineplot(
  458. data=data,
  459. x="trial_bin",
  460. y="error",
  461. hue="LearnerGroup",
  462. errorbar=("ci", 68),
  463. linewidth=1.5,
  464. ax=ax,
  465. )
  466. ax.axhline(0, lw=1, c="k", ls="--")
  467. ax.set_xlabel("Trial bin", fontsize=12, fontweight="bold")
  468. ax.set_ylabel("Angular error (°)", fontsize=12, fontweight="bold")
  469. sns.despine()
  470. fig.tight_layout()
  471. _savefig(fig, os.path.join(fig_dir, name))
  472. def plot_region_correlations(epoch_gradients, score, fig_dir, epoch, col='FPC1', suffix=''):
  473. """Plot scatterplot for exemplar regions
  474. Parameters
  475. ----------
  476. gradients : pd.DataFrame
  477. Subject-level gradients dataset with distance column
  478. error : _type_
  479. Subject-level median error data
  480. """
  481. # pre-determined regions
  482. rois = {
  483. 'ses-01_early-corrected': ['7Networks_RH_Default_pCunPCC_5'],
  484. 'ses-02_early-corrected': ['7Networks_RH_Default_PFCdPFCm_3'],
  485. 'ses-01_WOearly-corrected': ['7Networks_RH_Default_pCunPCC_5'],
  486. 'ses-02_WOearly-corrected': ['7Networks_RH_Default_PFCdPFCm_3']
  487. }
  488. cmap = plotting.yeo_cmap(networks=7)
  489. roi = rois[epoch]
  490. df = epoch_gradients.query("roi in @roi")
  491. df = df.merge(score, left_on='sub', right_on='sub')
  492. df['roi'] = df['roi'].str.replace('7Networks_', '')
  493. g = sns.lmplot(x='distance', y=col, col='roi', data=df, hue='network',
  494. scatter_kws={'clip_on': False}, palette=cmap, legend=False,
  495. facet_kws={'sharex': False}, height=2.3, aspect=.8, )
  496. g.set_xlabels('Eccentricity')
  497. g.set_ylabels('FPCA score')
  498. g.set(ylim=(-3, 2), yticks=np.arange(-3, 3, 1))
  499. g.tight_layout()
  500. g.savefig(os.path.join(fig_dir, f'{epoch}_example_roi_correlations{suffix}'))
  501. def plot_savings_by_group(df: pd.DataFrame, fig_dir: str, metric: str = "Savings") -> None:
  502. """Compare savings between fast and slow learners."""
  503. _ensure_dir(fig_dir)
  504. fig, ax = plt.subplots(figsize=(4, 4))
  505. sns.boxplot(
  506. data=df,
  507. x="LearnerGroup",
  508. y=metric,
  509. palette=["red", "green"],
  510. hue="LearnerGroup",
  511. showfliers=False,
  512. ax=ax,
  513. )
  514. sns.stripplot(data=df, x="LearnerGroup", y=metric, color="k", alpha=0.7, ax=ax)
  515. ax.set_xlabel("")
  516. ax.set_ylabel(metric, fontsize=12, fontweight="bold")
  517. sns.despine()
  518. fig.tight_layout()
  519. _savefig(fig, os.path.join(fig_dir, f"{metric}_fast_vs_slow"))
  520. def plot_permute_maps(data, out_dir, n_perm=1000, p_thresh=.05):
  521. """
  522. Plots permutation test results for brain network correlations.
  523. Args:
  524. data (pd.DataFrame): DataFrame containing neuroimaging stats.
  525. Expected columns include:
  526. - 'network': Names of the brain networks (e.g., Yeo 7 networks).
  527. - 'r': Observed correlation values.
  528. - 'pspin_fdr': FDR-corrected p-values from permutation testing.
  529. - Last n_perm columns: Null distribution values for each network.
  530. out_dir (str): Path (including filename and extension) where the
  531. resulting figure will be saved.
  532. n_perm (int, optional): Number of permutation columns to include from
  533. the end of the DataFrame. Defaults to 1000.
  534. p_thresh (float, optional): Significance threshold for coloring observed
  535. points. Points <= p_thresh are red; otherwise blue. Defaults to 0.05.
  536. Returns:
  537. None: The figure is saved to the specified directory.
  538. """
  539. rvals = data['r'].values
  540. pspin_fdr = data['pspin_fdr'].values
  541. # Scale and prepare null distribution data
  542. nulls = pd.DataFrame(1.8 * data.iloc[:, -n_perm:].values)
  543. nulls_dist = pd.concat([data.iloc[:, 0], nulls], axis=1)
  544. melted_data = pd.melt(pd.DataFrame(nulls_dist), id_vars='network')
  545. cmap = plotting.yeo_cmap(networks=7)
  546. fig = plt.figure(figsize=(5, 5))
  547. sns.boxplot(data=melted_data, x='network', y='value', whis=[0, 100],
  548. palette=cmap, hue='network', saturation=.8, width=.5)
  549. # Overlay real correlation values as points
  550. for i, r in enumerate(rvals):
  551. # Color based on FDR-corrected p-values
  552. color = 'red' if pspin_fdr[i] <= p_thresh else 'blue'
  553. plt.plot(i, r, color=color, marker='o', markersize=5, zorder=5)
  554. plt.axhline(0, color='blue', linestyle='dashed', zorder=-1)
  555. plt.grid(axis='x', linestyle='--', alpha=0.5)
  556. plt.xticks(range(len(data)), data['network'], rotation=90,
  557. ha='center', fontsize=12, fontweight='bold')
  558. plt.tick_params(axis='x', which='both', bottom=False, top=False)
  559. sns.despine(bottom=True)
  560. plt.xlabel('')
  561. plt.ylabel('Correlation (r)', fontsize=12, fontweight='bold')
  562. plt.tight_layout()
  563. fig.savefig(out_dir)
  564. # ----------------------------
  565. # Script entry
  566. # ----------------------------
  567. def main() -> None:
  568. config = Config()
  569. # Apply lab plotting style only when running as a script
  570. plotting.set_plotting()
  571. fig_dir = os.path.join(config.figures, "behaviour")
  572. _ensure_dir(fig_dir)
  573. sub_behav = pd.read_csv(os.path.join(config.resources, "subject_behavior_bin.csv"))
  574. sub_behav["error"] = (sub_behav["error"] * 180) / np.pi # radians -> degrees
  575. num_bins = 6
  576. df = behav_metrics(sub_behav, num_bins)
  577. fpca_score = pd.read_table(os.path.join(config.results, "fpca", "D1D2-17bases_angular_error_bin.tsv"))
  578. df = pd.merge(df, fpca_score, on="sub", how="left")
  579. df = add_recall_ratio(df) # uses D1 late, D2 early
  580. df = add_learner_groups(df, metric="FPC1") # or 'Savings'
  581. sub_behav = pd.merge(sub_behav, df[["sub", "LearnerGroup"]], on="sub", how="left")
  582. df.to_csv(os.path.join(config.results, f"behav_bin-{num_bins}bins.csv"), index=False)
  583. task_behaviour_plot(sub_behav, fig_dir)
  584. df["D1D2 early"] = df[["D1 early", "D2 early"]].mean(axis=1)
  585. plot_early_error_change(df, fig_dir)
  586. gradients = pd.read_table(os.path.join(config.results, "subject_gradients.tsv"))
  587. # Baseline eccentricity per subject, per ROI
  588. day1base = gradients.query('epoch == "base" & ses == "ses-01"')[["sub", "roi", "distance"]]
  589. day2base = gradients.query('epoch == "base" & ses == "ses-02"')[["sub", "roi", "distance"]]
  590. # Behavioural distributions & relationships
  591. plot_learning_curves_by_group(sub_behav, fig_dir)
  592. plot_savings_by_group(df, fig_dir)
  593. for col in ["FPC1", "RecallRatio"]:
  594. plot_behav_distribution(df[["sub", col]], fig_dir, name=col, angle=col, auto_ticks=True)
  595. plot_behav_corr(df[["sub", "D1 early"]], df[["sub", "D2 early"]], "D1 early", "D2 early", fig_dir, auto_xticks=False, auto_yticks=False)
  596. plot_behav_corr(df[["sub", "WO1 early"]], df[["sub", "FPC1"]], "abs WO1 early", "FPC1", fig_dir, auto_xticks=False)
  597. plot_behav_corr(df[["sub", "WO2 early"]], df[["sub", "FPC1"]], "abs WO2 early", "FPC1", fig_dir, auto_xticks=False)
  598. plot_behav_corr(df[["sub", "WO1 early"]], df[["sub", "WO2 early"]], "abs WO1 early", "abs WO2 early", fig_dir, auto_xticks=False, auto_yticks=False)
  599. plot_behav_corr(df[["sub", "D1 early"]], df[["sub", "FPC1"]], "D1 early", "FPC1", fig_dir, auto_xticks=False)
  600. plot_behav_corr(df[["sub", "D2 early"]], df[["sub", "FPC1"]], "D2 early", "FPC1", fig_dir, auto_xticks=False)
  601. plot_behav_corr(df[["sub", "Savings"]], df[["sub", "FPC1"]], "Saving", "FPC1", fig_dir)
  602. plot_behav_corr(df[["sub", "Savings"]], df[["sub", "RecallRatio"]], "Saving", "RecallRatio", fig_dir)
  603. plot_behav_corr(df[["sub", "FPC1"]], df[["sub", "RecallRatio"]], "FPC1", "RecallRatio", fig_dir)
  604. # Eccentricity–behaviour correlation maps (baseline-corrected eccentricity)
  605. n_perm = 1000
  606. # Day 1 early
  607. ses = "ses-01"
  608. epoch_gradients = gradients.query('epoch == "early" & ses == @ses').copy()
  609. epoch_gradients = epoch_gradients.merge(day1base, on=["sub", "roi"], how="left", suffixes=("", "_base"))
  610. epoch_gradients["distance"] = epoch_gradients["distance"] - epoch_gradients["distance_base"]
  611. epoch_gradients.drop(columns=["distance_base"], inplace=True)
  612. score = df[["sub", "FPC1"]]
  613. res = correlation_map(score, epoch_gradients, f"{ses}_early-corrected_fpca-score", fig_dir, angle="FPC1", sig_style="uncorrected")
  614. res.reset_index().to_csv(os.path.join(config.results, f"{ses}_early-corrected_fpca-score_correlations.tsv"), index=False, sep="\t")
  615. _, network_spins = permute_maps(res, parc=config.atlas, n_perm=n_perm)
  616. network_spins.to_csv(os.path.join(config.results, f"{ses}_early-corrected_fpca-score_spins.tsv"), index=False, sep="\t")
  617. plot_region_correlations(epoch_gradients, score, fig_dir, epoch='ses-01_early-corrected')
  618. prefix = os.path.join(fig_dir, f'{ses}_early-corrected_fpca-score_permute_maps_boxplot')
  619. plot_permute_maps(network_spins, prefix, n_perm)
  620. # Day 2 early
  621. ses = "ses-02"
  622. epoch_gradients = gradients.query('epoch == "early" & ses == @ses').copy()
  623. epoch_gradients = epoch_gradients.merge(day2base, on=["sub", "roi"], how="left", suffixes=("", "_base"))
  624. epoch_gradients["distance"] = epoch_gradients["distance"] - epoch_gradients["distance_base"]
  625. epoch_gradients.drop(columns=["distance_base"], inplace=True)
  626. res = correlation_map(score, epoch_gradients, f"{ses}_early-corrected_fpca-score", fig_dir, angle="FPC1", sig_style="uncorrected")
  627. res.reset_index().to_csv(os.path.join(config.results, f"{ses}_early-corrected_fpca-score_correlations.tsv"), index=False, sep="\t")
  628. _, network_spins = permute_maps(res, parc=config.atlas, n_perm=n_perm)
  629. network_spins.to_csv(os.path.join(config.results, f"{ses}_early-corrected_fpca-score_spins.tsv"), index=False, sep="\t")
  630. plot_region_correlations(epoch_gradients, score, fig_dir, epoch='ses-02_early-corrected')
  631. prefix = os.path.join(fig_dir, f'{ses}_early-corrected_fpca-score_permute_maps_boxplot')
  632. plot_permute_maps(network_spins, prefix, n_perm)
  633. # Day 1 WOearly
  634. ses = "ses-01"
  635. epoch_gradients = gradients.query('epoch == "washout-early" & ses == @ses').copy()
  636. epoch_gradients = epoch_gradients.merge(day1base, on=["sub", "roi"], how="left", suffixes=("", "_base"))
  637. epoch_gradients["distance"] = epoch_gradients["distance"] - epoch_gradients["distance_base"]
  638. epoch_gradients.drop(columns=["distance_base"], inplace=True)
  639. score = df[["sub", "FPC1"]]
  640. res = correlation_map(score, epoch_gradients, f"{ses}_WOearly-corrected_fpca-score", fig_dir, angle="FPC1", sig_style="uncorrected")
  641. res.reset_index().to_csv(os.path.join(config.results, f"{ses}_WOearly-corrected_fpca-score_correlations.tsv"), index=False, sep="\t")
  642. _, network_spins = permute_maps(res, parc=config.atlas, n_perm=n_perm)
  643. network_spins.to_csv(os.path.join(config.results, f"{ses}_WOearly-corrected_fpca-score_spins.tsv"), index=False, sep="\t")
  644. plot_region_correlations(epoch_gradients, score, fig_dir, epoch='ses-01_WOearly-corrected')
  645. prefix = os.path.join(fig_dir, f'{ses}_WOearly-corrected_fpca-score_permute_maps_boxplot')
  646. plot_permute_maps(network_spins, prefix, n_perm)
  647. # Day 2 WOearly
  648. ses = "ses-02"
  649. epoch_gradients = gradients.query('epoch == "washout-early" & ses == @ses').copy()
  650. epoch_gradients = epoch_gradients.merge(day2base, on=["sub", "roi"], how="left", suffixes=("", "_base"))
  651. epoch_gradients["distance"] = epoch_gradients["distance"] - epoch_gradients["distance_base"]
  652. epoch_gradients.drop(columns=["distance_base"], inplace=True)
  653. res = correlation_map(score, epoch_gradients, f"{ses}_WOearly-corrected_fpca-score", fig_dir, angle="FPC1", sig_style="uncorrected")
  654. res.reset_index().to_csv(os.path.join(config.results, f"{ses}_WOearly-corrected_fpca-score_correlations.tsv"), index=False, sep="\t")
  655. _, network_spins = permute_maps(res, parc=config.atlas, n_perm=n_perm)
  656. network_spins.to_csv(os.path.join(config.results, f"{ses}_WOearly-corrected_fpca-score_spins.tsv"), index=False, sep="\t")
  657. plot_region_correlations(epoch_gradients, score, fig_dir, epoch='ses-02_WOearly-corrected')
  658. prefix = os.path.join(fig_dir, f'{ses}_WOearly-corrected_fpca-score_permute_maps_boxplot')
  659. plot_permute_maps(network_spins, prefix, n_perm)
  660. # Recall Ratio with baseline corrected eccentricity - D1 late
  661. ses = "ses-01"
  662. epoch_gradients = gradients.query('epoch == "late" & ses == @ses').copy()
  663. epoch_gradients = epoch_gradients.merge(day1base, on=["sub", "roi"], how="left", suffixes=("", "_base"))
  664. epoch_gradients["distance"] = epoch_gradients["distance"] - epoch_gradients["distance_base"]
  665. epoch_gradients.drop(columns=["distance_base"], inplace=True)
  666. score_rr = df[["sub", "RecallRatio"]]
  667. res = correlation_map(score_rr, epoch_gradients, f"{ses}_late-corrected_recall_ratio", fig_dir, angle="RecallRatio", sig_style="uncorrected")
  668. res.reset_index().to_csv(os.path.join(config.results, f"{ses}_late-corrected_recall_ratio_correlations.tsv"), index=False, sep="\t")
  669. _, network_spins = permute_maps(res, parc=config.atlas, n_perm=n_perm)
  670. network_spins.to_csv(os.path.join(config.results, f"{ses}_late-corrected_recall_ratio_spins.tsv"), index=False, sep="\t")
  671. prefix = os.path.join(fig_dir, f'{ses}_late-corrected_recall_ratio_permute_maps_boxplot')
  672. plot_permute_maps(network_spins, prefix, n_perm)
  673. if __name__ == "__main__":
  674. main()

behaviour.py at commit d936893, under MIT · at the source

Overview

Authors: Ali Rezaei1, Corson N. Areshenkoff1,2, Daniel J. Gale1, Emily R. Oby1,3, Jonathan Smallwood1,2, J. Randall Flanagan1,2, Jeffrey D. Wammes1,2, Jason P. Gallivan1,2,3
  1. Centre for Neuroscience Studies, Queen’s University, Kingston, Ontario, Canada
  2. Department of Psychology, Queen’s University, Kingston, Ontario, Canada
  3. Department of Biomedical and Molecular Sciences, Queen’s University, Kingston, Ontario, Canada
Institutions: Queen's University (Canada)
Journal: PLoS biology, volume 24, issue 3, article e3003684
Dates: received 6 November 2025; accepted 18 February 2026; published online 10 March 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1371/journal.pbio.3003684 · PMID 41805913 · PMCID PMC12974888 · OpenAlex W7134978557
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: fMRI (modality), human (organism), cognitive (subfield)
Methods: Spectral & time-frequency, Connectivity, Statistics, Smoothing, state filtering, decompositions, Machine learning, Preprocessing, fMRI & imaging
MeSH: Brain*, Learning*, Memory*, Mental Recall*, Nerve Net*, Adult, Brain Mapping, Female, Humans, Magnetic Resonance Imaging, Male, Young Adult (* major topic)
Journal subjects: Biology and Life Sciences, Neuroscience, Cognitive Science, Cognitive Psychology, Learning, Psychology, Social Sciences, Learning and Memory, Cognition, Memory, Physical Sciences, Mathematics, Topology, Manifolds, Perception, Sensory Perception, Computer and Information Sciences, Neural Networks, Memory Recall, Learning Curves, Brain Mapping
Topic: Motor Control and Adaptation (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Citations: not cited yet (Europe PMC); 167 references in the paper

Abstract

Motor learning induces alterations in neural activity that can persist long after the effects of such learning have faded. These persistent neural alterations are thought to manifest behaviorally as “savings,” or faster relearning, via access to a latent motor memory. How the human brain forms and retrieves these latent memories, and the specific neural systems involved, remains unresolved. Here, using human functional MRI and a two-day sensorimotor adaptation paradigm, we show that savings are associated with the reinstatement of a large-scale cortical manifold structure formed during initial learning. Notably, this neural reinstatement effect was not observed across sensorimotor systems but was localized to regions of the default mode network (DMN). Moreover, the specific dynamics of DMN activity were linked to inter-subject differences in patterns of learning and relearning across days. These results suggest that motor savings arises from the re-expression of DMN activity patterns associated with initial learning, establishing a key role for this network in motor memory formation and retrieval. This finding, paralleling reinstatement principles from other memory domains (episodic memory, fear conditioning) and anticipated by recent computational models of motor learning, suggests a common mechanism for the flexible recall and reuse of stored memories across diverse behavioral contexts.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repositories

Its files are read in the Code ↔ Paper reader above, with 13 matches between paragraphs and lines of code.

Zenodo 18612771

License: MIT
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: “Data Availability”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (14 files), pandas (14 files), Matplotlib (11 files), seaborn (8 files), BrainSpace (4 files), Pingouin (4 files), scikit-learn (4 files), neuromaps (3 files), SciPy (3 files), Brain Connectivity Toolbox (2 files), NiBabel (2 files), Nilearn (2 files), pyRiemann (2 files), UMAP (1 file)
Availability: 1 check, the latest on 30 September 2026: the link answers (HTTP 200)
  • 30 September 2026: the link answers (HTTP 200)
21 files

alirzar/motorsaving

License: MIT
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Commit: d936893e7dd8a8c2d1807582ddaa0bbc9bacbaf8, 11 February 2026
Languages: Python (19)
Size: 22 files, 19 scripts
Software Heritage: not archived
Found in: the text, “Software”
Holds: README, license file, environment (environment.yml, setup.py)
Not found: CITATION.cff, tests, continuous integration, documentation
Tools: NumPy (14 files), pandas (14 files), Matplotlib (11 files), seaborn (8 files), BrainSpace (4 files), Pingouin (4 files), scikit-learn (4 files), neuromaps (3 files), SciPy (3 files), Brain Connectivity Toolbox (2 files), NiBabel (2 files), Nilearn (2 files), pyRiemann (2 files), UMAP (1 file)
Availability: 1 check, the latest on 30 September 2026: the link answers
  • 30 September 2026: the link answers
21 files

danjgale/surfplot

License: BSD-3-Clause
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 60c50008bcd0f58e1c132cb7d70aab9151911925, 18 November 2025
Languages: Python (22), Jupyter (8)
Size: 133 files, 30 scripts
Software Heritage: archived
Found in: the references
Holds: README, license file, environment (pyproject.toml, requirements.txt, docs/requirements.txt), documentation, 8 notebooks
Not found: CITATION.cff, tests, continuous integration
Tools: neuromaps (21 files), BrainSpace (9 files), Matplotlib (8 files), NumPy (7 files), NiBabel (4 files), Nilearn (3 files)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
32 files

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:

  • 3 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 68 scripts, each with its path and the digest of its content;
  • 13 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

Data Availability

The underlying numerical data for figures are provided in S1 Data and archived on Zenodo (https://doi.org/10.5281/zenodo.18613054). Raw behavioral and imaging data (including T1w and functional scans) are openly available on OpenNeuro (https://doi.org/10.18112/openneuro.ds005598.v1.0.3). The analysis code is archived on Zenodo (DOI: https://doi.org/10.5281/zenodo.18612771).

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, 30 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 8 authors, 12 MeSH terms, 2 funders, 148 references, 8 RRIDs.

Cite

This paper

Rezaei, A., Areshenkoff, C. N., Gale, D. J., Oby, E. R., Smallwood, J., Flanagan, J. R., Wammes, J. D., & Gallivan, J. P. (2026). The retrieval of previously learned motor memories is facilitated by the reinstatement of default mode network manifold structures. PLoS biology, 24(3), e3003684. https://doi.org/10.1371/journal.pbio.3003684

BibTeX

@article{rezaei2026retrieval,
author = {Rezaei, Ali and Areshenkoff, Corson N. and Gale, Daniel J. and Oby, Emily R. and Smallwood, Jonathan and Flanagan, J. Randall and Wammes, Jeffrey D. and Gallivan, Jason P.},
title = {{The retrieval of previously learned motor memories is facilitated by the reinstatement of default mode network manifold structures}},
journal = {PLoS biology},
year = {2026},
month = mar,
volume = {24},
number = {3},
pages = {e3003684},
publisher = {PLOS},
issn = {1544-9173},
doi = {10.1371/journal.pbio.3003684},
url = {https://doi.org/10.1371/journal.pbio.3003684},
pmid = {41805913},
pmcid = {PMC12974888}
}

RIS

TY - JOUR
AU - Rezaei, Ali
AU - Areshenkoff, Corson N.
AU - Gale, Daniel J.
AU - Oby, Emily R.
AU - Smallwood, Jonathan
AU - Flanagan, J. Randall
AU - Wammes, Jeffrey D.
AU - Gallivan, Jason P.
TI - The retrieval of previously learned motor memories is facilitated by the reinstatement of default mode network manifold structures
T2 - PLoS biology
J2 - PLoS Biol
PY - 2026
DA - 2026/03/10
VL - 24
IS - 3
SP - e3003684
SN - 1544-9173
PB - PLOS
DO - 10.1371/journal.pbio.3003684
UR - https://doi.org/10.1371/journal.pbio.3003684
LA - en
ER -

CSL-JSON

{
"id": "10.1371/journal.pbio.3003684",
"type": "article-journal",
"title": "The retrieval of previously learned motor memories is facilitated by the reinstatement of default mode network manifold structures",
"container-title": "PLoS biology",
"author": [
{
"family": "Rezaei",
"given": "Ali"
},
{
"family": "Areshenkoff",
"given": "Corson N."
},
{
"family": "Gale",
"given": "Daniel J."
},
{
"family": "Oby",
"given": "Emily R."
},
{
"family": "Smallwood",
"given": "Jonathan"
},
{
"family": "Flanagan",
"given": "J. Randall"
},
{
"family": "Wammes",
"given": "Jeffrey D."
},
{
"family": "Gallivan",
"given": "Jason P."
}
],
"container-title-short": "PLoS Biol",
"volume": "24",
"issue": "3",
"page": "e3003684",
"DOI": "10.1371/journal.pbio.3003684",
"PMID": "41805913",
"PMCID": "PMC12974888",
"ISSN": "1544-9173",
"publisher": "PLOS",
"URL": "https://doi.org/10.1371/journal.pbio.3003684",
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
10
]
]
}
}

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.7554/elife.103097 [code]
Canonical neurodevelopmental trajectories of structural and functional manifolds.
Journal: eLife
In common: neuromaps, BrainSpace, Brain Connectivity Toolbox, 7 other tools, 18 references
[2] doi:10.1038/s41467-026-71151-2 [code]
Common and distinct neural correlates of social interaction processing and theory of mind in narratives.
Journal: Nature communications
In common: Brain Connectivity Toolbox, UMAP, Nilearn, 7 other tools, cognitive, 12 references
[3] doi:10.1371/journal.pbio.3003755 [code]
Action information is integrated into entorhinal representations of conceptual space and is reflected in eye movements.
Journal: PLoS biology
In common: Nilearn, NiBabel, seaborn, 4 other tools, cognitive, 15 references
[4] doi:10.1038/s41467-026-71428-6 [code]
Binding items to contexts through conjunctive neural representations with the method of loci.
Journal: Nature communications
In common: Nilearn, NiBabel, seaborn, 5 other tools, 15 references
[5] doi:10.1038/s41531-026-01354-3 [code]
Neuromodulation-induced normalization of cortical metastable dynamics signatures in Parkinson's disease.
Journal: NPJ Parkinson's disease
In common: neuromaps, BrainSpace, Nilearn, 7 other tools, 10 references
[6] doi:10.1523/eneuro.0370-25.2026 [code]
Pretraining for Large-Scale Functional Connectome Fingerprinting Supports Generalization and Transfer Learning in Functional Neuroimaging.
Journal: eNeuro
In common: Nilearn, scikit-learn, pandas, 2 other tools, fMRI, 16 references
[7] doi:10.7554/elife.107423 [code]
A context-free model of savings in motor learning.
Journal: eLife
In common: seaborn, scikit-learn, pandas, 3 other tools, 10 references
[8] doi:10.1038/s41398-026-04025-2 [code]
Brain energetic landscapes shape state dysregulation in major depressive disorder: a morphological network controllability perspective.
Journal: Translational psychiatry
In common: neuromaps, BrainSpace, Brain Connectivity Toolbox, 9 other tools, 5 references
[9] doi:10.1038/s41467-026-71270-w [code]
Spatiotemporal dynamics of the human cortical functional hierarchy across the lifespan.
Journal: Nature communications
In common: BrainSpace, Nilearn, NiBabel, 6 other tools, fMRI, 9 references
[10] doi:10.1162/netn.a.547 [code]
An evaluation of the efficacy of single-echo and multi-echo fMRI denoising strategies.
Journal: Network neuroscience (Cambridge, Mass.)
In common: Nilearn, NiBabel, seaborn, 5 other tools, fMRI, 11 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.

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.