// Docs

Episode rows

The episode-level row model used by Refiner

Refiner exposes robotics data as rows. A row may be a plain mapping, a RoboticsRow semantic view, or a LeRobotRow.

Plain rows

Generic readers emit regular rows:

row = mdr.read_parquet("/data/episodes.parquet").take(1)[0]
print(row["episode_id"])

Plain rows are enough for tabular transforms. Robotics operations usually need a semantic episode view.

RoboticsRow

RoboticsRow is a protocol: it describes what an episode row can do without requiring one physical storage format.

Common properties:

PropertyMeaning
episode_idStable episode identifier.
num_framesNumber of frame rows.
taskOptional task text.
fpsOptional frame rate.
robot_typeOptional robot family/type.
timestampsFrame-aligned timestamps.
actionsFrame-aligned actions.
statesFrame-aligned state observations.
videosMapping of video key to video source.
statsFeature statistics when available.

LeRobotRow

read_lerobot(...) emits LeRobotRow values:

row = mdr.read_lerobot("hf://datasets/lerobot/aloha_sim_transfer_cube_human").take(1)[0]

print(row.episode_index)
print(row.tasks)
print(row.metadata.info.fps)

LeRobotRow is also a RoboticsRow, so episode operations such as Motion Trimming can use it directly.

Updating rows

Rows are immutable-style values: update methods return new rows.

def add_quality_flag(row):
    return row.update({"quality": "ok"})

Frame-aware rows also provide helpers:

def zero_actions(row):
    return row.with_actions([[0.0] for _ in range(row.num_frames)])

Selecting frames

def first_second(row):
    count = int((row.fps or 30) * 1.0)
    return row.select_frames(range(min(count, row.num_frames)))

Selecting frames updates the frame table. For LeRobotRow, frame indices are renumbered when present.