Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion placecell/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,11 @@ class BehaviorConfig(BaseModel):
...,
description="Analysis type: 'arena' for 2D open-field, 'maze' for 1D arm analysis.",
)
behavior_fps: float = Field(
20.0,
gt=0.0,
description="Behavior camera sampling rate in frames per second.",
)
speed_threshold: float = Field(
25.0,
description="Minimum running speed to keep events (mm/s).",
Expand Down Expand Up @@ -500,10 +505,23 @@ class AnalysisConfig(BaseModel):
@classmethod
def from_yaml(cls, path: str | Path) -> "AnalysisConfig":
"""Load from a YAML file."""
loader = yaml.SafeLoader
# Old bundles were saved with !!python/tuple tags; handle them safely.
loader.add_constructor(
"tag:yaml.org,2002:python/tuple",
lambda l, n: tuple(l.construct_sequence(n)),
)
with open(path) as f:
data = yaml.safe_load(f)
data = yaml.load(f, Loader=loader)
return cls(**data)

def to_yaml(self, path: str | Path) -> None:
"""Save to a YAML file."""
import json
data = json.loads(self.model_dump_json())
with open(path, "w") as f:
yaml.dump(data, f, default_flow_style=False)

def with_data_overrides(self, data_cfg: BaseDataConfig) -> "AnalysisConfig":
"""Create a new config with data-specific overrides applied.

Expand Down
29 changes: 29 additions & 0 deletions placecell/config/pcell_config_WL27.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
neural:
fps: 20.0
oasis:
g: [1.60, -0.63] # AR(2) coefficients (required, usually overridden by data config)
baseline: p10
penalty: 0.8 # Sparsity penalty (higher = fewer events). Default 0.
s_min: 0 # Minimum event size threshold. Default 0.
trace_name: C_lp

behavior:
type: arena
speed_threshold: 10.0 # mm/s
speed_window_frames: 5
jump_threshold_mm: 100 # Max plausible frame-to-frame displacement (mm). Larger = tracking error.
spatial_map_2d:
bins: 50
min_occupancy: 0.025 # Minimum occupancy (in seconds) to include a bin in spatial map
spatial_sigma: 3 # Gaussian smoothing (in bins) for occupancy and rate maps
n_shuffles: 1000
random_seed: 1
event_threshold_sigma: 0 # Sigma multiplier for event amplitude threshold in trajectory visualization
p_value_threshold: 0.05 # P-value threshold. Units with p < threshold pass.
min_shift_seconds: 20 # Minimum circular shift (seconds) for shuffle test. 0 = no minimum.
si_weight_mode: amplitude # SI weight mode: 'amplitude' (event amplitudes) or 'binary' (event counts)
place_field_threshold: 0.35 # Fraction of peak rate for place field boundary (red contour). Applied to smoothed rate map.
place_field_min_bins: 5 # Minimum contiguous bins for a place field component. Smaller disconnected regions are discarded.
place_field_seed_percentile: 95 # Percentile of shuffled rates for seed detection (Guo et al. 2023).
n_split_blocks: 10 # Number of temporal blocks for interleaved stability splitting.
block_shifts: [0] # Block boundary shifts (fractions of block width). [0, 0.5] for two arrangements.
13 changes: 11 additions & 2 deletions placecell/dataset/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,8 +449,17 @@ def save_bundle(self, path: str | Path, *, save_figures: bool = True) -> Path:
}
(path / "metadata.json").write_text(json.dumps(meta, indent=2))

# Config
self.cfg.to_yaml(path / "config.yaml")
# Config — propagate behavior_fps from data_cfg so bundles are self-contained
cfg_to_save = self.cfg
if self.data_cfg is not None and hasattr(self.data_cfg, "behavior_fps"):
cfg_to_save = self.cfg.model_copy(
update={
"behavior": self.cfg.behavior.model_copy(
update={"behavior_fps": self.data_cfg.behavior_fps}
)
}
)
cfg_to_save.to_yaml(path / "config.yaml")

# Spatial arrays
spatial_kw: dict[str, np.ndarray] = {}
Expand Down
Loading