canns 0.13.1__py3-none-any.whl → 0.14.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- canns/analyzer/data/__init__.py +5 -1
- canns/analyzer/data/asa/__init__.py +27 -12
- canns/analyzer/data/asa/cohospace.py +336 -10
- canns/analyzer/data/asa/config.py +3 -0
- canns/analyzer/data/asa/embedding.py +48 -45
- canns/analyzer/data/asa/path.py +104 -2
- canns/analyzer/data/asa/plotting.py +88 -19
- canns/analyzer/data/asa/tda.py +11 -4
- canns/analyzer/data/cell_classification/__init__.py +97 -0
- canns/analyzer/data/cell_classification/core/__init__.py +26 -0
- canns/analyzer/data/cell_classification/core/grid_cells.py +633 -0
- canns/analyzer/data/cell_classification/core/grid_modules_leiden.py +288 -0
- canns/analyzer/data/cell_classification/core/head_direction.py +347 -0
- canns/analyzer/data/cell_classification/core/spatial_analysis.py +431 -0
- canns/analyzer/data/cell_classification/io/__init__.py +5 -0
- canns/analyzer/data/cell_classification/io/matlab_loader.py +417 -0
- canns/analyzer/data/cell_classification/utils/__init__.py +39 -0
- canns/analyzer/data/cell_classification/utils/circular_stats.py +383 -0
- canns/analyzer/data/cell_classification/utils/correlation.py +318 -0
- canns/analyzer/data/cell_classification/utils/geometry.py +442 -0
- canns/analyzer/data/cell_classification/utils/image_processing.py +416 -0
- canns/analyzer/data/cell_classification/visualization/__init__.py +19 -0
- canns/analyzer/data/cell_classification/visualization/grid_plots.py +292 -0
- canns/analyzer/data/cell_classification/visualization/hd_plots.py +200 -0
- canns/analyzer/metrics/__init__.py +2 -1
- canns/analyzer/visualization/core/config.py +46 -4
- canns/data/__init__.py +6 -1
- canns/data/datasets.py +154 -1
- canns/data/loaders.py +37 -0
- canns/pipeline/__init__.py +13 -9
- canns/pipeline/__main__.py +6 -0
- canns/pipeline/asa/runner.py +105 -41
- canns/pipeline/asa_gui/__init__.py +68 -0
- canns/pipeline/asa_gui/__main__.py +6 -0
- canns/pipeline/asa_gui/analysis_modes/__init__.py +42 -0
- canns/pipeline/asa_gui/analysis_modes/base.py +39 -0
- canns/pipeline/asa_gui/analysis_modes/batch_mode.py +21 -0
- canns/pipeline/asa_gui/analysis_modes/cohomap_mode.py +56 -0
- canns/pipeline/asa_gui/analysis_modes/cohospace_mode.py +194 -0
- canns/pipeline/asa_gui/analysis_modes/decode_mode.py +52 -0
- canns/pipeline/asa_gui/analysis_modes/fr_mode.py +81 -0
- canns/pipeline/asa_gui/analysis_modes/frm_mode.py +92 -0
- canns/pipeline/asa_gui/analysis_modes/gridscore_mode.py +123 -0
- canns/pipeline/asa_gui/analysis_modes/pathcompare_mode.py +199 -0
- canns/pipeline/asa_gui/analysis_modes/tda_mode.py +112 -0
- canns/pipeline/asa_gui/app.py +29 -0
- canns/pipeline/asa_gui/controllers/__init__.py +6 -0
- canns/pipeline/asa_gui/controllers/analysis_controller.py +59 -0
- canns/pipeline/asa_gui/controllers/preprocess_controller.py +89 -0
- canns/pipeline/asa_gui/core/__init__.py +15 -0
- canns/pipeline/asa_gui/core/cache.py +14 -0
- canns/pipeline/asa_gui/core/runner.py +1936 -0
- canns/pipeline/asa_gui/core/state.py +324 -0
- canns/pipeline/asa_gui/core/worker.py +260 -0
- canns/pipeline/asa_gui/main_window.py +184 -0
- canns/pipeline/asa_gui/models/__init__.py +7 -0
- canns/pipeline/asa_gui/models/config.py +14 -0
- canns/pipeline/asa_gui/models/job.py +31 -0
- canns/pipeline/asa_gui/models/presets.py +21 -0
- canns/pipeline/asa_gui/resources/__init__.py +16 -0
- canns/pipeline/asa_gui/resources/dark.qss +167 -0
- canns/pipeline/asa_gui/resources/light.qss +163 -0
- canns/pipeline/asa_gui/resources/styles.qss +130 -0
- canns/pipeline/asa_gui/utils/__init__.py +1 -0
- canns/pipeline/asa_gui/utils/formatters.py +15 -0
- canns/pipeline/asa_gui/utils/io_adapters.py +40 -0
- canns/pipeline/asa_gui/utils/validators.py +41 -0
- canns/pipeline/asa_gui/views/__init__.py +1 -0
- canns/pipeline/asa_gui/views/help_content.py +171 -0
- canns/pipeline/asa_gui/views/pages/__init__.py +6 -0
- canns/pipeline/asa_gui/views/pages/analysis_page.py +565 -0
- canns/pipeline/asa_gui/views/pages/preprocess_page.py +492 -0
- canns/pipeline/asa_gui/views/panels/__init__.py +1 -0
- canns/pipeline/asa_gui/views/widgets/__init__.py +21 -0
- canns/pipeline/asa_gui/views/widgets/artifacts_tab.py +44 -0
- canns/pipeline/asa_gui/views/widgets/drop_zone.py +80 -0
- canns/pipeline/asa_gui/views/widgets/file_list.py +27 -0
- canns/pipeline/asa_gui/views/widgets/gridscore_tab.py +308 -0
- canns/pipeline/asa_gui/views/widgets/help_dialog.py +27 -0
- canns/pipeline/asa_gui/views/widgets/image_tab.py +50 -0
- canns/pipeline/asa_gui/views/widgets/image_viewer.py +97 -0
- canns/pipeline/asa_gui/views/widgets/log_box.py +16 -0
- canns/pipeline/asa_gui/views/widgets/pathcompare_tab.py +200 -0
- canns/pipeline/asa_gui/views/widgets/popup_combo.py +25 -0
- canns/pipeline/gallery/__init__.py +15 -5
- canns/pipeline/gallery/__main__.py +11 -0
- canns/pipeline/gallery/app.py +705 -0
- canns/pipeline/gallery/runner.py +790 -0
- canns/pipeline/gallery/state.py +51 -0
- canns/pipeline/gallery/styles.tcss +123 -0
- canns/pipeline/launcher.py +81 -0
- {canns-0.13.1.dist-info → canns-0.14.0.dist-info}/METADATA +11 -1
- canns-0.14.0.dist-info/RECORD +163 -0
- canns-0.14.0.dist-info/entry_points.txt +5 -0
- canns/pipeline/_base.py +0 -50
- canns-0.13.1.dist-info/RECORD +0 -89
- canns-0.13.1.dist-info/entry_points.txt +0 -3
- {canns-0.13.1.dist-info → canns-0.14.0.dist-info}/WHEEL +0 -0
- {canns-0.13.1.dist-info → canns-0.14.0.dist-info}/licenses/LICENSE +0 -0
canns/analyzer/data/asa/path.py
CHANGED
|
@@ -216,7 +216,31 @@ def interp_coords_to_full(idx_map: np.ndarray, coords2: np.ndarray, T_full: int)
|
|
|
216
216
|
return np.mod(out, 2 * np.pi)
|
|
217
217
|
|
|
218
218
|
|
|
219
|
-
def
|
|
219
|
+
def interp_coords_to_full_1d(idx_map: np.ndarray, coords1: np.ndarray, T_full: int) -> np.ndarray:
|
|
220
|
+
"""Interpolate (K,) circular coords back to full length (T_full,1)."""
|
|
221
|
+
idx_map = np.asarray(idx_map).astype(int).ravel()
|
|
222
|
+
coords1 = np.asarray(coords1, float)
|
|
223
|
+
if coords1.ndim == 2 and coords1.shape[1] == 1:
|
|
224
|
+
coords1 = coords1[:, 0]
|
|
225
|
+
if coords1.ndim != 1:
|
|
226
|
+
raise ValueError(f"coords1 must have shape (K,) or (K,1), got {coords1.shape}")
|
|
227
|
+
|
|
228
|
+
order = np.argsort(idx_map)
|
|
229
|
+
idx_map = idx_map[order]
|
|
230
|
+
coords1 = coords1[order]
|
|
231
|
+
|
|
232
|
+
uniq_idx, uniq_pos = np.unique(idx_map, return_index=True)
|
|
233
|
+
coords1 = coords1[uniq_pos]
|
|
234
|
+
idx_map = uniq_idx
|
|
235
|
+
|
|
236
|
+
ang = np.unwrap(coords1)
|
|
237
|
+
full_i = np.arange(T_full, dtype=float)
|
|
238
|
+
out = np.interp(full_i, idx_map.astype(float), ang)
|
|
239
|
+
|
|
240
|
+
return np.mod(out, 2 * np.pi)[:, None]
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def align_coords_to_position_2d(
|
|
220
244
|
t_full: np.ndarray,
|
|
221
245
|
x_full: np.ndarray,
|
|
222
246
|
y_full: np.ndarray,
|
|
@@ -248,7 +272,7 @@ def align_coords_to_position(
|
|
|
248
272
|
|
|
249
273
|
Examples
|
|
250
274
|
--------
|
|
251
|
-
>>> t, x, y, coords2, tag =
|
|
275
|
+
>>> t, x, y, coords2, tag = align_coords_to_position_2d( # doctest: +SKIP
|
|
252
276
|
... t_full, x_full, y_full, coords2,
|
|
253
277
|
... use_box=True, times_box=decoding["times_box"], interp_to_full=True
|
|
254
278
|
... )
|
|
@@ -320,6 +344,84 @@ def align_coords_to_position(
|
|
|
320
344
|
)
|
|
321
345
|
|
|
322
346
|
|
|
347
|
+
def align_coords_to_position_1d(
|
|
348
|
+
t_full: np.ndarray,
|
|
349
|
+
x_full: np.ndarray,
|
|
350
|
+
y_full: np.ndarray,
|
|
351
|
+
coords1: np.ndarray,
|
|
352
|
+
use_box: bool,
|
|
353
|
+
times_box: np.ndarray | None,
|
|
354
|
+
interp_to_full: bool,
|
|
355
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, str]:
|
|
356
|
+
"""Align 1D decoded coordinates to the original (x, y, t) trajectory."""
|
|
357
|
+
t_full = np.asarray(t_full).ravel()
|
|
358
|
+
x_full = np.asarray(x_full).ravel()
|
|
359
|
+
y_full = np.asarray(y_full).ravel()
|
|
360
|
+
coords1 = np.asarray(coords1, float)
|
|
361
|
+
if coords1.ndim == 2 and coords1.shape[1] == 1:
|
|
362
|
+
coords1 = coords1[:, 0]
|
|
363
|
+
if coords1.ndim != 1:
|
|
364
|
+
raise ValueError(f"coords1 must have shape (T,) or (T,1), got {coords1.shape}")
|
|
365
|
+
|
|
366
|
+
T_full = len(t_full)
|
|
367
|
+
|
|
368
|
+
if not use_box:
|
|
369
|
+
if len(coords1) != T_full:
|
|
370
|
+
raise ValueError(
|
|
371
|
+
f"coords length {len(coords1)} != t length {T_full} "
|
|
372
|
+
f"(set --use-box if you have times_box)"
|
|
373
|
+
)
|
|
374
|
+
return t_full, x_full, y_full, coords1[:, None], "full(no-box)"
|
|
375
|
+
|
|
376
|
+
if times_box is None:
|
|
377
|
+
if len(coords1) == T_full:
|
|
378
|
+
return (
|
|
379
|
+
t_full,
|
|
380
|
+
x_full,
|
|
381
|
+
y_full,
|
|
382
|
+
coords1[:, None],
|
|
383
|
+
"full(use-box but no times_box; treated as full)",
|
|
384
|
+
)
|
|
385
|
+
raise KeyError("use_box=True but times_box not found, and coords is not full-length.")
|
|
386
|
+
|
|
387
|
+
idx_map, kind = parse_times_box_to_indices(times_box, t_full)
|
|
388
|
+
|
|
389
|
+
if len(coords1) == T_full and len(idx_map) == T_full:
|
|
390
|
+
return (
|
|
391
|
+
t_full,
|
|
392
|
+
x_full,
|
|
393
|
+
y_full,
|
|
394
|
+
coords1[:, None],
|
|
395
|
+
f"full(coords already full; times_box kind={kind} ignored)",
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
if len(idx_map) != len(coords1):
|
|
399
|
+
raise ValueError(f"times_box length {len(idx_map)} != coords length {len(coords1)}")
|
|
400
|
+
|
|
401
|
+
order = np.argsort(idx_map)
|
|
402
|
+
idx_map = idx_map[order]
|
|
403
|
+
coords1 = coords1[order]
|
|
404
|
+
|
|
405
|
+
if interp_to_full:
|
|
406
|
+
coords_full = interp_coords_to_full_1d(idx_map, coords1, T_full)
|
|
407
|
+
return (
|
|
408
|
+
t_full,
|
|
409
|
+
x_full,
|
|
410
|
+
y_full,
|
|
411
|
+
coords_full,
|
|
412
|
+
f"interp_to_full(times_box kind={kind}, K={len(idx_map)})",
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
idx_map = np.clip(idx_map, 0, T_full - 1)
|
|
416
|
+
return (
|
|
417
|
+
t_full[idx_map],
|
|
418
|
+
x_full[idx_map],
|
|
419
|
+
y_full[idx_map],
|
|
420
|
+
coords1[:, None],
|
|
421
|
+
f"subset(times_box kind={kind}, K={len(idx_map)})",
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
|
|
323
425
|
def snake_wrap_trail_in_parallelogram(
|
|
324
426
|
xy_base: np.ndarray, e1: np.ndarray, e2: np.ndarray
|
|
325
427
|
) -> np.ndarray:
|
|
@@ -237,7 +237,7 @@ def plot_projection(
|
|
|
237
237
|
return fig
|
|
238
238
|
|
|
239
239
|
|
|
240
|
-
def
|
|
240
|
+
def plot_path_compare_2d(
|
|
241
241
|
x: np.ndarray,
|
|
242
242
|
y: np.ndarray,
|
|
243
243
|
coords: np.ndarray,
|
|
@@ -248,14 +248,14 @@ def plot_path_compare(
|
|
|
248
248
|
show: bool = True,
|
|
249
249
|
save_path: str | None = None,
|
|
250
250
|
) -> tuple[plt.Figure, np.ndarray]:
|
|
251
|
-
"""Plot physical path vs decoded coho-space path side-by-side.
|
|
251
|
+
"""Plot physical path vs decoded coho-space path (2D) side-by-side.
|
|
252
252
|
|
|
253
253
|
Parameters
|
|
254
254
|
----------
|
|
255
255
|
x, y : np.ndarray
|
|
256
256
|
Physical position arrays of shape (T,).
|
|
257
257
|
coords : np.ndarray
|
|
258
|
-
Decoded circular coordinates, shape (T,
|
|
258
|
+
Decoded circular coordinates, shape (T, 2) or (T, 2+).
|
|
259
259
|
config : PlotConfig, optional
|
|
260
260
|
Plot configuration. If None, a default config is created.
|
|
261
261
|
title, figsize, show, save_path : optional
|
|
@@ -268,7 +268,7 @@ def plot_path_compare(
|
|
|
268
268
|
|
|
269
269
|
Examples
|
|
270
270
|
--------
|
|
271
|
-
>>> fig, axes =
|
|
271
|
+
>>> fig, axes = plot_path_compare_2d(x, y, coords, show=False) # doctest: +SKIP
|
|
272
272
|
"""
|
|
273
273
|
from .path import draw_base_parallelogram, skew_transform, snake_wrap_trail_in_parallelogram
|
|
274
274
|
|
|
@@ -276,8 +276,8 @@ def plot_path_compare(
|
|
|
276
276
|
y = np.asarray(y).ravel()
|
|
277
277
|
coords = np.asarray(coords)
|
|
278
278
|
|
|
279
|
-
if coords.ndim != 2 or coords.shape[1] <
|
|
280
|
-
raise ValueError(f"coords must be 2D with at least
|
|
279
|
+
if coords.ndim != 2 or coords.shape[1] < 2:
|
|
280
|
+
raise ValueError(f"coords must be 2D with at least 2 columns, got {coords.shape}")
|
|
281
281
|
|
|
282
282
|
config = _ensure_plot_config(
|
|
283
283
|
config,
|
|
@@ -314,19 +314,88 @@ def plot_path_compare(
|
|
|
314
314
|
ax1.set_aspect("equal", "box")
|
|
315
315
|
ax1.axis("off")
|
|
316
316
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
317
|
+
theta2 = coords[:, :2] % (2 * np.pi)
|
|
318
|
+
xy = skew_transform(theta2)
|
|
319
|
+
draw_base_parallelogram(ax1)
|
|
320
|
+
trail = snake_wrap_trail_in_parallelogram(
|
|
321
|
+
xy, np.array([2 * np.pi, 0.0]), np.array([np.pi, np.sqrt(3) * np.pi])
|
|
322
|
+
)
|
|
323
|
+
ax1.plot(trail[:, 0], trail[:, 1], lw=0.9, alpha=0.9)
|
|
324
|
+
|
|
325
|
+
fig.tight_layout()
|
|
326
|
+
_ensure_parent_dir(config.save_path)
|
|
327
|
+
finalize_figure(fig, config)
|
|
328
|
+
return fig, axes
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def plot_path_compare_1d(
|
|
332
|
+
x: np.ndarray,
|
|
333
|
+
y: np.ndarray,
|
|
334
|
+
coords: np.ndarray,
|
|
335
|
+
config: PlotConfig | None = None,
|
|
336
|
+
*,
|
|
337
|
+
title: str = "Path Compare (1D)",
|
|
338
|
+
figsize: tuple[int, int] = (12, 5),
|
|
339
|
+
show: bool = True,
|
|
340
|
+
save_path: str | None = None,
|
|
341
|
+
) -> tuple[plt.Figure, np.ndarray]:
|
|
342
|
+
"""Plot physical path vs decoded coho-space path (1D) side-by-side."""
|
|
343
|
+
x = np.asarray(x).ravel()
|
|
344
|
+
y = np.asarray(y).ravel()
|
|
345
|
+
coords = np.asarray(coords)
|
|
346
|
+
if coords.ndim == 2 and coords.shape[1] == 1:
|
|
347
|
+
coords = coords[:, 0]
|
|
348
|
+
if coords.ndim != 1:
|
|
349
|
+
raise ValueError(f"coords must have shape (T,) or (T, 1), got {coords.shape}")
|
|
350
|
+
|
|
351
|
+
config = _ensure_plot_config(
|
|
352
|
+
config,
|
|
353
|
+
PlotConfig.for_static_plot,
|
|
354
|
+
title=title,
|
|
355
|
+
figsize=figsize,
|
|
356
|
+
save_path=save_path,
|
|
357
|
+
show=show,
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
fig, axes = plt.subplots(1, 2, figsize=config.figsize)
|
|
361
|
+
if config.title:
|
|
362
|
+
fig.suptitle(config.title)
|
|
363
|
+
|
|
364
|
+
ax0 = axes[0]
|
|
365
|
+
ax0.set_title("Physical path (x,y)")
|
|
366
|
+
ax0.set_aspect("equal", "box")
|
|
367
|
+
ax0.plot(x, y, lw=0.9, alpha=0.8)
|
|
368
|
+
ax0.set_xticks([])
|
|
369
|
+
ax0.set_yticks([])
|
|
370
|
+
for spine in ax0.spines.values():
|
|
371
|
+
spine.set_visible(True)
|
|
372
|
+
x_min, x_max = np.min(x), np.max(x)
|
|
373
|
+
y_min, y_max = np.min(y), np.max(y)
|
|
374
|
+
pad_x = (x_max - x_min) * 0.03 if x_max > x_min else 1.0
|
|
375
|
+
pad_y = (y_max - y_min) * 0.03 if y_max > y_min else 1.0
|
|
376
|
+
ax0.set_xlim(x_min - pad_x, x_max + pad_x)
|
|
377
|
+
ax0.set_ylim(y_min - pad_y, y_max + pad_y)
|
|
378
|
+
|
|
379
|
+
ax1 = axes[1]
|
|
380
|
+
ax1.set_title("Decoded coho path (1D)")
|
|
381
|
+
ax1.set_aspect("equal", "box")
|
|
382
|
+
ax1.axis("off")
|
|
383
|
+
|
|
384
|
+
theta = coords % (2 * np.pi)
|
|
385
|
+
x_unit = np.cos(theta)
|
|
386
|
+
y_unit = np.sin(theta)
|
|
387
|
+
sc = ax1.scatter(
|
|
388
|
+
x_unit,
|
|
389
|
+
y_unit,
|
|
390
|
+
c=np.arange(len(theta)),
|
|
391
|
+
cmap="viridis",
|
|
392
|
+
s=4,
|
|
393
|
+
alpha=0.8,
|
|
394
|
+
)
|
|
395
|
+
cbar = plt.colorbar(sc, ax=ax1, fraction=0.046, pad=0.04)
|
|
396
|
+
cbar.set_label("Time")
|
|
397
|
+
ax1.set_xlim(-1.2, 1.2)
|
|
398
|
+
ax1.set_ylim(-1.2, 1.2)
|
|
330
399
|
|
|
331
400
|
fig.tight_layout()
|
|
332
401
|
_ensure_parent_dir(config.save_path)
|
canns/analyzer/data/asa/tda.py
CHANGED
|
@@ -41,7 +41,7 @@ def tda_vis(embed_data: np.ndarray, config: TDAConfig | None = None, **kwargs) -
|
|
|
41
41
|
**kwargs : Any
|
|
42
42
|
Legacy keyword parameters (``dim``, ``num_times``, ``active_times``, ``k``,
|
|
43
43
|
``n_points``, ``metric``, ``nbs``, ``maxdim``, ``coeff``, ``show``,
|
|
44
|
-
``do_shuffle``, ``num_shuffles``, ``progress_bar``).
|
|
44
|
+
``do_shuffle``, ``num_shuffles``, ``progress_bar``, ``standardize``).
|
|
45
45
|
|
|
46
46
|
Returns
|
|
47
47
|
-------
|
|
@@ -77,6 +77,7 @@ def tda_vis(embed_data: np.ndarray, config: TDAConfig | None = None, **kwargs) -
|
|
|
77
77
|
do_shuffle=kwargs.get("do_shuffle", False),
|
|
78
78
|
num_shuffles=kwargs.get("num_shuffles", 1000),
|
|
79
79
|
progress_bar=kwargs.get("progress_bar", True),
|
|
80
|
+
standardize=kwargs.get("standardize", True),
|
|
80
81
|
)
|
|
81
82
|
|
|
82
83
|
try:
|
|
@@ -120,7 +121,7 @@ def _compute_real_persistence(embed_data: np.ndarray, config: TDAConfig) -> dict
|
|
|
120
121
|
|
|
121
122
|
# Step 3: PCA dimensionality reduction
|
|
122
123
|
logging.info("Step 3/5: PCA dimensionality reduction")
|
|
123
|
-
dimred = _apply_pca_reduction(embed_data, movetimes, config.dim)
|
|
124
|
+
dimred = _apply_pca_reduction(embed_data, movetimes, config.dim, config.standardize)
|
|
124
125
|
|
|
125
126
|
# Step 4: Point cloud sampling (denoising)
|
|
126
127
|
logging.info("Step 4/5: Point cloud denoising")
|
|
@@ -156,9 +157,15 @@ def _select_active_timepoints(
|
|
|
156
157
|
return times_cube[movetimes]
|
|
157
158
|
|
|
158
159
|
|
|
159
|
-
def _apply_pca_reduction(
|
|
160
|
+
def _apply_pca_reduction(
|
|
161
|
+
embed_data: np.ndarray, movetimes: np.ndarray, dim: int, standardize: bool
|
|
162
|
+
) -> np.ndarray:
|
|
160
163
|
"""Apply PCA dimensionality reduction."""
|
|
161
|
-
|
|
164
|
+
subset = embed_data[movetimes, :]
|
|
165
|
+
if standardize:
|
|
166
|
+
scaled_data = preprocessing.scale(subset)
|
|
167
|
+
else:
|
|
168
|
+
scaled_data = np.asarray(subset, dtype=np.float32)
|
|
162
169
|
dimred, *_ = _pca(scaled_data, dim=dim)
|
|
163
170
|
return dimred
|
|
164
171
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Cell Classification Package
|
|
3
|
+
|
|
4
|
+
Python implementation of grid cell and head direction cell classification algorithms.
|
|
5
|
+
|
|
6
|
+
Based on the MATLAB code from:
|
|
7
|
+
Vollan, Gardner, Moser & Moser (Nature, 2025)
|
|
8
|
+
"Left-right-alternating sweeps in entorhinal-hippocampal maps of space"
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
__version__ = "0.1.0"
|
|
12
|
+
|
|
13
|
+
from .core import ( # noqa: F401
|
|
14
|
+
GridnessAnalyzer,
|
|
15
|
+
GridnessResult,
|
|
16
|
+
HDCellResult,
|
|
17
|
+
HeadDirectionAnalyzer,
|
|
18
|
+
compute_2d_autocorrelation,
|
|
19
|
+
compute_field_statistics,
|
|
20
|
+
compute_grid_spacing,
|
|
21
|
+
compute_rate_map,
|
|
22
|
+
compute_rate_map_from_binned,
|
|
23
|
+
compute_spatial_information,
|
|
24
|
+
identify_grid_modules_and_stats,
|
|
25
|
+
)
|
|
26
|
+
from .io import MATFileLoader, TuningCurve, Unit # noqa: F401
|
|
27
|
+
from .utils import ( # noqa: F401
|
|
28
|
+
autocorrelation_2d,
|
|
29
|
+
cart2pol,
|
|
30
|
+
circ_dist,
|
|
31
|
+
circ_dist2,
|
|
32
|
+
circ_mean,
|
|
33
|
+
circ_r,
|
|
34
|
+
circ_rtest,
|
|
35
|
+
circ_std,
|
|
36
|
+
fit_ellipse,
|
|
37
|
+
label_connected_components,
|
|
38
|
+
normalized_xcorr2,
|
|
39
|
+
pearson_correlation,
|
|
40
|
+
pol2cart,
|
|
41
|
+
polyarea,
|
|
42
|
+
regionprops,
|
|
43
|
+
rotate_image,
|
|
44
|
+
squared_distance,
|
|
45
|
+
wrap_to_pi,
|
|
46
|
+
)
|
|
47
|
+
from .visualization import ( # noqa: F401
|
|
48
|
+
plot_autocorrelogram,
|
|
49
|
+
plot_grid_score_histogram,
|
|
50
|
+
plot_gridness_analysis,
|
|
51
|
+
plot_hd_analysis,
|
|
52
|
+
plot_polar_tuning,
|
|
53
|
+
plot_rate_map,
|
|
54
|
+
plot_temporal_autocorr,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
__all__ = [
|
|
58
|
+
"GridnessAnalyzer",
|
|
59
|
+
"GridnessResult",
|
|
60
|
+
"HeadDirectionAnalyzer",
|
|
61
|
+
"HDCellResult",
|
|
62
|
+
"compute_2d_autocorrelation",
|
|
63
|
+
"compute_rate_map",
|
|
64
|
+
"compute_rate_map_from_binned",
|
|
65
|
+
"compute_spatial_information",
|
|
66
|
+
"compute_field_statistics",
|
|
67
|
+
"compute_grid_spacing",
|
|
68
|
+
"identify_grid_modules_and_stats",
|
|
69
|
+
"MATFileLoader",
|
|
70
|
+
"TuningCurve",
|
|
71
|
+
"Unit",
|
|
72
|
+
"circ_r",
|
|
73
|
+
"circ_mean",
|
|
74
|
+
"circ_std",
|
|
75
|
+
"circ_dist",
|
|
76
|
+
"circ_dist2",
|
|
77
|
+
"circ_rtest",
|
|
78
|
+
"pearson_correlation",
|
|
79
|
+
"normalized_xcorr2",
|
|
80
|
+
"autocorrelation_2d",
|
|
81
|
+
"fit_ellipse",
|
|
82
|
+
"squared_distance",
|
|
83
|
+
"polyarea",
|
|
84
|
+
"wrap_to_pi",
|
|
85
|
+
"cart2pol",
|
|
86
|
+
"pol2cart",
|
|
87
|
+
"rotate_image",
|
|
88
|
+
"label_connected_components",
|
|
89
|
+
"regionprops",
|
|
90
|
+
"plot_autocorrelogram",
|
|
91
|
+
"plot_gridness_analysis",
|
|
92
|
+
"plot_rate_map",
|
|
93
|
+
"plot_grid_score_histogram",
|
|
94
|
+
"plot_polar_tuning",
|
|
95
|
+
"plot_temporal_autocorr",
|
|
96
|
+
"plot_hd_analysis",
|
|
97
|
+
]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Core analysis modules."""
|
|
2
|
+
|
|
3
|
+
from .grid_cells import GridnessAnalyzer, GridnessResult, compute_2d_autocorrelation
|
|
4
|
+
from .grid_modules_leiden import identify_grid_modules_and_stats
|
|
5
|
+
from .head_direction import HDCellResult, HeadDirectionAnalyzer
|
|
6
|
+
from .spatial_analysis import (
|
|
7
|
+
compute_field_statistics,
|
|
8
|
+
compute_grid_spacing,
|
|
9
|
+
compute_rate_map,
|
|
10
|
+
compute_rate_map_from_binned,
|
|
11
|
+
compute_spatial_information,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"GridnessAnalyzer",
|
|
16
|
+
"compute_2d_autocorrelation",
|
|
17
|
+
"GridnessResult",
|
|
18
|
+
"HeadDirectionAnalyzer",
|
|
19
|
+
"HDCellResult",
|
|
20
|
+
"compute_rate_map",
|
|
21
|
+
"compute_rate_map_from_binned",
|
|
22
|
+
"compute_spatial_information",
|
|
23
|
+
"compute_field_statistics",
|
|
24
|
+
"compute_grid_spacing",
|
|
25
|
+
"identify_grid_modules_and_stats",
|
|
26
|
+
]
|