OSCR

Dentate gyrus and CA3 activity mediates light-tone second-order conditioning expression in mice.

Code ↔ Paper

6 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 6 matches
  1. [1] § STAR★Methods › Method details › CellRake’s classification algorithm ↔ cellrake/utils.py, lines 651 to 693 · score 0.67 · ROC AUC, F1 score, precision, AP, probabilities, metrics · Report a mismatch
  2. [2] § Results › CellRake accurately quantifies fluorescent markers in histological brain sections ↔ cellrake/utils.py, lines 651 to 693 · score 0.58 · ROC AUC, F1 score, precision, AP, metrics, recall · Report a mismatch
  3. [3] § STAR★Methods › Method details › CellRake’s segmentation algorithm ↔ cellrake/segmentation.py, lines 245 to 278 · score 0.52 · polygonal contours, clips, bounds, segmented, dictionary, CellRake · Report a mismatch
  4. [4] § STAR★Methods › Method details › CellRake’s classification algorithm ↔ cellrake/main.py, lines 130 to 245 · score 0.52 · semi supervised learning, spreading, clustering, algorithm, blobs, classify · Report a mismatch
  5. [5] § STAR★Methods › Method details › CellRake’s classification algorithm ↔ cellrake/training.py, lines 528 to 636 · score 0.51 · semi supervised learning, spreading, clustering, pipeline, algorithm, classify · Report a mismatch
  6. [6] § STAR★Methods › Method details › CellRake’s classification algorithm ↔ cellrake/training.py, lines 139 to 262 · score 0.51 · Minority, SMOTE, kernel, probabilities, entropy, confidence · Report a mismatch

Paper

Loaded from Europe PMC by your browser; OSCR does not store it: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 693 lines · 21 KB · MIT · 2 matches

  1. # Created by: Marc Canela
  2. from pathlib import Path
  3. from typing import Any, Dict, List, Optional, Tuple, Union
  4. import numpy as np
  5. import pandas as pd
  6. from scipy.stats import uniform
  7. from shapely.geometry import Polygon
  8. from skimage.draw import polygon
  9. from skimage.feature import graycomatrix, graycoprops, hog, local_binary_pattern
  10. from skimage.measure import label, regionprops
  11. from sklearn.decomposition import PCA
  12. from sklearn.ensemble import ExtraTreesClassifier, RandomForestClassifier
  13. from sklearn.linear_model import LogisticRegression
  14. from sklearn.metrics import (
  15. average_precision_score,
  16. f1_score,
  17. precision_score,
  18. recall_score,
  19. roc_auc_score,
  20. )
  21. from sklearn.model_selection import RandomizedSearchCV, cross_val_predict
  22. from sklearn.pipeline import Pipeline
  23. from sklearn.preprocessing import StandardScaler
  24. from sklearn.svm import SVC
  25. def export_data(data: pd.DataFrame, project_folder: Path, file_name: str) -> None:
  26. """
  27. Save data to CSV and Excel files in the specified project folder.
  28. Parameters
  29. ----------
  30. data : pd.DataFrame
  31. Data to save.
  32. project_folder : Path
  33. Folder to save the files.
  34. file_name : str
  35. Base name of the files (without extensions).
  36. """
  37. data.to_csv(project_folder / f"{file_name}.csv", index=False)
  38. data.to_excel(project_folder / f"{file_name}.xlsx", index=False)
  39. def get_cell_mask(layer: np.ndarray, coordinates: np.ndarray) -> np.ndarray:
  40. """
  41. Generate a binary mask for ROI regions defined by polygonal coordinates.
  42. Parameters
  43. ----------
  44. layer : np.ndarray
  45. 2D image layer determining mask dimensions.
  46. coordinates : np.ndarray
  47. Array of shape (N, 2) representing polygon vertices defining ROI.
  48. Returns
  49. -------
  50. np.ndarray
  51. Binary mask with ROI pixels set to 1, others to 0.
  52. """
  53. mask = np.zeros(layer.shape, dtype=np.uint8)
  54. # Extract x and y coordinates separately
  55. r = coordinates[0, :, 1] # y-coordinates
  56. c = coordinates[0, :, 0] # x-coordinate
  57. # Get the indices of the pixels that are inside the polygon
  58. rr, cc = polygon(r, c, mask.shape)
  59. # Fill the mask
  60. mask[rr, cc] = 1
  61. return mask
  62. def crop_cell(
  63. layer: np.ndarray,
  64. x_coords: Union[List[int], np.ndarray],
  65. y_coords: Union[List[int], np.ndarray],
  66. ) -> np.ndarray:
  67. """
  68. Extract a rectangular subregion from an image layer.
  69. Parameters
  70. ----------
  71. layer : np.ndarray
  72. 2D image layer to crop from.
  73. x_coords : Union[List[int], np.ndarray]
  74. X-coordinates defining horizontal crop boundaries.
  75. y_coords : Union[List[int], np.ndarray]
  76. Y-coordinates defining vertical crop boundaries.
  77. Returns
  78. -------
  79. np.ndarray
  80. Cropped rectangular region from the image layer.
  81. """
  82. # Extract min and max coordinates and convert to integers
  83. x_min, x_max = int(min(x_coords)), int(max(x_coords))
  84. y_min, y_max = int(min(y_coords)), int(max(y_coords))
  85. # Crop the rectangular region from the layer
  86. return layer[y_min : y_max + 1, x_min : x_max + 1]
  87. def extract_roi_stats(
  88. layer: np.ndarray, roi_info: Dict[str, Any]
  89. ) -> Tuple[np.ndarray, Dict[str, Any]]:
  90. """
  91. Extract statistical and texture features from a region of interest (ROI).
  92. Parameters
  93. ----------
  94. layer : np.ndarray
  95. 2D image layer for feature extraction.
  96. roi_info : Dict[str, Any]
  97. Dictionary with ROI coordinates containing "x" and "y" keys.
  98. Returns
  99. -------
  100. Tuple[np.ndarray, Dict[str, Any]]
  101. Binary mask and dictionary of extracted features including intensity,
  102. texture, and morphological statistics.
  103. """
  104. # Extract ROI coordinates from the dictionary
  105. x_coords, y_coords = roi_info["x"], roi_info["y"]
  106. coordinates = np.array([list(zip(x_coords, y_coords))], dtype=np.int32)
  107. # Create a binary mask for the ROI
  108. cell_mask = get_cell_mask(layer, coordinates)
  109. # If the mask is empty, skip
  110. if np.mean(cell_mask) == 0:
  111. cell_mask = None
  112. stats_dict = None
  113. return cell_mask, stats_dict
  114. # Crop the layer and mask to the bounding box of the ROI
  115. layer_cropped = crop_cell(layer, x_coords, y_coords)
  116. cell_mask_cropped = crop_cell(cell_mask, x_coords, y_coords)
  117. # Create a background mask
  118. background_mask_cropped = 1 - cell_mask_cropped
  119. # Extract pixel values for the ROI and background
  120. cell_pixels = layer[cell_mask == 1]
  121. cell_pixels_mean = np.mean(cell_pixels)
  122. background_pixels = layer_cropped[background_mask_cropped == 1]
  123. background_pixels_mean = (
  124. np.mean(background_pixels) if background_pixels.size > 0 else cell_pixels_mean
  125. )
  126. # Mask the cropped layer and calculate texture features
  127. layer_cropped_masked = layer_cropped * cell_mask_cropped
  128. layer_masked = layer * cell_mask
  129. # Calculate Local Binary Pattern (LBP)
  130. lbp = local_binary_pattern(layer_cropped_masked, P=8, R=1, method="uniform")
  131. # Calculate Gray Level Co-occurrence Matrix (GLCM) features
  132. glcm = graycomatrix(
  133. layer_masked, distances=[1], angles=[0], levels=256, symmetric=True, normed=True
  134. )
  135. # Calculate region properties
  136. prop = regionprops(label(cell_mask_cropped))[0]
  137. height, width = layer_cropped_masked.shape[:2]
  138. pixels_per_cell_val = 4
  139. cells_per_block_val = 2
  140. # Set the required minimum size based on the HOG parameters
  141. min_height = pixels_per_cell_val * cells_per_block_val
  142. min_width = pixels_per_cell_val * cells_per_block_val
  143. # Calculate the amount of padding needed
  144. pad_height = max(0, min_height - height)
  145. pad_width = max(0, min_width - width)
  146. # Apply padding to the image
  147. if pad_height > 0 or pad_width > 0:
  148. padded_image = np.pad(
  149. layer_cropped_masked,
  150. ((0, pad_height), (0, pad_width)),
  151. mode="constant",
  152. constant_values=0,
  153. )
  154. else:
  155. padded_image = layer_cropped_masked
  156. # Compute Histogram of Oriented Gradients (HOG) features
  157. h_values, _ = hog(
  158. padded_image,
  159. orientations=9,
  160. pixels_per_cell=(pixels_per_cell_val, pixels_per_cell_val),
  161. cells_per_block=(cells_per_block_val, cells_per_block_val),
  162. block_norm="L2-Hys",
  163. visualize=True,
  164. feature_vector=True,
  165. )
  166. # Flatten the HOG features if needed (though skimage.hog already returns a flat array if feature_vector=True)
  167. hog_descriptor_values = h_values.flatten()
  168. # Create a dictionary to store the extracted features
  169. stats_dict = {
  170. "mean_intensity": cell_pixels_mean,
  171. "median_intensity": np.median(cell_pixels),
  172. "sd_intensity": np.std(cell_pixels),
  173. "min_intensity": np.min(cell_pixels),
  174. "max_intensity": np.max(cell_pixels),
  175. "mean_ratio": cell_pixels_mean
  176. / (background_pixels_mean if background_pixels_mean != 0 else 1),
  177. "mean_difference": cell_pixels_mean - background_pixels_mean,
  178. "mean_all_ratio": None,
  179. "mean_all_difference": None,
  180. "mean_max_ratio": None,
  181. "mean_max_difference": None,
  182. "lbp_mean": np.mean(lbp),
  183. "lbp_std": np.std(lbp),
  184. "contrast": graycoprops(glcm, "contrast")[0, 0],
  185. "correlation": graycoprops(glcm, "correlation")[0, 0],
  186. "energy": graycoprops(glcm, "energy")[0, 0],
  187. "homogeneity": graycoprops(glcm, "homogeneity")[0, 0],
  188. "area": prop.area,
  189. "perimeter": prop.perimeter,
  190. "eccentricity": prop.eccentricity,
  191. "major_axis_length": prop.major_axis_length,
  192. "minor_axis_length": prop.minor_axis_length,
  193. "solidity": prop.solidity,
  194. "extent": prop.extent,
  195. "hog_mean": np.mean(hog_descriptor_values),
  196. "hog_std": np.std(hog_descriptor_values),
  197. }
  198. return cell_mask, stats_dict
  199. def create_stats_dict(
  200. roi_dict: Dict[str, Dict[str, Any]], layer: np.ndarray
  201. ) -> Dict[str, Dict[str, Any]]:
  202. """
  203. Calculate features for multiple ROIs with background statistics.
  204. Parameters
  205. ----------
  206. roi_dict : Dict[str, Dict[str, Any]]
  207. Dictionary mapping ROI names to coordinate dictionaries.
  208. layer : np.ndarray
  209. 2D image layer for feature extraction.
  210. Returns
  211. -------
  212. Dict[str, Dict[str, Any]]
  213. Dictionary with ROI names as keys and feature dictionaries as values.
  214. """
  215. cell_masks = []
  216. roi_props = {}
  217. # Extract statistics for each ROI
  218. for roi_name, roi_info in roi_dict.items():
  219. cell_mask, stats_dict = extract_roi_stats(layer, roi_info)
  220. if cell_mask is not None and stats_dict is not None:
  221. roi_props[roi_name] = stats_dict
  222. cell_masks.append(cell_mask)
  223. # Compute combined background statistics
  224. combined_cell_mask = np.bitwise_or.reduce(cell_masks)
  225. combined_background_mask = 1 - combined_cell_mask
  226. combined_background_pixels = layer[combined_background_mask == 1]
  227. nonzero_background_pixels = combined_background_pixels[
  228. combined_background_pixels != 0
  229. ]
  230. # Handle case when all background pixels are zero to avoid division by zero
  231. mean_background = (
  232. np.mean(nonzero_background_pixels) if nonzero_background_pixels.size > 0 else 0
  233. )
  234. # Update statistics with additional ratios and differences
  235. for stats_dict in roi_props.values():
  236. mean_intensity = stats_dict["mean_intensity"]
  237. stats_dict["mean_all_ratio"] = mean_intensity / (
  238. mean_background if mean_background != 0 else 1
  239. )
  240. stats_dict["mean_all_difference"] = mean_intensity - mean_background
  241. # Compute maximum mean intensity across all ROIs
  242. max_mean_intensity = max(
  243. stats_dict["mean_intensity"] for stats_dict in roi_props.values()
  244. )
  245. for stats_dict in roi_props.values():
  246. mean_intensity = stats_dict["mean_intensity"]
  247. stats_dict["mean_max_ratio"] = max_mean_intensity / (
  248. mean_intensity if mean_intensity != 0 else 1
  249. )
  250. stats_dict["mean_max_difference"] = max_mean_intensity - mean_intensity
  251. return roi_props
  252. def crop_cell_large(
  253. layer: np.ndarray,
  254. x_coords: List[float],
  255. y_coords: List[float],
  256. padding: Optional[int] = None,
  257. ) -> Tuple[np.ndarray, List[float], List[float]]:
  258. """
  259. Extract a padded subregion from an image layer with coordinate adjustment.
  260. Parameters
  261. ----------
  262. layer : np.ndarray
  263. 2D or 3D image layer to crop from.
  264. x_coords : List[float]
  265. X-coordinates defining the crop boundary.
  266. y_coords : List[float]
  267. Y-coordinates defining the crop boundary.
  268. padding : Optional[int]
  269. Padding to add around crop area. If None, uses ROI dimensions.
  270. Returns
  271. -------
  272. Tuple[np.ndarray, List[float], List[float]]
  273. Cropped image layer and adjusted coordinates.
  274. """
  275. # Determine cropping boundaries
  276. x_min, x_max = int(min(x_coords)), int(max(x_coords))
  277. y_min, y_max = int(min(y_coords)), int(max(y_coords))
  278. # Apply padding if specified
  279. if padding is not None:
  280. x_min -= padding
  281. x_max += padding
  282. y_min -= padding
  283. y_max += padding
  284. else:
  285. x_padding = x_max - x_min
  286. y_padding = y_max - y_min
  287. x_min -= x_padding
  288. x_max += x_padding
  289. y_min -= y_padding
  290. y_max += y_padding
  291. # Ensure cropping boundaries are within image dimensions
  292. layer_height, layer_width = layer.shape[:2]
  293. x_min = max(0, x_min)
  294. x_max = min(layer_width - 1, x_max)
  295. y_min = max(0, y_min)
  296. y_max = min(layer_height - 1, y_max)
  297. # Crop the image
  298. layer_cropped = layer[y_min : y_max + 1, x_min : x_max + 1]
  299. # Adjust coordinates relative to the cropped image
  300. x_coords_cropped = [x - x_min for x in x_coords]
  301. y_coords_cropped = [y - y_min for y in y_coords]
  302. return layer_cropped, x_coords_cropped, y_coords_cropped
  303. def fix_polygon(polygon: Polygon) -> Polygon:
  304. """
  305. Validate and fix invalid polygons using buffer operations.
  306. Parameters
  307. ----------
  308. polygon : Polygon
  309. Input polygon to validate and potentially fix.
  310. Returns
  311. -------
  312. Polygon
  313. Valid polygon or None if correction failed.
  314. """
  315. if not polygon.is_valid:
  316. # Attempt to fix the polygon by applying a zero-width buffer
  317. polygon = polygon.buffer(0)
  318. # Check if the polygon is still invalid after buffering
  319. if not polygon.is_valid:
  320. print("Polygon could not be fixed. Skipping this polygon.")
  321. return None
  322. return polygon
  323. def crop(layer: np.ndarray) -> np.ndarray:
  324. """
  325. Remove rows and columns containing only zero values from image layer.
  326. Parameters
  327. ----------
  328. layer : np.ndarray
  329. 2D image layer to trim.
  330. Returns
  331. -------
  332. np.ndarray
  333. Cropped image layer with zero-only rows and columns removed.
  334. """
  335. # Remove rows that are entirely zeros
  336. layer = layer[~np.all(layer == 0, axis=1)]
  337. # Remove columns that are entirely zeros
  338. layer = layer[:, ~np.all(layer == 0, axis=0)]
  339. return layer
  340. def train_svm(processed_dfs: Dict, random_state: int) -> Pipeline:
  341. """
  342. Train an SVM model with hyperparameter tuning using RandomizedSearchCV.
  343. Parameters
  344. ----------
  345. processed_dfs : Dict
  346. Dictionary containing processed data sets.
  347. random_state : int, default=42
  348. Random seed for reproducibility.
  349. Returns
  350. -------
  351. Pipeline
  352. Best SVM estimator found by random search.
  353. """
  354. # Perform randomized search with cross-validation with the validation data
  355. pipeline_steps = [
  356. ("scaler", StandardScaler()),
  357. ("pca", PCA(n_components=0.95, random_state=random_state)),
  358. (
  359. "svm",
  360. SVC(
  361. kernel="rbf",
  362. probability=True,
  363. class_weight="balanced",
  364. random_state=random_state
  365. ),
  366. ),
  367. ]
  368. pipeline = Pipeline(pipeline_steps)
  369. param_dist = {
  370. "svm__C": uniform(1, 100), # Regularization parameter
  371. }
  372. random_search = RandomizedSearchCV(
  373. pipeline,
  374. param_dist,
  375. n_iter=50,
  376. cv=3,
  377. random_state=42,
  378. n_jobs=-1,
  379. verbose=0,
  380. error_score="raise",
  381. )
  382. random_search.fit(processed_dfs['val']['X'], processed_dfs['val']['y'])
  383. best_params = random_search.best_params_
  384. # Perform training on the training set with the best parameters
  385. final_pipeline_steps = [
  386. ("scaler", StandardScaler()),
  387. ("pca", PCA(n_components=0.95, random_state=random_state)),
  388. (
  389. "svm",
  390. SVC(
  391. kernel="rbf",
  392. probability=True,
  393. class_weight="balanced",
  394. random_state=random_state,
  395. C=best_params['svm__C']
  396. ),
  397. ),
  398. ]
  399. final_model = Pipeline(final_pipeline_steps)
  400. final_model.fit(processed_dfs['train']['X'], processed_dfs['train']['y'])
  401. return final_model
  402. def train_rf(
  403. processed_dfs: Dict, model_type: str, random_state : int
  404. ) -> Union[RandomForestClassifier, ExtraTreesClassifier]:
  405. """
  406. Train a Random Forest or Extra Trees classifier with hyperparameter tuning.
  407. Parameters
  408. ----------
  409. processed_dfs : Dict
  410. Dictionary containing processed data sets.
  411. model_type : str
  412. Model type: 'rf' for Random Forest or 'et' for Extra Trees.
  413. random_state : int, default=42
  414. Random seed for reproducibility.
  415. Returns
  416. -------
  417. Union[RandomForestClassifier, ExtraTreesClassifier]
  418. Best estimator found by random search.
  419. """
  420. if model_type == "et":
  421. classifier = ExtraTreesClassifier(class_weight="balanced", random_state=random_state)
  422. elif model_type == "rf":
  423. classifier = RandomForestClassifier(class_weight="balanced", random_state=random_state)
  424. # Perform randomized search with cross-validation with the validation data
  425. param_dist = {
  426. f"{model_type}__n_estimators": [int(x) for x in np.linspace(start=50, stop=500, num=10)],
  427. f"{model_type}__max_features": ["sqrt", "log2", None],
  428. f"{model_type}__max_depth": [int(x) for x in np.linspace(10, 100, num=10)] + [None],
  429. f"{model_type}__min_samples_split": [2, 5, 10, 15, 20],
  430. f"{model_type}__min_samples_leaf": [1, 2, 4, 8, 10],
  431. f"{model_type}__bootstrap": [True, False],
  432. f"{model_type}__criterion": ["gini", "entropy"],
  433. }
  434. cv_pipeline = Pipeline([
  435. (model_type, classifier)
  436. ])
  437. random_search = RandomizedSearchCV(
  438. cv_pipeline,
  439. param_dist,
  440. n_iter=50,
  441. cv=3,
  442. random_state=random_state,
  443. n_jobs=-1,
  444. verbose=0,
  445. error_score="raise",
  446. )
  447. random_search.fit(processed_dfs['val']['X'], processed_dfs['val']['y'])
  448. best_params = random_search.best_params_
  449. # Perform training on the training set with the best parameters
  450. final_params = {k.replace(f'{model_type}__', ''): v for k, v in best_params.items()}
  451. if model_type == "et":
  452. final_classifier = ExtraTreesClassifier(class_weight="balanced", random_state=random_state, **final_params)
  453. else:
  454. final_classifier = RandomForestClassifier(class_weight="balanced", random_state=random_state, **final_params)
  455. final_classifier.fit(processed_dfs['train']['X'], processed_dfs['train']['y'])
  456. return final_classifier
  457. def train_logreg(processed_dfs : Dict, random_state : int) -> Pipeline:
  458. """
  459. Train a Logistic Regression model with hyperparameter tuning.
  460. Parameters
  461. ----------
  462. processed_dfs : Dict
  463. Dictionary containing processed data sets.
  464. random_state : int, default=42
  465. Random seed for reproducibility.
  466. Returns
  467. -------
  468. Pipeline
  469. Best estimator pipeline with PCA and LogisticRegression.
  470. """
  471. # Perform randomized search with cross-validation with the validation data
  472. pipeline_steps = [
  473. ("scaler", StandardScaler()),
  474. ("pca", PCA(n_components=0.95, random_state=random_state)),
  475. ("log_reg", LogisticRegression(class_weight="balanced", solver='liblinear', random_state=random_state)),
  476. ]
  477. cv_pipeline = Pipeline(pipeline_steps)
  478. param_dist = {
  479. "log_reg__C": uniform(1, 100),
  480. "log_reg__penalty": ['l1', 'l2', 'elasticnet']
  481. }
  482. random_search = RandomizedSearchCV(
  483. cv_pipeline,
  484. param_dist,
  485. n_iter=50,
  486. cv=3,
  487. random_state=random_state,
  488. n_jobs=-1,
  489. verbose=0,
  490. error_score="raise",
  491. )
  492. random_search.fit(processed_dfs['val']['X'], processed_dfs['val']['y'])
  493. best_params = random_search.best_params_
  494. # Perform training on the training set with the best parameters
  495. final_pipeline_steps = [
  496. ("scaler", StandardScaler()),
  497. ("pca", PCA(n_components=0.95, random_state=random_state)),
  498. (
  499. "log_reg",
  500. LogisticRegression(
  501. class_weight="balanced",
  502. random_state=random_state,
  503. solver='liblinear',
  504. C=best_params['log_reg__C'],
  505. penalty=best_params['log_reg__penalty']
  506. ),
  507. ),
  508. ]
  509. final_model = Pipeline(final_pipeline_steps)
  510. final_model.fit(processed_dfs['train']['X'], processed_dfs['train']['y'])
  511. return final_model
  512. def train_model(
  513. processed_dfs: Dict, model_type: str, random_state: int
  514. ) -> Union[Pipeline, RandomForestClassifier, ExtraTreesClassifier]:
  515. """
  516. Train a machine learning model based on the specified model type.
  517. Parameters
  518. ----------
  519. processed_dfs : Dict
  520. Dictionary containing processed data sets.
  521. model_type : str
  522. Model type: "svm", "rf", "et", or "logreg".
  523. random_state : int, default=42
  524. Random seed for reproducibility.
  525. Returns
  526. -------
  527. Union[Pipeline, RandomForestClassifier, ExtraTreesClassifier]
  528. Trained machine learning model.
  529. """
  530. if model_type == "svm":
  531. best_model = train_svm(processed_dfs, random_state)
  532. elif model_type in ["rf", "et"]:
  533. best_model = train_rf(processed_dfs, model_type, random_state)
  534. elif model_type == "logreg":
  535. best_model = train_logreg(processed_dfs, random_state)
  536. return best_model
  537. def evaluate_model(
  538. best_model: Union[Pipeline, RandomForestClassifier, ExtraTreesClassifier],
  539. processed_dfs: Dict,
  540. ) -> Dict[str, float]:
  541. """
  542. Evaluate model performance using cross-validated predictions.
  543. Parameters
  544. ----------
  545. best_model : Union[Pipeline, RandomForestClassifier, ExtraTreesClassifier]
  546. Model to be evaluated.
  547. processed_dfs : Dict
  548. Dictionary containing processed data sets.
  549. Returns
  550. -------
  551. Dict[str, float]
  552. Dictionary containing evaluation metrics: roc_auc, ap, precision, recall, f1_score.
  553. """
  554. X = processed_dfs['X']
  555. y = processed_dfs['y']
  556. # Get cross-validated predictions and probabilities
  557. y_pred_proba = cross_val_predict(best_model, X, y, cv=3, method="predict_proba")
  558. y_pred = (y_pred_proba[:, 1] >= 0.5).astype(int)
  559. y_proba = y_pred_proba[:, 1]
  560. # Calculate metrics
  561. roc_auc = roc_auc_score(y, y_proba)
  562. ap = average_precision_score(y, y_proba)
  563. precision_score_value = precision_score(y, y_pred)
  564. recall_score_value = recall_score(y, y_pred)
  565. f1_score_value = f1_score(y, y_pred)
  566. metrics = {
  567. "roc_auc": roc_auc,
  568. "ap": ap,
  569. "precision": precision_score_value,
  570. "recall": recall_score_value,
  571. "f1_score": f1_score_value,
  572. }
  573. return metrics

utils.py at commit 6a397fc, under MIT · at the source

Overview

Authors: Marc Canela1, Jordi Bach Adell1, David Roura Coll1, Jose Antonio González-Parra1, Julia S. Pinho1, Arnau Busquets-García1
  1. Cell-Type Mechanisms in Normal and Pathological Behavior Research Group, Neuroscience Research Program, Hospital del Mar Research Institute, Barcelona, Spain
Journal: iScience, volume 29, issue 4, article 115522
Dates: received 1 September 2025; accepted 11 February 2026; published online 30 March 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1016/j.isci.2026.115522 · PMID 42006365 · PMCID PMC13091456 · OpenAlex W7143520602
Open access: gold, a free copy (OpenAlex)
Status: code verified (how the code is found and verified)
Categories: mouse (organism)
Methods: Statistics, Machine learning, Connectivity, fMRI & imaging
Keywords: Neuroscience, Behavioral neuroscience, Systems neuroscience
Topic: Retinal Development and Disorders (Ophthalmology, Medicine), according to OpenAlex
Funding: Generalitat de Catalunya (SGR-00022); Departament d’Economia i Coneixement de la Generalitat de Catalunya (Spain); European Research Council; EC | Horizon 2020 (EU Framework Programme for Research and Innovation) (948217); “la Caixa” Foundation (100010434)
Citations: not cited yet (Europe PMC); 77 references in the paper

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.

Repositories

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

Zenodo 19075438

License: CC-BY-4.0
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Size: 18 files
Software Heritage: not checked
Found in: “Data and code availability”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 30 September 2026: the link answers (HTTP 200)
  • 30 September 2026: the link answers (HTTP 200)
At the source:

marccanela/cellrake

License: MIT
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Commit: 6a397fcdd5a37f99d4ba272d21cc333d033e0cff, 23 November 2025
Languages: Python (6), Jupyter (1)
Size: 42 files, 7 scripts
Software Heritage: not archived
Found in: “Data and code availability”
Holds: README, license file, environment (pyproject.toml), 1 notebook
Not found: CITATION.cff, tests, continuous integration, documentation
Tools: NumPy (4 files), pandas (4 files), scikit-learn (4 files), Matplotlib (3 files), Pillow (2 files), scikit-image (2 files), SciPy (2 files), imbalanced-learn (1 file)
Availability: 1 check, the latest on 30 September 2026: the link answers
  • 30 September 2026: the link answers
9 files

Zenodo 19002677

License: MIT
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: “Data and code availability”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (4 files), pandas (4 files), scikit-learn (4 files), Matplotlib (3 files), Pillow (2 files), scikit-image (2 files), SciPy (2 files), imbalanced-learn (1 file)
Availability: 1 check, the latest on 30 September 2026: the link answers (HTTP 200)
  • 30 September 2026: the link answers (HTTP 200)
9 files
At the source:

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;
  • 14 scripts, each with its path and the digest of its content;
  • 6 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

No dataset and no data link were found in the paper.

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 the authors' code: marccanela/cellrake, Zenodo 19002677, Zenodo 19075438
  • 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.isci.2026.115522.

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, 6 authors, 3 keywords, 5 funders, 69 references.

Cite

This paper

Canela, M., Adell, J. B., Coll, D. R., González-Parra, J. A., Pinho, J. S., & Busquets-García, A. (2026). Dentate gyrus and CA3 activity mediates light-tone second-order conditioning expression in mice. iScience, 29(4), 115522. https://doi.org/10.1016/j.isci.2026.115522

BibTeX

@article{canela2026dentate,
author = {Canela, Marc and Adell, Jordi Bach and Coll, David Roura and González-Parra, Jose Antonio and Pinho, Julia S. and Busquets-García, Arnau},
title = {{Dentate gyrus and CA3 activity mediates light-tone second-order conditioning expression in mice}},
journal = {iScience},
year = {2026},
month = mar,
volume = {29},
number = {4},
pages = {115522},
publisher = {Elsevier},
issn = {2589-0042},
doi = {10.1016/j.isci.2026.115522},
url = {https://doi.org/10.1016/j.isci.2026.115522},
pmid = {42006365},
pmcid = {PMC13091456}
}

RIS

TY - JOUR
AU - Canela, Marc
AU - Adell, Jordi Bach
AU - Coll, David Roura
AU - González-Parra, Jose Antonio
AU - Pinho, Julia S.
AU - Busquets-García, Arnau
TI - Dentate gyrus and CA3 activity mediates light-tone second-order conditioning expression in mice
T2 - iScience
J2 - iScience
PY - 2026
DA - 2026/03/30
VL - 29
IS - 4
SP - 115522
SN - 2589-0042
PB - Elsevier
DO - 10.1016/j.isci.2026.115522
UR - https://doi.org/10.1016/j.isci.2026.115522
LA - en
ER -

CSL-JSON

{
"id": "10.1016/j.isci.2026.115522",
"type": "article-journal",
"title": "Dentate gyrus and CA3 activity mediates light-tone second-order conditioning expression in mice",
"container-title": "iScience",
"author": [
{
"family": "Canela",
"given": "Marc"
},
{
"family": "Adell",
"given": "Jordi Bach"
},
{
"family": "Coll",
"given": "David Roura"
},
{
"family": "González-Parra",
"given": "Jose Antonio"
},
{
"family": "Pinho",
"given": "Julia S."
},
{
"family": "Busquets-García",
"given": "Arnau"
}
],
"container-title-short": "iScience",
"volume": "29",
"issue": "4",
"page": "115522",
"DOI": "10.1016/j.isci.2026.115522",
"PMID": "42006365",
"PMCID": "PMC13091456",
"ISSN": "2589-0042",
"publisher": "Elsevier",
"URL": "https://doi.org/10.1016/j.isci.2026.115522",
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
30
]
]
}
}

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.105863 [code]
Dorsal hippocampus mediates light-tone associations in male mice.
Journal: eLife
In common: pandas, SciPy, Matplotlib, 1 other tool, mouse, 9 references, author Arnau Busquets-García
[2] doi:10.1016/j.isci.2026.116825 [code]
Social hierarchy shapes behavioral and transcriptional responses to chronic stress and ketamine in male mice.
Journal: iScience
In common: imbalanced-learn, scikit-image, Pillow, 5 other tools, mouse, 2 references
[3] doi:10.1038/s41593-023-01281-z [code]
Ascending neurons convey behavioral state to integrative sensory and action selection brain regions.
Journal: Nature neuroscience
In common: imbalanced-learn, scikit-image, Pillow, 5 other tools, 2 references
[4] doi:10.7554/elife.95273 [code]
Unbiased identification of cell identity in dense mixed neural cultures.
Journal: eLife
In common: imbalanced-learn, scikit-image, scikit-learn, 4 other tools, 3 references
[5] doi:10.1371/journal.pbio.3002646 [code]
Deep brain stimulation of the Tbr1-deficient mouse model of autism spectrum disorder at the basolateral amygdala alters amygdalar connectivity, whole-brain synchronization, and social behaviors.
Journal: PLoS biology
In common: scikit-image, scikit-learn, pandas, 3 other tools, mouse, 4 references
[6] doi:10.1126/sciadv.adu9136 [code]
High-resolution in vivo kinematic tracking with customized injectable fluorescent nanoparticles.
Journal: Science advances
In common: scikit-image, Pillow, scikit-learn, 4 other tools, mouse, 3 references
[7] doi:10.1016/j.cell.2022.12.010 [code]
Molecular and spatial signatures of mouse brain aging at single-cell resolution.
Journal: Cell
In common: imbalanced-learn, scikit-image, Pillow, 5 other tools, mouse, 1 reference
[8] doi:10.1242/jcs.261950 [code]
Gut Analysis Toolbox - automating quantitative analysis of enteric neurons.
Journal: Journal of cell science
In common: scikit-image, Pillow, scikit-learn, 4 other tools, mouse, 3 references
[9] doi:10.1038/s41586-024-07780-8 [code]
Inhibitory specificity from a connectomic census of mouse visual cortex.
Journal: Nature
In common: imbalanced-learn, scikit-image, Pillow, 5 other tools, mouse, 1 reference
[10] doi:10.1038/s42003-026-10957-8 [code]
Brain defence by the extracellular matrix protein Cochlin.
Journal: Communications biology
In common: imbalanced-learn, scikit-image, Pillow, 5 other tools, mouse, 1 reference

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.

Add the badge to the code's README

OSCR: the badge of this paper

If you are an author or a maintainer of this paper's code, you can add this badge. It says what OSCR knows of the paper (its code, its validated tracing map) and links the code to this page. Paste one of these snippets under the README's title. Keep the two marks around it, so that the badge can be replaced in place and is never added twice. OSCR changes nothing in your repository.

Markdown
<!-- oscr-badge:start -->
[![OSCR](https://openscicode.org/badge/doi/10.1016/j.isci.2026.115522.svg)](https://openscicode.org/badge/doi/10.1016/j.isci.2026.115522)
<!-- oscr-badge:end -->
reStructuredText
.. oscr-badge:start

.. image:: https://openscicode.org/badge/doi/10.1016/j.isci.2026.115522.svg
   :target: https://openscicode.org/badge/doi/10.1016/j.isci.2026.115522
   :alt: OSCR

.. oscr-badge:end
HTML
<!-- oscr-badge:start -->
<a href="https://openscicode.org/badge/doi/10.1016/j.isci.2026.115522"><img src="https://openscicode.org/badge/doi/10.1016/j.isci.2026.115522.svg" alt="OSCR"></a>
<!-- oscr-badge:end -->

On GitHub, open the README, choose Edit (the pencil), paste the snippet under the title, then "Commit changes".

If you choose to, you can have the badge kept up to date for you, with oscr badge --write from the command line or with the GitHub Action in your repository's workflows. Every other form and option (a card, AsciiDoc, LaTeX, shields.io) is on the badge 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: code errors and mismatches between the code and the paper about this paper and its code, from readers and its authors, beside the paper and its tracing map.

Report a mismatch between the code and the paper · Report a code error (signed in)

Reproductions: reports from readers who ran the authors' code and did not get the paper's result, what they ran, with which environment, commit and data.

Report a reproduction failure (signed in)

Versions of its code: the releases of its code tied to a version of this paper (the preprint, the accepted manuscript, the version of record…), each with the tracing map versioned with it.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.