CSV Format¶
sleap-io supports multiple CSV formats for exporting pose tracking data. Each format has different structures suited for various analysis workflows.
Supported Formats¶
| Format | Description | Best For |
|---|---|---|
sleap |
SLEAP Analysis CSV | Native SLEAP exports, one row per instance |
dlc |
DeepLabCut format | DLC compatibility, multi-header structure |
points |
One row per point | Database imports, normalized data |
instances |
One row per instance | Analysis pipelines |
frames |
One row per frame | Time series analysis, wide format |
Format Details¶
SLEAP Format (sleap)¶
The default SLEAP Analysis CSV format produces one row per instance.
Columns:
| Column | Type | Description |
|---|---|---|
track |
string | Track name (empty if untracked) |
frame_idx |
int | Frame index in video |
instance.score |
float | Instance confidence score |
{node}.x |
float | X coordinate for node |
{node}.y |
float | Y coordinate for node |
{node}.score |
float | Point confidence score |
Example:
track,frame_idx,instance.score,nose.x,nose.y,nose.score,tail.x,tail.y,tail.score
mouse_1,0,0.95,100.5,200.3,0.98,150.2,250.1,0.92
mouse_1,1,0.94,102.1,198.5,0.97,148.8,248.3,0.91
mouse_2,0,0.93,300.2,400.1,0.96,350.5,450.8,0.89
DeepLabCut Format (dlc)¶
Multi-header format compatible with DeepLabCut analysis tools.
Structure:
- Row 1: Scorer name (repeated)
- Row 2: Body part names (repeated for x, y, likelihood)
- Row 3: Coordinate type (x, y, likelihood)
- Data rows: One per frame
Example:
scorer,MyModel,MyModel,MyModel,MyModel,MyModel,MyModel
bodyparts,nose,nose,nose,tail,tail,tail
coords,x,y,likelihood,x,y,likelihood
0,100.5,200.3,0.98,150.2,250.1,0.92
1,102.1,198.5,0.97,148.8,248.3,0.91
Multi-animal DLC format adds an individuals row:
scorer,MyModel,MyModel,MyModel,MyModel,MyModel,MyModel
individuals,mouse_1,mouse_1,mouse_1,mouse_2,mouse_2,mouse_2
bodyparts,nose,nose,nose,nose,nose,nose
coords,x,y,likelihood,x,y,likelihood
0,100.5,200.3,0.98,300.2,400.1,0.96
Points Format (points)¶
The most normalized format with one row per point. Ideal for database imports.
Columns:
| Column | Type | Description |
|---|---|---|
video_path |
string | Path to video file |
frame_idx |
int | Frame index |
track |
string | Track name |
instance_idx |
int | Instance index within frame |
instance_score |
float | Instance confidence |
node |
string | Node/keypoint name |
x |
float | X coordinate |
y |
float | Y coordinate |
score |
float | Point confidence |
Example:
video_path,frame_idx,track,instance_idx,instance_score,node,x,y,score
video.mp4,0,mouse_1,0,0.95,nose,100.5,200.3,0.98
video.mp4,0,mouse_1,0,0.95,tail,150.2,250.1,0.92
video.mp4,0,mouse_2,1,0.93,nose,300.2,400.1,0.96
video.mp4,0,mouse_2,1,0.93,tail,350.5,450.8,0.89
Instances Format (instances)¶
One row per instance with all node coordinates as columns.
Columns:
| Column | Type | Description |
|---|---|---|
video_path |
string | Path to video file |
frame_idx |
int | Frame index |
track |
string | Track name |
instance_idx |
int | Instance index within frame |
instance_score |
float | Instance confidence |
{node}.x |
float | X coordinate for node |
{node}.y |
float | Y coordinate for node |
{node}.score |
float | Point confidence for node |
Example:
video_path,frame_idx,track,instance_idx,instance_score,nose.x,nose.y,nose.score,tail.x,tail.y,tail.score
video.mp4,0,mouse_1,0,0.95,100.5,200.3,0.98,150.2,250.1,0.92
video.mp4,0,mouse_2,1,0.93,300.2,400.1,0.96,350.5,450.8,0.89
video.mp4,1,mouse_1,0,0.94,102.1,198.5,0.97,148.8,248.3,0.91
Frames Format (frames)¶
One row per frame with all instances multiplexed into columns. Best for time series analysis.
Columns:
| Column | Type | Description |
|---|---|---|
frame_idx |
int | Frame index |
video_path |
string | Path to video file |
inst{N}.{node}.x |
float | X coordinate for instance N, node |
inst{N}.{node}.y |
float | Y coordinate for instance N, node |
inst{N}.{node}.score |
float | Point confidence for instance N, node |
Example:
frame_idx,video_path,inst0.nose.x,inst0.nose.y,inst0.nose.score,inst0.tail.x,inst0.tail.y,inst0.tail.score,inst1.nose.x,inst1.nose.y,inst1.nose.score,inst1.tail.x,inst1.tail.y,inst1.tail.score
0,video.mp4,100.5,200.3,0.98,150.2,250.1,0.92,300.2,400.1,0.96,350.5,450.8,0.89
1,video.mp4,102.1,198.5,0.97,148.8,248.3,0.91,,,,,,
Empty Frames
When all_frames=True (or include_empty=True), frames without instances are included with NaN values for all coordinates.
Frame Padding¶
By default, CSV export only includes frames with instances. To include all frames:
import sleap_io as sio
labels = sio.load_slp("predictions.slp")
# Include empty frames (padded with NaN)
sio.save_csv(labels, "all_frames.csv", include_empty=True)
# Only frames with instances (sparse)
sio.save_csv(labels, "sparse.csv", include_empty=False)
# Specific frame range
sio.save_csv(labels, "clip.csv", start_frame=100, end_frame=500)
When include_empty=True and no explicit end_frame is given, padding spans the
full video length when it is known (e.g. instances stopping at frame 1000 in a
2000-frame video still export rows through frame 1999). This matches the Analysis
HDF5 export. If the video length cannot be resolved, padding falls back to the last
labeled frame.
Metadata Sidecar¶
CSV files cannot store all Labels information (skeleton edges, symmetries, suggestions). To enable full round-trip reconstruction, use save_metadata=True:
Metadata JSON Structure¶
{
"version": "1.0",
"videos": [
{
"filename": "video.mp4",
"backend_metadata": {}
}
],
"skeletons": [
{
"name": "Skeleton-0",
"nodes": ["nose", "head", "neck", "tail"],
"edges": [[0, 1], [1, 2], [2, 3]],
"symmetries": []
}
],
"tracks": ["mouse_1", "mouse_2"],
"suggestions": [],
"provenance": {}
}
| Field | Description |
|---|---|
version |
Metadata schema version (currently "1.0") |
videos |
Video metadata; each entry has filename and a backend-specific backend_metadata object (e.g. shape, dataset, grayscale) |
skeletons |
List of skeleton definitions, each with nodes, edges, and symmetries |
tracks |
Track names in order |
suggestions |
Suggested frame indices |
provenance |
Source-file / creation metadata, mirroring labels.provenance |
Loading CSV Files¶
import sleap_io as sio
# Load CSV (auto-detects format)
labels = sio.load_csv("data.csv")
# If metadata sidecar exists, it's loaded automatically
# data.json provides skeleton edges, symmetries, etc.
CLI Usage¶
# Export to CSV
sio export predictions.slp -o analysis.csv
# Specify format
sio export predictions.slp -o dlc.csv --csv-format dlc --scorer MyModel
# Include all frames
sio export predictions.slp -o all.csv --empty-frames
# Frame range
sio export predictions.slp -o clip.csv --start 100 --end 500
# With metadata sidecar
sio export predictions.slp -o data.csv --save-metadata
API Reference¶
sleap_io.io.main.load_csv(filename, format='auto', video=None, skeleton=None, **kwargs)
¶
Load pose data from a CSV file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
str
|
Path to CSV file. |
required |
format
|
str
|
CSV format. One of "auto", "sleap", "dlc", "points", "instances", "frames". Default "auto" detects format from file content. |
'auto'
|
video
|
Video | str | None
|
Video to associate with data. Can be Video object or path string. |
None
|
skeleton
|
Skeleton | None
|
Skeleton to use. If None, inferred from columns or metadata. |
None
|
**kwargs
|
Additional loader keyword arguments forwarded by |
required |
Returns:
| Type | Description |
|---|---|
Labels
|
Labels object. |
Notes
If a metadata JSON file exists alongside the CSV (same base name with .json extension), it will be automatically loaded to restore full Labels context including skeleton edges, symmetries, and provenance.
See Also
save_csv: Save Labels to CSV file.
Source code in sleap_io/io/main.py
def load_csv(
filename: str,
format: str = "auto",
video: "Video | str | None" = None,
skeleton: "Skeleton | None" = None,
**kwargs,
) -> "Labels":
"""Load pose data from a CSV file.
Args:
filename: Path to CSV file.
format: CSV format. One of "auto", "sleap", "dlc", "points", "instances",
"frames". Default "auto" detects format from file content.
video: Video to associate with data. Can be Video object or path string.
skeleton: Skeleton to use. If None, inferred from columns or metadata.
**kwargs: Additional loader keyword arguments forwarded by `load_file`
(e.g. ``open_videos``, ``lazy``). They are accepted but ignored; this
format does not use them.
Returns:
Labels object.
Notes:
If a metadata JSON file exists alongside the CSV (same base name with
.json extension), it will be automatically loaded to restore full
Labels context including skeleton edges, symmetries, and provenance.
See Also:
save_csv: Save Labels to CSV file.
"""
from sleap_io.io import csv
return csv.read_labels(filename, format=format, video=video, skeleton=skeleton)
sleap_io.io.main.save_csv(labels, filename, format='sleap', video=None, include_score=True, include_empty=False, start_frame=None, end_frame=None, scorer='sleap-io', save_metadata=False, chunk_size=None, video_id='path')
¶
Save pose data to a CSV file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
labels
|
Labels
|
Labels to save. |
required |
filename
|
str
|
Output path. |
required |
format
|
str
|
CSV format. One of "sleap" (default), "dlc", "points", "instances", "frames". |
'sleap'
|
video
|
Video | int | None
|
Video to filter to. Can be Video object or integer index. If None, includes all videos. |
None
|
include_score
|
bool
|
Include confidence scores in output. Default True. |
True
|
include_empty
|
bool
|
Include frames with no instances (filled with NaN values). Default False. Only applies to "frames" and "instances" formats. |
False
|
start_frame
|
int | None
|
Start frame index (inclusive) for output. If None, starts from 0 when include_empty=True, or from first labeled frame otherwise. |
None
|
end_frame
|
int | None
|
End frame index (exclusive) for output. If None, ends at the full video length when known, otherwise at last labeled frame + 1. |
None
|
scorer
|
str
|
Scorer name for DLC format. Default "sleap-io". |
'sleap-io'
|
save_metadata
|
bool
|
Save JSON metadata file alongside CSV that enables full round-trip reconstruction. Default False. |
False
|
chunk_size
|
int | None
|
Number of rows per chunk for memory-efficient writing. If None (default), writes entire DataFrame at once. Useful for large datasets. Not supported for DLC format. |
None
|
video_id
|
str
|
How to represent videos in the CSV. Options: "path" (default), "index", or "name". |
'path'
|
See Also
load_csv: Load Labels from CSV file.
Source code in sleap_io/io/main.py
def save_csv(
labels: "Labels",
filename: str,
format: str = "sleap",
video: "Video | int | None" = None,
include_score: bool = True,
include_empty: bool = False,
start_frame: int | None = None,
end_frame: int | None = None,
scorer: str = "sleap-io",
save_metadata: bool = False,
chunk_size: int | None = None,
video_id: str = "path",
) -> None:
"""Save pose data to a CSV file.
Args:
labels: Labels to save.
filename: Output path.
format: CSV format. One of "sleap" (default), "dlc", "points",
"instances", "frames".
video: Video to filter to. Can be Video object or integer index.
If None, includes all videos.
include_score: Include confidence scores in output. Default True.
include_empty: Include frames with no instances (filled with NaN values).
Default False. Only applies to "frames" and "instances" formats.
start_frame: Start frame index (inclusive) for output. If None, starts
from 0 when include_empty=True, or from first labeled frame otherwise.
end_frame: End frame index (exclusive) for output. If None, ends at the
full video length when known, otherwise at last labeled frame + 1.
scorer: Scorer name for DLC format. Default "sleap-io".
save_metadata: Save JSON metadata file alongside CSV that enables
full round-trip reconstruction. Default False.
chunk_size: Number of rows per chunk for memory-efficient writing. If None
(default), writes entire DataFrame at once. Useful for large datasets.
Not supported for DLC format.
video_id: How to represent videos in the CSV. Options: "path" (default),
"index", or "name".
See Also:
load_csv: Load Labels from CSV file.
"""
from sleap_io.io import csv
csv.write_labels(
labels,
filename,
format=format,
video=video,
include_score=include_score,
include_empty=include_empty,
start_frame=start_frame,
end_frame=end_frame,
scorer=scorer,
save_metadata=save_metadata,
chunk_size=chunk_size,
video_id=video_id,
)