phys-ai-dataprofiler
Profile physical-AI and lab datasets on the Hugging Face Hub without downloading them, and check them against what your model needs.
It reads only the bytes that hold metadata (headers, indexes, footers) through HTTP range requests,
usually well under 1% of each file, and reports streams, message counts, rates, jitter, gaps, episodes,
NaN counts and problems such as truncated or unindexed recordings. A requirements file turns that into a
pass / fail / unknown verdict, and compare ranks candidate datasets side by side.
| Format | What is read |
|---|---|
| MCAP (incl. ROS 2 MCAP storage) | header, footer, summary, message indexes |
| ROS 1 bag (V2.0) | bag index and per-chunk index data; compressed payloads are skipped |
ROS 2 bag (SQLite .db3) |
schema, topics, metadata and the leaf pages of the messages table |
MATLAB .mat (v5, v7, v7.3) |
header and each variable's header; float data within a NaN budget |
| Parquet / LeRobot | footer, column statistics, timestamp and episode columns |
Measured on public Hub data: a 5.8 GB ROS 1 bag profiled from 533 KB, a 2.6 GB MAT v7.3 file from 808 KB.
Install
pip install phys-ai-dataprofiler
pip install "phys-ai-dataprofiler[mat73]" # MAT v7.3 (HDF5) files; needs Python 3.13 or older for h5py
Python 3.10 or newer. Public datasets need no account; for gated or private ones set HF_TOKEN (or run
hf auth login). The token is only sent to huggingface.co, never to the CDN hosts that serve file bytes.
Quick start
phys-ai-dataprofiler org/dataset --dry-run # what files are there, and how big
phys-ai-dataprofiler org/dataset --max-files 10 # profile a sample -> reports/<repo>__<time>.md/.json
phys-ai-dataprofiler requirements list # built-in presets
phys-ai-dataprofiler requirements show teleop-imitation > req.yaml # start your own, edit the numbers
phys-ai-dataprofiler check org/dataset -r req.yaml # verdict; exit 0 pass, 1 fail, 4 unknown
phys-ai-dataprofiler compare org/a org/b -r req.yaml # rank candidates against the same requirements
Nothing is downloaded unless you ask for it (--mode download, or --full-read-max-mb for small files).
Nothing is ever written to the Hub.
Requirements and verdicts
Say what your model needs in a YAML file, and the profiler answers pass, fail or unknown for each requirement, with evidence and, for unknowns, how to settle them. Checks are deterministic rules, with no model call. Full reference: docs/REQUIREMENTS.md.
phys-ai-dataprofiler check org/dataset -r preset:teleop-imitation # exit 0 pass, 1 fail, 4 unknown
phys-ai-dataprofiler compare org/a org/b -r my_requirements.yaml # rank candidate datasets
phys-ai-dataprofiler check --report reports/run.json -r my_requirements.yaml # re-check offline
streams:
- name: camera
match: {modality: video}
min_rate_hz: 25
- name: joint state
match: {topic: ["/joint_states", "observation.state"]}
min_rate_hz: 50
max_gap_ms: 100
dataset:
min_total_hours: 20
min_episodes: 500
A number that was sampled and lies within 10% of the threshold, a NaN count the format cannot provide, or a file that failed to profile makes the result unknown, never a guessed pass.
ROS bag support
.bag (ROS 1, format V2.0) and .db3 (ROS 2, SQLite storage) files are profiled from byte ranges, like
MCAP. ROS 2 bags in MCAP storage are plain .mcap files and use the MCAP path. Code:
phys_ai_dataprofiler/remote/rosbag.py.
- ROS 1: the bag header points to the index at the end of the file (connections plus per-chunk counts
and time ranges). Per-message timestamps come from the IndexData records after each chunk, 12 bytes per
message, one request per chunk. Chunk payloads are never read, so compression (bz2, lz4) makes no
difference. Over
--msgidx-budget-mb, chunks are sampled; counts stay exact. Bags with no index (recorder killed) are walked record by record and reportedunindexed_bag; truncated bags are reportedtruncated. Old V1.2 bags are identified but not parsed. - ROS 2 SQLite: a small read-only SQLite reader descends the
messagestable B-tree. Each row'stopic_idandtimestampsit at the start of its leaf cell, so message payloads and their overflow pages are never read. Counts and duration come from themetadatatable, or from themetadata.yamlnext to the.db3when the database has none. Over budget, runs of leaf pages are sampled and the start and end times come fromtimestamp_idx. Without any metadata, sampled per-topic counts are estimates, reported asestimated_counts.
Timing statistics use the same code as MCAP. Tests compare every case against a full read through
rosbags: python tests/test_rosbag_inspector.py (add --hub for two public bags).
| Dataset | Result | Read |
|---|---|---|
Adapting/nuscenes-rosbags 103.bag (ROS 1) |
14 topics, 1,585 chunks, 19.5 s | 533 KB of 5.8 GB |
dvar1ch/ros2-bag-datasets (ROS 2 SQLite, 2 files) |
/image at 30 Hz, metadata v9, jazzy |
1.5 MB of 668 MB |
liuxh010/IndoorLocalization_... (ROS 1), UniflexAI/rosbag_lekiwi_exp_result (ROS 2) |
per-topic stats identical to a full read | 146 KB of 891 KB; 2.1 MB of 18.6 MB |
Parquet support
Parquet keeps its schema, row groups and per-column statistics in a footer at the end of the file, so the profiler reads the footer first (typically under 100 KB, whatever the file size). It reports rows, row groups, every column's type, null count, min and max, compressed size and codec.
If a time column is present (timestamp, log_time, time, ...), it also reads that column, and
episode_index when present (the LeRobot convention). Their byte ranges are known from the footer, so the cost
is computed up front and capped by --column-budget-mb (default 16); beyond that, row groups are sampled and
the report says so. From these it derives frame rate, jitter, gaps, episode count and lengths, and flags
dropped frames, short episodes and non-monotonic time. Image, text and state columns are never read.
Units are inferred: float seconds (LeRobot), integer epoch s/ms/us/ns, and Arrow timestamp types.
NaN/inf counts are not in Parquet statistics, so float columns are read too, capped by --nan-budget-mb
(default 32); beyond that, row groups are sampled and the report says so. --nan-budget-mb 0 turns this off.
| Dataset | Result | Read |
|---|---|---|
lerobot/pusht (LeRobot v3) |
206 episodes at 10 Hz, matching meta/info.json |
205 KB of 659 KB |
nvidia/Nemotron-Personas-Vietnam (text) |
90,910 rows, 21 columns per file | 143 KB of 194 MB |
Repos with nothing to profile now say so and exit with code 3, listing the file types they do contain.
MCAP File Inspector
A tool to inspect MCAP (Message Capture) files and extract header, footer, channels, topics, and schema information.
Features
- Header Inspection: Extracts profile and library information
- Footer Inspection: Extracts summary start offset, summary offset start, and CRC
- Channel Discovery: Lists all channels with their topics, encoding, schema IDs, and metadata
- Topic Listing: Provides a sorted list of all unique topics
- Schema Information: Extracts schema details including name, encoding, and data
- Local and Remote Files: Supports both local file paths and remote URLs
- Fallback Support: Works with or without the mcap library installed
Installation
- Create a virtual environment:
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
- Install dependencies:
pip install mcap
Usage
As a module
from phys_ai_dataprofiler.inspectors.mcap import McapFileInspector
# Inspect a local file
inspector = McapFileInspector("samples/sample.mcap")
result = inspector.inspect()
print(f"Topics: {result.topics}")
print(f"Channels: {len(result.channels)}")
print(f"Header Profile: {result.header.profile}")
print(f"Footer Summary Start: {result.footer.summary_start}")
As a command-line tool
# Using the basic inspect_file script
python -m phys_ai_dataprofiler.inspectors.mcap samples/sample.mcap
# Using the test script with formatted output
python tests/test_mcap_inspector.py samples/sample.mcap
Remote URLs
python tests/test_mcap_inspector.py https://example.com/data.mcap
Data Structures
McapInspectionResult
url: File URL or pathis_mcap: Boolean indicating if file is a valid MCAPmagic: Magic bytes from file headersize_bytes: File size in bytespath: Local path (if applicable)error: Error message (if any)header: HeaderInfo objectfooter: FooterInfo objectchannels: List of ChannelInfo objectsschemas: List of SchemaInfo objectstopics: Sorted list of topic stringsmcap_available: Boolean indicating if mcap library is available
HeaderInfo
profile: MCAP profile stringlibrary: Library that created the file
FooterInfo
summary_start: Offset where summary section startssummary_offset_start: Offset where summary offsets startsummary_crc: CRC checksum of summary section
ChannelInfo
id: Channel IDtopic: Topic namemessage_encoding: Message encoding (e.g., "json", "protobuf")schema_id: Associated schema IDmetadata: Dictionary of channel metadata
SchemaInfo
id: Schema IDname: Schema nameencoding: Schema encoding typedata: Raw schema data (bytes)
Example Output
Inspecting: samples/sample.mcap
URL: samples/sample.mcap
Is MCAP: True
Size: 1042 bytes
MCAP Library Available: True
=== Header ===
Profile: test
Library: sample_creator
=== Footer ===
Summary Start: 397
Summary Offset Start: 849
Summary CRC: 544820573
=== Topics (2) ===
- /sensors/gps
- /sensors/imu
=== Channels (2) ===
Channel 1:
Topic: /sensors/gps
Encoding: json
Schema ID: 1
Metadata: {'description': 'GPS sensor data'}
Channel 2:
Topic: /sensors/imu
Encoding: json
Schema ID: 1
Metadata: {'description': 'IMU sensor data'}
=== Schemas (1) ===
Schema 1:
Name: example.Point
Encoding: jsonschema
Data size: 84 bytes
Creating Test Files
Use the included create_sample_mcap.py script to create test MCAP files:
python scripts/create_sample_mcap.py output.mcap
Implementation Details
The inspector uses the official mcap library to parse MCAP files:
make_reader()for high-level summary and header accessStreamReaderfor low-level footer record extraction- Supports both file handles and BytesIO for remote content
For remote files, the inspector first checks the magic bytes using HTTP Range requests, then downloads the full file for parsing if valid.
MATLAB File Inspector
A tool to inspect MATLAB .mat files and extract header information, variable metadata, and data types.
Features
- Header Inspection: Extracts description, version, and endianness information
- Variable Discovery: Lists all variables with their names, types, shapes, and sizes
- Type Detection: Identifies scalars, vectors, matrices, complex numbers, sparse matrices, structs, and cell arrays
- Format Support: Handles MAT-File v4, v5, v7, and v7.3 (HDF5-based) formats
- Local and Remote Files: Supports both local file paths and remote URLs
- Fallback Support: Works with limited functionality if scipy/h5py are not installed
Installation
- Create a virtual environment (if not already created):
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
- Install dependencies:
pip install scipy numpy # For v4/v5/v7 format support
pip install h5py # For v7.3 (HDF5) format support (requires HDF5 library)
Usage
As a module
from phys_ai_dataprofiler.inspectors.mat import MatFileInspector
# Inspect a local file
inspector = MatFileInspector("data.mat")
result = inspector.inspect()
print(f"Format: {result.file_format}")
print(f"Variables: {result.variable_names}")
for var in result.variables:
print(f"{var.name}: {var.dtype} {var.shape}")
As a command-line tool
# Using the basic matfile_inspector script
python -m phys_ai_dataprofiler.inspectors.mat data.mat
# Using the test script with formatted output
python tests/test_mat_inspector.py data.mat
Remote URLs
python tests/test_mat_inspector.py https://example.com/data.mat
Data Structures
MatInspectionResult
url: File URL or pathis_matfile: Boolean indicating if file is a valid MAT-Filefile_format: Format version ("v4", "v5", "v7", or "v7.3")size_bytes: File size in bytespath: Local path (if applicable)error: Error message (if any)header: MatFileHeader objectvariables: List of VariableInfo objectsvariable_names: Sorted list of variable namesscipy_available: Boolean indicating if scipy is availableh5py_available: Boolean indicating if h5py is available
MatFileHeader
description: File description string (includes platform and creation time)version: Version number in hex formatendian: Endianness ("little", "big", or "unknown")created: Creation timestamp (when available)
VariableInfo
name: Variable namedtype: Data type stringshape: Tuple of dimensionssize_bytes: Size in bytesis_sparse: Boolean indicating sparse matrixis_complex: Boolean indicating complex numbersis_struct: Boolean indicating MATLAB structis_cell: Boolean indicating cell arrayfield_names: List of field names (for structs)
Example Output
Inspecting: samples/sample_v5.mat
URL: samples/sample_v5.mat
Is MAT-File: True
File Format: v5
Size: 1544 bytes
SciPy Available: True
h5py Available: False
=== Header ===
Description: MATLAB 5.0 MAT-file Platform: posix, Created on: Sun Oct 11 18:59:29 2026
Version: 0x0100
Endian: little
=== Variables (12) ===
- col_vector
- complex_data
- double_data
- float_data
- int_array
- logical_array
- matrix_2d
- matrix_3d
- row_vector
- scalar_value
- text_string
- uint_array
=== Variable Details ===
scalar_value:
Type: int64
Shape: (1, 1)
Size: 8 bytes
complex_data:
Type: complex128
Shape: (1, 3)
Size: 48 bytes
Flags: complex
matrix_3d:
Type: float64
Shape: (2, 3, 4)
Size: 192 bytes
Supported MAT-File Formats
v4 Format
- Legacy format from MATLAB 4
- Limited data type support
- Requires scipy
v5 Format (Default)
- MATLAB 5 and later (default save format in older versions)
- Supports all standard MATLAB types
- Requires scipy
v7 Format
- Compressed v5 format
- Same structure as v5 but with compression
- Requires scipy
v7.3 Format (HDF5-based)
- MATLAB R2006b and later
- Based on HDF5 file format
- Required for variables larger than 2GB
- Requires h5py and HDF5 library
Creating Test Files
Use the included create_sample_mat.py script to create test MAT files:
python scripts/create_sample_mat.py samples/sample_v5.mat samples/sample_v7.mat
Implementation Details
The inspector uses two libraries depending on the MAT-File format:
- scipy.io.loadmat: For v4, v5, and v7 formats
- h5py: For v7.3 (HDF5-based) format
For remote files, the inspector first checks the file header using HTTP Range requests to determine the format, then downloads the full file for parsing if valid.
Comparison with MCAP Inspector
Both inspectors follow a similar architecture:
| Feature | MCAP Inspector | MATLAB Inspector |
|---|---|---|
| File Format | MCAP (robotics) | MAT-File (MATLAB) |
| Header Info | ✓ | ✓ |
| Footer Info | ✓ | N/A |
| Variable/Channel List | ✓ | ✓ |
| Type Information | ✓ | ✓ |
| Remote Support | ✓ | ✓ |
| Compression Support | ✓ | ✓ |
Hugging Face Connector
phys_ai_dataprofiler/connectors/huggingface.py (CLI: phys-ai-dataprofiler, or python -m phys_ai_dataprofiler) connects to the Hugging Face Hub with an access token,
finds the MCAP / MAT files in a repo, inspects them and writes a JSON and a
Markdown report. By default nothing is downloaded: files are read through
HTTP Range requests and only the bytes holding metadata are fetched.
| What | MCAP | MAT (v5 / v7) |
|---|---|---|
| Bytes read | header, footer, summary section, MessageIndex records | 128-byte header + each variable's tag and array header, then float variables up to --nan-budget-mb |
| Typical share of file | well under 1% (163 MB file: ~0.5 MB) | headers: one 64 KB block per few variables; plus the float data that fits the NaN budget |
| Available | profile, library, topics, schemas, channels, per-channel count, rate, jitter, gaps, duration | format, header, every variable with name, class/dtype, dims, stored size, complex/sparse/struct/cell flags, struct field names, NaN counts |
| No summary / truncated | walked record by record: chunk headers and message indexes, chunk bodies only for channel definitions or when a chunk has no index; capped by --msgidx-budget-mb |
- |
| Needs a full read | - | v4 files, v7.3 without h5py |
Remote mode gives the same streams, rates, gaps, findings and NaN counts as
the full-read RichInspector (checked by tests/test_remote_rich.py). It also
profiles truncated MCAPs, which the full read cannot open. Anything partial
(budget reached, chunks sampled) is reported as a finding. Use
--full-read-max-mb N to fetch files at or below that size whole instead
(deleted afterwards unless --keep-downloads), or --mode download to go
back to downloading everything.
Token
Resolved in this order: --token, then HF_TOKEN / HUGGINGFACE_HUB_TOKEN,
then the login cached by hf auth login. Public repos work with no token.
The token is only ever sent to huggingface.co, never to the CDN hosts that
serve the file bytes.
Usage
# Inspect every .mcap/.mat file in a dataset repo without downloading
phys-ai-dataprofiler org/dataset --token hf_xxx
# See what would be inspected first
phys-ai-dataprofiler org/dataset --dry-run
# Limit the selection: by glob, by count, by size
phys-ai-dataprofiler org/dataset --include "episodes/*.mcap" --max-files 5
# Exact files
phys-ai-dataprofiler org/dataset --file logs/run1.mcap --file calib/cam.mat
# Larger NaN budget (default 32 MB of float data per file), or none
phys-ai-dataprofiler org/dataset --nan-budget-mb 256
phys-ai-dataprofiler org/dataset --nan-budget-mb 0
# Fetch small files whole instead of range-reading them
phys-ai-dataprofiler org/dataset --full-read-max-mb 50
# Classic download mode, and re-running on an earlier download
phys-ai-dataprofiler org/dataset --mode download --max-total-mb 2000
phys-ai-dataprofiler org/dataset --mode download --offline
# Model repos, specific revisions
phys-ai-dataprofiler org/model --repo-type model --revision v1.2
Tuning for remote mode: --workers (parallel range requests, default 16),
--msgidx-budget-mb (cap on MCAP message-index bytes per file; beyond it
chunks are sampled and the report says so; also caps the record walk of MCAPs
without a summary), --nan-budget-mb (float bytes read per file for NaN
counts), --block-kb (cached read block).
Reports go to ./reports/<repo>__<timestamp>.{json,md} (override with
--report-dir, --report-name) and include, per file, how many bytes were
read and in how many requests. Exit code is 1 when any file failed, so it is
CI-friendly.
Modules
phys_ai_dataprofiler/remote/range_file.py:HfRangeFile(seekable file over Hub byte ranges, block cache, parallelfetch_many) andLocalRangeFile(same API on disk).phys_ai_dataprofiler/remote/inspector.py:inspect_mcap_remote/inspect_mat_remote, returning the sameMcapInspectionResult,MatInspectionResultandRichProfiletypes as the full-read inspectors. Run it on a local file withpython -m phys_ai_dataprofiler.remote.inspector file.mcapto see what it extracts.phys_ai_dataprofiler/inspectors/rich.pyexposestiming_statsandprofile_mcap_channels, shared by both paths so remote and local statistics are identical.
from phys_ai_dataprofiler.connectors.huggingface import HuggingFaceConnector
from phys_ai_dataprofiler.remote.inspector import inspect_mcap_remote, inspect_mat_remote, sniff_format
hf = HuggingFaceConnector("org/dataset", token="hf_xxx")
hf.connect()
selected, skipped = hf.select_files(hf.list_files(), max_files=10)
for f in selected:
rf = hf.open_range(f, block_size=64 * 1024)
fmt = sniff_format(rf, f.path)
result, profile = inspect_mcap_remote(rf) if fmt == "mcap" else inspect_mat_remote(rf)
print(f.path, rf.bytes_fetched, "of", rf.size, "bytes read")
Development
git clone https://github.com/akrishni/phys-ai-dataprofiler
cd phys-ai-dataprofiler
pip install -e ".[mat73,rosbag,dev]"
python tests/run_all.py # every test script, with a summary
python tests/run_all.py --hub # also profile small public Hub datasets
python tests/test_rosbag_inspector.py # or run one script on its own
CI (.github/workflows/tests.yml) runs tests/run_all.py on every push and pull request, on Python 3.10
to 3.13 on Linux and 3.13 on macOS, and builds the package and smoke-tests the installed CLI. The Hub
checks need network access and are only run on demand: Actions tab, "tests", "Run workflow", tick
"hub".
phys_ai_dataprofiler/
connectors/huggingface.py Hub connector and the default CLI command
cli.py check, compare, requirements subcommands
requirements.py evaluate.py compare.py requirements files, verdicts, comparisons
presets/ built-in requirements files
remote/ byte-range readers: MCAP, MAT, Parquet, ROS bags
inspectors/ full-read inspectors for local files
tests/ samples/ scripts/ docs/
License and citation
MIT License, copyright (c) 2026 Anand Krishniyer. See LICENSE.
If you use this tool in research, please cite it; GitHub's "Cite this repository" button (from CITATION.cff) gives a ready-made reference.
Metadata
Release files for phys-ai-dataprofiler 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| phys_ai_dataprofiler-0.3.1.tar.gz | 116.6 kB | With provenance | Details for phys_ai_dataprofiler-0.3.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| phys_ai_dataprofiler-0.3.1-py3-none-any.whl | Python 3 | none | any | Details for phys_ai_dataprofiler-0.3.1-py3-none-any.whl |
Total release size: 208.8 kB
Release files / phys_ai_dataprofiler-0.3.1.tar.gz
| Download URL | phys_ai_dataprofiler-0.3.1.tar.gz |
|---|---|
| Size | 116.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5a03dcfd719ba2661e845a693f4f05da31d3f93ebe25e806cc695b03f0bff283
|
|
BLAKE2b-256 checksum How to use checksums |
6da51204205ffa2de23c36312665a1d201faa96a779cdca90e9fc5b85a64bc25
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.
Transparency logRelease files / phys_ai_dataprofiler-0.3.1-py3-none-any.whl
| Download URL | phys_ai_dataprofiler-0.3.1-py3-none-any.whl |
|---|---|
| Size | 92.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2e1e501ce5a548472616f0002c0f762a89bf8802ab5c04f0766a4ee3247a68f4
|
|
BLAKE2b-256 checksum How to use checksums |
83935ece587a14b9f774b51383d6ccf116846bca3afc2bfb3a3788fc3956b66d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.
Transparency logSecurity for phys-ai-dataprofiler 0.3.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for phys_ai_dataprofiler-0.3.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.