ic-analysis 0.1.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.
- ic_analysis/__init__.py +124 -0
- ic_analysis/experiment.py +2667 -0
- ic_analysis/loader.py +546 -0
- ic_analysis/metadata.py +346 -0
- ic_analysis/metrics.py +2121 -0
- ic_analysis/plotting.py +2614 -0
- ic_analysis/subjects.py +197 -0
- ic_analysis/workflows/__init__.py +5 -0
- ic_analysis/workflows/place_learning_reversal.py +2627 -0
- ic_analysis-0.1.0.dist-info/METADATA +304 -0
- ic_analysis-0.1.0.dist-info/RECORD +13 -0
- ic_analysis-0.1.0.dist-info/WHEEL +4 -0
- ic_analysis-0.1.0.dist-info/licenses/LICENSE +674 -0
ic_analysis/__init__.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""IntelliCage Analysis Toolkit.
|
|
2
|
+
|
|
3
|
+
The package provides reusable utilities to read IntelliCage exports, define
|
|
4
|
+
script-level experiment and subject metadata, compute behavior metrics, and
|
|
5
|
+
create publication-oriented plots.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from . import metrics as metrics
|
|
14
|
+
from .experiment import IntelliCageExperiment as Experiment
|
|
15
|
+
from .loader import CohortData, load_cohort_data
|
|
16
|
+
from .metadata import ExperimentMetadata, PhaseMetadata, SubjectMetadata, SubjectRegistry
|
|
17
|
+
from .subjects import create_subjects_yaml_template, load_subjects_yaml
|
|
18
|
+
|
|
19
|
+
metric = metrics
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"CohortData",
|
|
23
|
+
"Experiment",
|
|
24
|
+
"ExperimentMetadata",
|
|
25
|
+
"PhaseMetadata",
|
|
26
|
+
"SubjectMetadata",
|
|
27
|
+
"SubjectRegistry",
|
|
28
|
+
"create_subjects_yaml_template",
|
|
29
|
+
"experiment",
|
|
30
|
+
"load_subjects_yaml",
|
|
31
|
+
"load_cohort_data",
|
|
32
|
+
"metric",
|
|
33
|
+
"metrics"]
|
|
34
|
+
__version__ = "0.1.0"
|
|
35
|
+
|
|
36
|
+
def experiment(
|
|
37
|
+
*,
|
|
38
|
+
EXPERIMENT: ExperimentMetadata | dict[str, Any],
|
|
39
|
+
PHASES: dict[int | str, PhaseMetadata | dict[str, Any]] | None = None,
|
|
40
|
+
SUBJECTS: SubjectRegistry | dict[str | int, SubjectMetadata | dict[str, Any]] | None = None) -> Experiment:
|
|
41
|
+
"""Build a generic IntelliCage experiment object from user-script metadata.
|
|
42
|
+
|
|
43
|
+
This is the recommended public entry point after ``import ic_analysis as
|
|
44
|
+
ic``. It validates experiment-level metadata, phase definitions, and the
|
|
45
|
+
subject registry, creates the configured results folder, and returns an
|
|
46
|
+
:class:`ic_analysis.experiment.IntelliCageExperiment` object ready for
|
|
47
|
+
``load()``, ``prepare_analysis()``, and modular plotting calls.
|
|
48
|
+
|
|
49
|
+
:param EXPERIMENT: Experiment metadata as an
|
|
50
|
+
:class:`ExperimentMetadata` instance or a dictionary. Required keys for
|
|
51
|
+
dictionaries are ``name``, ``root_data_path``, ``results_data_path``,
|
|
52
|
+
and ``group_names``. The optional ``mouse_day`` dictionary can define
|
|
53
|
+
``{"start": "06:00", "end": "18:00"}``.
|
|
54
|
+
:param PHASES: Phase definitions as a mapping from phase number to
|
|
55
|
+
:class:`PhaseMetadata` or dictionary. Default is ``None`` when phases
|
|
56
|
+
are embedded in ``EXPERIMENT``. Each phase can define ``short_name``,
|
|
57
|
+
``long_name``, ``folder_name``, ``color``, and
|
|
58
|
+
``scheduled_start_hour``.
|
|
59
|
+
:param SUBJECTS: Subject definitions as a
|
|
60
|
+
:class:`SubjectRegistry` or dictionary. No default is allowed: the
|
|
61
|
+
toolkit deliberately analyzes only explicitly declared animals.
|
|
62
|
+
:returns: A configured :class:`ic_analysis.experiment.IntelliCageExperiment`.
|
|
63
|
+
:raises ValueError: If phases, groups, or subjects are missing or invalid.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
experiment_metadata = _coerce_experiment_metadata(EXPERIMENT, PHASES)
|
|
67
|
+
if SUBJECTS is None:
|
|
68
|
+
raise ValueError("`SUBJECTS` must define the animals included in the analysis.")
|
|
69
|
+
if isinstance(SUBJECTS, SubjectRegistry):
|
|
70
|
+
subjects = SUBJECTS
|
|
71
|
+
else:
|
|
72
|
+
subjects = SubjectRegistry.from_mapping(SUBJECTS)
|
|
73
|
+
experiment_metadata.results_data_path.mkdir(parents=True, exist_ok=True)
|
|
74
|
+
return Experiment(experiment_metadata, subjects)
|
|
75
|
+
|
|
76
|
+
def _coerce_experiment_metadata(
|
|
77
|
+
experiment_value: ExperimentMetadata | dict[str, Any],
|
|
78
|
+
phases_value: dict[int | str, PhaseMetadata | dict[str, Any]] | None) -> ExperimentMetadata:
|
|
79
|
+
"""Normalize public ``ic.experiment`` metadata inputs."""
|
|
80
|
+
|
|
81
|
+
if isinstance(experiment_value, ExperimentMetadata):
|
|
82
|
+
return experiment_value
|
|
83
|
+
data = dict(experiment_value)
|
|
84
|
+
phases = phases_value or data.pop("phases", None) or data.pop("PHASES", None)
|
|
85
|
+
if phases is None:
|
|
86
|
+
raise ValueError("`PHASES` must define at least one experiment phase.")
|
|
87
|
+
mouse_day = data.pop("mouse_day", None) or data.pop("MouseDay", None)
|
|
88
|
+
if isinstance(mouse_day, dict):
|
|
89
|
+
if "start" in mouse_day and "mouse_day_start_time" not in data:
|
|
90
|
+
data["mouse_day_start_time"] = mouse_day["start"]
|
|
91
|
+
if "end" in mouse_day and "mouse_day_end_time" not in data:
|
|
92
|
+
data["mouse_day_end_time"] = mouse_day["end"]
|
|
93
|
+
normalized_phases = _normalize_phase_definitions(phases)
|
|
94
|
+
phase_colors = data.get("phase_colors") or {
|
|
95
|
+
int(number): phase.get("color")
|
|
96
|
+
for number, phase in normalized_phases.items()
|
|
97
|
+
if isinstance(phase, dict) and phase.get("color") is not None}
|
|
98
|
+
group_names = data.get("group_names") or data.get("groups") or data.get("GROUPS")
|
|
99
|
+
if group_names is None:
|
|
100
|
+
raise ValueError("`EXPERIMENT` must define `group_names`.")
|
|
101
|
+
return ExperimentMetadata(
|
|
102
|
+
name=str(data.get("name") or data.get("experiment_name") or data.get("ExperimentName")),
|
|
103
|
+
root_data_path=Path(data.get("root_data_path") or data.get("root") or data.get("data_path")),
|
|
104
|
+
results_data_path=Path(data.get("results_data_path") or data.get("results") or data.get("output_path")),
|
|
105
|
+
phases=normalized_phases,
|
|
106
|
+
group_names=list(group_names),
|
|
107
|
+
group_colors=data.get("group_colors") or {},
|
|
108
|
+
phase_colors=phase_colors,
|
|
109
|
+
optional_phase_numbers=set(data.get("optional_phase_numbers") or []),
|
|
110
|
+
mouse_day_start_hour=float(data.get("mouse_day_start_hour", 6.0)),
|
|
111
|
+
awake_duration_hours=float(data.get("awake_duration_hours", 12.0)),
|
|
112
|
+
mouse_day_start_time=data.get("mouse_day_start_time"),
|
|
113
|
+
mouse_day_end_time=data.get("mouse_day_end_time"),
|
|
114
|
+
experiment_day0_start_hour=data.get("experiment_day0_start_hour"),
|
|
115
|
+
schedule_anchor_phase_number=data.get("schedule_anchor_phase_number"))
|
|
116
|
+
|
|
117
|
+
def _normalize_phase_definitions(
|
|
118
|
+
phases: dict[int | str, PhaseMetadata | dict[str, Any]]) -> dict[int, PhaseMetadata | dict[str, Any]]:
|
|
119
|
+
"""Normalize public phase definitions to integer keys."""
|
|
120
|
+
|
|
121
|
+
normalized: dict[int, PhaseMetadata | dict[str, Any]] = {}
|
|
122
|
+
for phase_number, phase in phases.items():
|
|
123
|
+
normalized[int(phase_number)] = phase
|
|
124
|
+
return normalized
|