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
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
QWidget {
|
|
2
|
+
font-family: "Avenir Next", "Avenir", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
|
3
|
+
font-size: 13px;
|
|
4
|
+
color: #2c2f33;
|
|
5
|
+
background: #f6f4ef;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
QLabel {
|
|
9
|
+
background: transparent;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
QGroupBox {
|
|
13
|
+
font-weight: 600;
|
|
14
|
+
border: 1px solid #d8d4cc;
|
|
15
|
+
border-radius: 10px;
|
|
16
|
+
margin-top: 14px;
|
|
17
|
+
background: #ffffff;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
QGroupBox::title {
|
|
21
|
+
subcontrol-origin: margin;
|
|
22
|
+
left: 12px;
|
|
23
|
+
padding: 0 6px 0 6px;
|
|
24
|
+
color: #3b4a58;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
QPushButton {
|
|
28
|
+
background: #efece6;
|
|
29
|
+
border: 1px solid #cdc7bc;
|
|
30
|
+
border-radius: 8px;
|
|
31
|
+
padding: 6px 12px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
QPushButton:hover {
|
|
35
|
+
background: #e7e2da;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
QPushButton:pressed {
|
|
39
|
+
background: #ded7cc;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
QPushButton:disabled {
|
|
43
|
+
color: #9a968f;
|
|
44
|
+
background: #f2f0ec;
|
|
45
|
+
border: 1px solid #e2ded7;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
QPushButton#PrimaryButton {
|
|
49
|
+
background: #1b8a6a;
|
|
50
|
+
color: #ffffff;
|
|
51
|
+
border: 1px solid #17785c;
|
|
52
|
+
font-weight: 600;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
QPushButton#PrimaryButton:hover {
|
|
56
|
+
background: #187e61;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
QPushButton#DangerButton {
|
|
60
|
+
background: #c4423f;
|
|
61
|
+
color: #ffffff;
|
|
62
|
+
border: 1px solid #a93735;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
QLineEdit,
|
|
66
|
+
QComboBox,
|
|
67
|
+
QSpinBox,
|
|
68
|
+
QDoubleSpinBox {
|
|
69
|
+
background: #ffffff;
|
|
70
|
+
border: 1px solid #cdc7bc;
|
|
71
|
+
border-radius: 6px;
|
|
72
|
+
padding: 4px 6px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
QLineEdit:disabled,
|
|
76
|
+
QComboBox:disabled,
|
|
77
|
+
QSpinBox:disabled,
|
|
78
|
+
QDoubleSpinBox:disabled {
|
|
79
|
+
background: #f5f3ef;
|
|
80
|
+
color: #a39f97;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
QTabWidget::pane {
|
|
84
|
+
border: 1px solid #d8d4cc;
|
|
85
|
+
border-radius: 10px;
|
|
86
|
+
background: #ffffff;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
QTabBar::tab {
|
|
90
|
+
background: #eae6df;
|
|
91
|
+
border: 1px solid #d8d4cc;
|
|
92
|
+
border-bottom: none;
|
|
93
|
+
border-top-left-radius: 8px;
|
|
94
|
+
border-top-right-radius: 8px;
|
|
95
|
+
padding: 6px 12px;
|
|
96
|
+
margin-right: 2px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
QTabBar::tab:selected {
|
|
100
|
+
background: #ffffff;
|
|
101
|
+
color: #1b8a6a;
|
|
102
|
+
font-weight: 600;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
QTabBar::tab:hover {
|
|
106
|
+
background: #f1eee8;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
QProgressBar {
|
|
110
|
+
border: 1px solid #cfcac1;
|
|
111
|
+
border-radius: 6px;
|
|
112
|
+
text-align: center;
|
|
113
|
+
background: #f2f0ec;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
QProgressBar::chunk {
|
|
117
|
+
background-color: #1b8a6a;
|
|
118
|
+
border-radius: 6px;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
QTextEdit,
|
|
122
|
+
QListWidget {
|
|
123
|
+
background: #ffffff;
|
|
124
|
+
border: 1px solid #d8d4cc;
|
|
125
|
+
border-radius: 8px;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
QSplitter::handle {
|
|
129
|
+
background: #ddd7cd;
|
|
130
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Utility modules for ASA GUI."""
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Formatting helpers for ASA GUI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def format_duration(seconds: float) -> str:
|
|
7
|
+
if seconds < 0:
|
|
8
|
+
return "0s"
|
|
9
|
+
if seconds < 60:
|
|
10
|
+
return f"{seconds:.1f}s"
|
|
11
|
+
minutes = seconds / 60
|
|
12
|
+
if minutes < 60:
|
|
13
|
+
return f"{minutes:.1f}m"
|
|
14
|
+
hours = minutes / 60
|
|
15
|
+
return f"{hours:.1f}h"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""I/O adapters for loading ASA GUI inputs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def load_asa_npz(path: Path) -> dict:
|
|
11
|
+
data = np.load(path, allow_pickle=True)
|
|
12
|
+
return {k: data[k] for k in data.files}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def pack_neuron_traj_to_asa(neuron_path: Path, traj_path: Path, out_path: Path) -> Path:
|
|
16
|
+
"""Pack neuron and trajectory arrays into ASA-style .npz.
|
|
17
|
+
|
|
18
|
+
Minimal format support: neuron_path is .npy (T,N), traj_path is .npy (T,2).
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
neural = np.load(neuron_path)
|
|
22
|
+
traj = np.load(traj_path)
|
|
23
|
+
|
|
24
|
+
if neural.ndim != 2:
|
|
25
|
+
raise ValueError(f"neural data must be (T,N). got {neural.shape}")
|
|
26
|
+
if traj.ndim != 2 or traj.shape[1] != 2:
|
|
27
|
+
raise ValueError(f"traj must be (T,2). got {traj.shape}")
|
|
28
|
+
if neural.shape[0] != traj.shape[0]:
|
|
29
|
+
raise ValueError("T mismatch between neural and trajectory")
|
|
30
|
+
|
|
31
|
+
t = np.arange(neural.shape[0])
|
|
32
|
+
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
33
|
+
np.savez_compressed(
|
|
34
|
+
out_path,
|
|
35
|
+
spike=neural.astype(np.float32),
|
|
36
|
+
x=traj[:, 0],
|
|
37
|
+
y=traj[:, 1],
|
|
38
|
+
t=t,
|
|
39
|
+
)
|
|
40
|
+
return out_path
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Validation helpers for ASA GUI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def validate_asa_npz(path: Path) -> tuple[bool, str]:
|
|
11
|
+
if not path.exists():
|
|
12
|
+
return False, f"File not found: {path}"
|
|
13
|
+
try:
|
|
14
|
+
data = np.load(path, allow_pickle=True)
|
|
15
|
+
except Exception as e:
|
|
16
|
+
return False, f"Failed to load npz: {e}"
|
|
17
|
+
|
|
18
|
+
required = {"spike", "t"}
|
|
19
|
+
missing = [k for k in required if k not in data.files]
|
|
20
|
+
if missing:
|
|
21
|
+
return False, f"Missing keys in asa npz: {missing}"
|
|
22
|
+
return True, ""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def validate_neuron_traj(neuron_path: Path, traj_path: Path) -> tuple[bool, str]:
|
|
26
|
+
if not neuron_path.exists():
|
|
27
|
+
return False, f"Neuron file not found: {neuron_path}"
|
|
28
|
+
if not traj_path.exists():
|
|
29
|
+
return False, f"Trajectory file not found: {traj_path}"
|
|
30
|
+
try:
|
|
31
|
+
neuron = np.load(neuron_path)
|
|
32
|
+
traj = np.load(traj_path)
|
|
33
|
+
except Exception as e:
|
|
34
|
+
return False, f"Failed to load input arrays: {e}"
|
|
35
|
+
if neuron.ndim != 2:
|
|
36
|
+
return False, f"Neuron data must be (T,N), got {neuron.shape}"
|
|
37
|
+
if traj.ndim != 2 or traj.shape[1] != 2:
|
|
38
|
+
return False, f"Trajectory must be (T,2), got {traj.shape}"
|
|
39
|
+
if neuron.shape[0] != traj.shape[0]:
|
|
40
|
+
return False, "Neuron and trajectory length mismatch"
|
|
41
|
+
return True, ""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""UI views for ASA GUI."""
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""Help content for ASA GUI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def preprocess_help_markdown(lang: str = "en") -> str:
|
|
7
|
+
if str(lang).lower().startswith("zh"):
|
|
8
|
+
return (
|
|
9
|
+
"# Preprocess 说明\n"
|
|
10
|
+
"\n"
|
|
11
|
+
"- **输入格式**:仅支持 ASA `.npz`,需要包含 `spike`, `x`, `y`, `t`。\n"
|
|
12
|
+
"- **Embed spike trains**:将脉冲事件嵌入为密集矩阵,供 TDA / FR / FRM 使用。\n"
|
|
13
|
+
"\n"
|
|
14
|
+
"## Embed 参数\n"
|
|
15
|
+
"- `res, dt`:时间分箱/步长,需与 `t` 的单位一致。\n"
|
|
16
|
+
"- `sigma`:平滑尺度(越大越平滑,但会模糊快变化)。\n"
|
|
17
|
+
"- `smooth`:是否启用平滑。\n"
|
|
18
|
+
"- `speed_filter` + `min_speed`:过滤低速时间点(常见于 grid cell 数据)。\n"
|
|
19
|
+
" 启用后解码会生成 `times_box`,后续 CohoMap/PathCompare/CohoSpace\n"
|
|
20
|
+
" 需要用 `times_box` 对齐坐标与 `x/y/t`。\n"
|
|
21
|
+
)
|
|
22
|
+
return (
|
|
23
|
+
"# Preprocess Guide\n"
|
|
24
|
+
"\n"
|
|
25
|
+
"- **Input format**: ASA `.npz` with `spike`, `x`, `y`, `t`.\n"
|
|
26
|
+
"- **Embed spike trains**: build a dense matrix for TDA / FR / FRM.\n"
|
|
27
|
+
"\n"
|
|
28
|
+
"## Embed parameters\n"
|
|
29
|
+
"- `res, dt`: time bin / step size (same unit as `t`).\n"
|
|
30
|
+
"- `sigma`: smoothing scale (larger → smoother).\n"
|
|
31
|
+
"- `smooth`: enable smoothing.\n"
|
|
32
|
+
"- `speed_filter` + `min_speed`: remove low-speed samples (common for grid data).\n"
|
|
33
|
+
" When enabled, decoding yields `times_box`; downstream plots must align with it.\n"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def analysis_help_markdown(mode: str | None, lang: str = "en") -> str:
|
|
38
|
+
if str(lang).lower().startswith("zh"):
|
|
39
|
+
base = (
|
|
40
|
+
"# ASA 分析流程\n"
|
|
41
|
+
"\n"
|
|
42
|
+
"TDA → 解码 → CohoMap / PathCompare / CohoSpace / FR / FRM\n"
|
|
43
|
+
"\n"
|
|
44
|
+
"- 若 preprocess 启用了 `speed_filter`,解码会输出 `times_box`。\n"
|
|
45
|
+
" 绘图时需用 `times_box` 对齐,否则可能出现长度不匹配。\n"
|
|
46
|
+
)
|
|
47
|
+
mode_text = {
|
|
48
|
+
"tda": (
|
|
49
|
+
"## TDA 参数\n"
|
|
50
|
+
"- `num_times`:时间下采样步长(越大越快,但可能丢细节)。\n"
|
|
51
|
+
"- `active_times`:选取最活跃时间点数(过小不稳,过大更慢/更噪)。\n"
|
|
52
|
+
"- `dim`:PCA 维度(常见起点 6–12)。\n"
|
|
53
|
+
"- `k`, `n_points`, `nbs`:采样/邻域构建相关,影响速度与稳定性。\n"
|
|
54
|
+
"- `metric`:推荐 `cosine`。\n"
|
|
55
|
+
"- `maxdim`:建议先用 1 确认环结构,再尝试 2 看 torus。\n"
|
|
56
|
+
"- `coeff`:有限域系数(默认 47)。\n"
|
|
57
|
+
"- `Shuffle`:用于显著性检验,代价高,建议少量。\n"
|
|
58
|
+
),
|
|
59
|
+
"cohomap": (
|
|
60
|
+
"## CohoMap\n"
|
|
61
|
+
"- 将解码得到的相位轨迹投影回真实 `x/y` 轨迹。\n"
|
|
62
|
+
"- 若启用 `speed_filter`,请使用 `coordsbox/times_box` 对齐。\n"
|
|
63
|
+
),
|
|
64
|
+
"pathcompare": (
|
|
65
|
+
"## PathCompare\n"
|
|
66
|
+
"- 对比真实路径与解码路径的对齐效果。\n"
|
|
67
|
+
"- `dim_mode`:1D/2D;`dim/dim1/dim2` 对应解码维度。\n"
|
|
68
|
+
"- `use_box`:使用 `coordsbox/times_box` 对齐(speed_filter 时建议开启)。\n"
|
|
69
|
+
"- `tmin/tmax` 或 `imin/imax`:截取区间。\n"
|
|
70
|
+
"- `stride`:抽样步长;`tail`:尾迹长度;`fps`:动画帧率。\n"
|
|
71
|
+
),
|
|
72
|
+
"cohospace": (
|
|
73
|
+
"## CohoSpace / CohoScore\n"
|
|
74
|
+
"- 查看神经元在相位空间中的偏好分布。\n"
|
|
75
|
+
"- `top_percent`:选取活跃点百分比。\n"
|
|
76
|
+
"- `view`:single neuron / population;`top_k` 仅对 population 生效。\n"
|
|
77
|
+
"- `use_best`:使用 CohoScore 最小(更集中)的 neuron。\n"
|
|
78
|
+
"- `unfold=skew`:在 skewed torus 平铺展示(2D 才可用)。\n"
|
|
79
|
+
),
|
|
80
|
+
"fr": (
|
|
81
|
+
"## FR Heatmap\n"
|
|
82
|
+
"- 群体放电热图。\n"
|
|
83
|
+
"- `neuron_range` / `time_range`:裁剪范围。\n"
|
|
84
|
+
"- `normalize`:归一化方式;`none` 表示不归一化。\n"
|
|
85
|
+
"- `mode=fr` 需要 preprocess;`spike` 可直接用事件。\n"
|
|
86
|
+
),
|
|
87
|
+
"frm": (
|
|
88
|
+
"## FRM\n"
|
|
89
|
+
"- 单神经元空间放电图。\n"
|
|
90
|
+
"- `neuron_id`:要看的神经元。\n"
|
|
91
|
+
"- `bin_size`:空间分箱大小;`min_occupancy`:最小占据数。\n"
|
|
92
|
+
"- `smoothing/smooth_sigma`:平滑选项。\n"
|
|
93
|
+
),
|
|
94
|
+
"gridscore": (
|
|
95
|
+
"## GridScore\n"
|
|
96
|
+
"- 基于自相关图计算 gridness。\n"
|
|
97
|
+
"- `bins`:空间分箱数;`min_occupancy`:最小占据。\n"
|
|
98
|
+
"- `smoothing/sigma`:平滑选项(需 scipy)。\n"
|
|
99
|
+
"- `overlap`:自相关重叠比例。\n"
|
|
100
|
+
"- `mode=fr` 需要 preprocess;`spike` 可直接用事件。\n"
|
|
101
|
+
),
|
|
102
|
+
}
|
|
103
|
+
return f"{base}\n{mode_text.get(str(mode or ''), '')}".strip()
|
|
104
|
+
|
|
105
|
+
base = (
|
|
106
|
+
"# ASA Analysis Flow\n"
|
|
107
|
+
"\n"
|
|
108
|
+
"TDA → decode → CohoMap / PathCompare / CohoSpace / FR / FRM\n"
|
|
109
|
+
"\n"
|
|
110
|
+
"- If `speed_filter` is enabled in preprocess, decoding yields `times_box`.\n"
|
|
111
|
+
" Downstream plots must align with `times_box` to avoid length mismatches.\n"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
mode_text = {
|
|
115
|
+
"tda": (
|
|
116
|
+
"## TDA parameters\n"
|
|
117
|
+
"- `num_times`: time downsampling step (larger is faster but less detail).\n"
|
|
118
|
+
"- `active_times`: number of most active points (too small = unstable).\n"
|
|
119
|
+
"- `dim`: PCA dimension (typical 6–12).\n"
|
|
120
|
+
"- `k`, `n_points`, `nbs`: sampling / neighborhood parameters.\n"
|
|
121
|
+
"- `metric`: distance metric (recommend `cosine`).\n"
|
|
122
|
+
"- `maxdim`: start with 1; try 2 for torus.\n"
|
|
123
|
+
"- `coeff`: finite field coefficient (default 47).\n"
|
|
124
|
+
"- `Shuffle`: significance test; expensive, keep small.\n"
|
|
125
|
+
),
|
|
126
|
+
"cohomap": (
|
|
127
|
+
"## CohoMap\n"
|
|
128
|
+
"- Project decoded phase back to real `x/y` trajectory.\n"
|
|
129
|
+
"- If `speed_filter` is on, use `coordsbox/times_box` for alignment.\n"
|
|
130
|
+
),
|
|
131
|
+
"pathcompare": (
|
|
132
|
+
"## PathCompare\n"
|
|
133
|
+
"- Compare decoded vs real trajectory.\n"
|
|
134
|
+
"- `dim_mode`: 1D/2D; `dim/dim1/dim2` select decoded dimensions.\n"
|
|
135
|
+
"- `use_box`: align with `coordsbox/times_box` (recommended with speed_filter).\n"
|
|
136
|
+
"- `tmin/tmax` or `imin/imax`: crop range.\n"
|
|
137
|
+
"- `stride`: sampling step; `tail`: trail length; `fps`: animation rate.\n"
|
|
138
|
+
),
|
|
139
|
+
"cohospace": (
|
|
140
|
+
"## CohoSpace / CohoScore\n"
|
|
141
|
+
"- Visualize neuron preference in phase space.\n"
|
|
142
|
+
"- `top_percent`: active percentile threshold.\n"
|
|
143
|
+
"- `view`: single neuron / population; `top_k` for population only.\n"
|
|
144
|
+
"- `use_best`: pick neuron with smallest CohoScore (most selective).\n"
|
|
145
|
+
"- `unfold=skew`: skewed torus tiling (2D only).\n"
|
|
146
|
+
),
|
|
147
|
+
"fr": (
|
|
148
|
+
"## FR Heatmap\n"
|
|
149
|
+
"- Population firing-rate heatmap.\n"
|
|
150
|
+
"- `neuron_range` / `time_range`: crop.\n"
|
|
151
|
+
"- `normalize`: normalization method; `none` = no normalization.\n"
|
|
152
|
+
"- `mode=fr` requires preprocess; `spike` uses events directly.\n"
|
|
153
|
+
),
|
|
154
|
+
"frm": (
|
|
155
|
+
"## FRM\n"
|
|
156
|
+
"- Single-neuron spatial firing map.\n"
|
|
157
|
+
"- `neuron_id`: target neuron.\n"
|
|
158
|
+
"- `bin_size`: spatial bins; `min_occupancy`: minimum occupancy.\n"
|
|
159
|
+
"- `smoothing/smooth_sigma`: smoothing options.\n"
|
|
160
|
+
),
|
|
161
|
+
"gridscore": (
|
|
162
|
+
"## GridScore\n"
|
|
163
|
+
"- Compute gridness from autocorrelation.\n"
|
|
164
|
+
"- `bins`: spatial bin count; `min_occupancy`: minimum occupancy.\n"
|
|
165
|
+
"- `smoothing/sigma`: smoothing (requires scipy).\n"
|
|
166
|
+
"- `overlap`: autocorr overlap ratio.\n"
|
|
167
|
+
"- `mode=fr` requires preprocess; `spike` uses events directly.\n"
|
|
168
|
+
),
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return f"{base}\n{mode_text.get(str(mode or ''), '')}".strip()
|