Analysis¶
Convert source data without the GUI¶
You can convert supported recordings into a *_ephyr experiment folder from Python with EphyrIO,
without opening the desktop app. The result is the same layout as File → Open in the GUI
(see Files Format for supported sources and folder structure).
convert_from_source_to_ephyr is a generator: it yields progress percentages while writing
header.json and the data/ memmaps. By default the output folder is created next to the source as
{stem}_ephyr; pass out_dir to override that path.
from pathlib import Path
from ephyr.converter.ephyr_io import EphyrIO
source_path = Path("/path/to/recording") # file or folder, depending on format
out_ephyr_folder = source_path.parent / f"{source_path.stem}_ephyr"
for progress in EphyrIO.convert_from_source_to_ephyr(source_path):
print(f"\rProgress: {progress}%", end="", flush=True)
print("Created:", out_ephyr_folder)
After conversion, open the resulting folder in the GUI or load it with EphyrSessionManager as below.
Usage of the Labeled Data¶
After you annotate an experiment in Ephyr, you can load the same *_ephyr folder from Python and work
with signals, sessions, events, periods, and add-on outputs.
You can also generate a starter script from the GUI (Add-ons → Generate script). The example below follows the same API and extends it with reading spike detection results from disk.
Main objects¶
| Object | Role |
|---|---|
EphyrSessionManager |
Entry point: load an experiment folder, switch sessions, access experiment_data and user_session |
ExperimentData |
header plus data_memmaps[sweep_idx][channel_idx] and voltage conversion helpers |
Header |
Sample rate, sweep/channel counts, channel names, units, ranges, and source provenance |
UserSession |
Events, periods, vocabularies, experiment description, and gui_setup |
GuiSetup |
View state: current sweep/window, channel groups, filters, visibility flags, add-on toggles |
events_table |
Convenience view of events with names and overlapping period names |
Header (high level)¶
- Provenance:
type_before_conversion,name_before_conversion, creation date/time before conversion - Timing:
sample_interval_microseconds,sample_rate - Shape:
number_of_channels,number_of_sweeps,number_of_points_per_sweep channel_info: per-channelname,probe,units, analog/digital min/max, prefiltering, points, impedance
UserSession / GuiSetup (high level)¶
- Labels:
events,events_vocabulary,periods,periods_vocabulary - Text:
experiment_description - View:
gui_setup(current_sweep_idx,start_point,duration_ms,channels_groups,channels_setup, visibility flags such astraces_are_shown/channel_names_are_shown/events_are_shown/periods_are_shown, …) events_tablerows:name,sweep_idx,time_ms,is_bad,periods(list of period names)
Example script¶
import json
from pathlib import Path
from ephyr.core.ephyr_session import EphyrSessionManager, UserSession
# Init session
OUT_EPHYR_FOLDER = Path("/path/to/experiment_ephyr")
session = EphyrSessionManager()
session.init_from_folder(OUT_EPHYR_FOLDER)
# Work with data
print(session.experiment_data.header)
sweep_idx, start_point, end_point = 0, 0, 10_000
for ch_idx in range(session.experiment_data.header.number_of_channels):
channel_data = session.experiment_data.data_memmaps[sweep_idx][ch_idx][start_point:end_point]
print(
f"Channel {session.experiment_data.header.channel_info.name[ch_idx]} max voltage val: ",
max(session.experiment_data.from_int16_to_voltage_val(channel_data, ch_idx)),
)
# Work with GUI session
session_filename = UserSession.session_name_to_filename("your_session")
session.switch_sessions(session_filename)
print(session.current_user_session.gui_setup)
# Work with events
for event in session.user_session.events_table:
# skip events in period
if "PERIOD_NAME" in event.periods:
continue
print(
f"event={event.name} sweep={event.sweep_idx} "
f"time_ms={event.time_ms} bad={event.is_bad} periods={event.periods}"
)
# Work with periods
for period in session.user_session.periods:
period_name = session.user_session.get_period_vocabulary_name(period.period_name_id)
print(
f"period={period_name} "
f"start=({period.start_sweep_idx}, {period.start_time_ms} ms) "
f"end=({period.end_sweep_idx}, {period.end_time_ms} ms)"
)
# Work with spikes (Spike detection add-on results under add_ons/data)
# Each result directory contains files named "{sweep_idx}.spikes.json" (SpikesPayload).
spikes_root = Path(OUT_EPHYR_FOLDER) / "add_ons" / "data" / "spike_detection"
if spikes_root.exists():
for result_dir in spikes_root.iterdir():
if not result_dir.is_dir():
continue
for spikes_path in sorted(result_dir.glob("*.spikes.json")):
payload = json.loads(spikes_path.read_text(encoding="utf-8"))
sweep_idx = payload["sweep_idx"]
for ch_idx, spikes in payload["spikes_by_channel"].items():
print("Sweep", sweep_idx, "channel", ch_idx, "spikes:", spikes)
Spike payload shape¶
Spike utils (Spike detection) write JSON files that match the SpikesPayload model. Important fields:
| Field | Meaning |
|---|---|
sweep_idx |
Sweep the detection was run on |
sample_rate |
Sample rate used during detection |
detector_name, threshold, polarity flags, merge window, ignore rules |
Detection parameters |
spikes_by_channel |
Map of channel index → list of spikes |
| Each spike | time_ms, value, polarity, optional sample_idx |
Reading with the standard library json module keeps analysis scripts independent of the add-on package.
Add-on authors can instead use SpikesPayload / read_spikes_payload from the Spike utils shared helpers.
Notes¶
- Indexing for memmaps is
[sweep_idx][channel_idx], then a sample slice. from_int16_to_voltage_valscales using the channel’s analog/digital range and units (results in µV-oriented values used by the GUI pipeline).session.user_sessionandsession.current_user_sessionrefer to the active session afterswitch_sessions/new_user_session.- Run analysis scripts in the same Python environment where
ephyris installed (pip install ephyr).