phys-ai-dataprofiler 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. phys_ai_dataprofiler-0.3.1/LICENSE +21 -0
  2. phys_ai_dataprofiler-0.3.1/PKG-INFO +659 -0
  3. phys_ai_dataprofiler-0.3.1/README.md +618 -0
  4. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/__init__.py +13 -0
  5. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/__main__.py +5 -0
  6. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/cli.py +214 -0
  7. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/compare.py +123 -0
  8. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/connectors/__init__.py +1 -0
  9. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/connectors/huggingface.py +1113 -0
  10. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/evaluate.py +570 -0
  11. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/inspectors/__init__.py +1 -0
  12. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/inspectors/mat.py +485 -0
  13. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/inspectors/mcap.py +266 -0
  14. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/inspectors/rich.py +428 -0
  15. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/presets/lidar-perception.yaml +15 -0
  16. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/presets/teleop-imitation.yaml +22 -0
  17. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/presets/timeseries-clean.yaml +14 -0
  18. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/presets/visual-odometry.yaml +21 -0
  19. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/remote/__init__.py +1 -0
  20. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/remote/inspector.py +795 -0
  21. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/remote/parquet.py +578 -0
  22. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/remote/range_file.py +326 -0
  23. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/remote/rosbag.py +998 -0
  24. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler/requirements.py +421 -0
  25. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler.egg-info/PKG-INFO +659 -0
  26. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler.egg-info/SOURCES.txt +38 -0
  27. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler.egg-info/dependency_links.txt +1 -0
  28. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler.egg-info/entry_points.txt +2 -0
  29. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler.egg-info/requires.txt +16 -0
  30. phys_ai_dataprofiler-0.3.1/phys_ai_dataprofiler.egg-info/top_level.txt +1 -0
  31. phys_ai_dataprofiler-0.3.1/pyproject.toml +53 -0
  32. phys_ai_dataprofiler-0.3.1/setup.cfg +4 -0
  33. phys_ai_dataprofiler-0.3.1/tests/test_connector.py +188 -0
  34. phys_ai_dataprofiler-0.3.1/tests/test_mat_inspector.py +109 -0
  35. phys_ai_dataprofiler-0.3.1/tests/test_mcap_inspector.py +99 -0
  36. phys_ai_dataprofiler-0.3.1/tests/test_parquet_inspector.py +290 -0
  37. phys_ai_dataprofiler-0.3.1/tests/test_range_file.py +165 -0
  38. phys_ai_dataprofiler-0.3.1/tests/test_remote_rich.py +240 -0
  39. phys_ai_dataprofiler-0.3.1/tests/test_requirements.py +482 -0
  40. phys_ai_dataprofiler-0.3.1/tests/test_rosbag_inspector.py +307 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anand Krishniyer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,659 @@
1
+ Metadata-Version: 2.4
2
+ Name: phys-ai-dataprofiler
3
+ Version: 0.3.1
4
+ Summary: Profile robotics and lab datasets on the Hugging Face Hub without downloading them (MCAP, ROS bags, MATLAB, Parquet), and check them against your requirements
5
+ Author-email: Anand Krishniyer <akrishniyer@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/akrishni/phys-ai-dataprofiler
8
+ Project-URL: Documentation, https://github.com/akrishni/phys-ai-dataprofiler#readme
9
+ Project-URL: Issues, https://github.com/akrishni/phys-ai-dataprofiler/issues
10
+ Keywords: robotics,dataset,mcap,rosbag,ros2,matlab,parquet,lerobot,huggingface,data-quality,profiling
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Utilities
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: huggingface_hub>=1.0
28
+ Requires-Dist: mcap>=1.0
29
+ Requires-Dist: numpy
30
+ Requires-Dist: scipy
31
+ Requires-Dist: pyarrow>=14
32
+ Requires-Dist: pyyaml>=6
33
+ Provides-Extra: mat73
34
+ Requires-Dist: h5py; extra == "mat73"
35
+ Provides-Extra: rosbag
36
+ Requires-Dist: rosbags; extra == "rosbag"
37
+ Requires-Dist: lz4; extra == "rosbag"
38
+ Provides-Extra: dev
39
+ Requires-Dist: pytest; extra == "dev"
40
+ Dynamic: license-file
41
+
42
+ # phys-ai-dataprofiler
43
+
44
+ [![tests](https://github.com/akrishni/phys-ai-dataprofiler/actions/workflows/tests.yml/badge.svg)](https://github.com/akrishni/phys-ai-dataprofiler/actions/workflows/tests.yml)
45
+
46
+ Profile physical-AI and lab datasets on the Hugging Face Hub **without downloading them**, and check
47
+ them against what your model needs.
48
+
49
+ It reads only the bytes that hold metadata (headers, indexes, footers) through HTTP range requests,
50
+ usually well under 1% of each file, and reports streams, message counts, rates, jitter, gaps, episodes,
51
+ NaN counts and problems such as truncated or unindexed recordings. A requirements file turns that into a
52
+ pass / fail / unknown verdict, and `compare` ranks candidate datasets side by side.
53
+
54
+ | Format | What is read |
55
+ |---|---|
56
+ | MCAP (incl. ROS 2 MCAP storage) | header, footer, summary, message indexes |
57
+ | ROS 1 bag (V2.0) | bag index and per-chunk index data; compressed payloads are skipped |
58
+ | ROS 2 bag (SQLite `.db3`) | schema, topics, metadata and the leaf pages of the messages table |
59
+ | MATLAB `.mat` (v5, v7, v7.3) | header and each variable's header; float data within a NaN budget |
60
+ | Parquet / LeRobot | footer, column statistics, timestamp and episode columns |
61
+
62
+ 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.
63
+
64
+ ## Install
65
+
66
+ ```bash
67
+ pip install phys-ai-dataprofiler
68
+ pip install "phys-ai-dataprofiler[mat73]" # MAT v7.3 (HDF5) files; needs Python 3.13 or older for h5py
69
+ ```
70
+
71
+ Python 3.10 or newer. Public datasets need no account; for gated or private ones set `HF_TOKEN` (or run
72
+ `hf auth login`). The token is only sent to huggingface.co, never to the CDN hosts that serve file bytes.
73
+
74
+ ## Quick start
75
+
76
+ ```bash
77
+ phys-ai-dataprofiler org/dataset --dry-run # what files are there, and how big
78
+ phys-ai-dataprofiler org/dataset --max-files 10 # profile a sample -> reports/<repo>__<time>.md/.json
79
+
80
+ phys-ai-dataprofiler requirements list # built-in presets
81
+ phys-ai-dataprofiler requirements show teleop-imitation > req.yaml # start your own, edit the numbers
82
+ phys-ai-dataprofiler check org/dataset -r req.yaml # verdict; exit 0 pass, 1 fail, 4 unknown
83
+ phys-ai-dataprofiler compare org/a org/b -r req.yaml # rank candidates against the same requirements
84
+ ```
85
+
86
+ Nothing is downloaded unless you ask for it (`--mode download`, or `--full-read-max-mb` for small files).
87
+ Nothing is ever written to the Hub.
88
+
89
+ ## Requirements and verdicts
90
+
91
+ Say what your model needs in a YAML file, and the profiler answers pass, fail or unknown for each
92
+ requirement, with evidence and, for unknowns, how to settle them. Checks are deterministic rules, with no
93
+ model call. Full reference: [docs/REQUIREMENTS.md](docs/REQUIREMENTS.md).
94
+
95
+ ```bash
96
+ phys-ai-dataprofiler check org/dataset -r preset:teleop-imitation # exit 0 pass, 1 fail, 4 unknown
97
+ phys-ai-dataprofiler compare org/a org/b -r my_requirements.yaml # rank candidate datasets
98
+ phys-ai-dataprofiler check --report reports/run.json -r my_requirements.yaml # re-check offline
99
+ ```
100
+
101
+ ```yaml
102
+ streams:
103
+ - name: camera
104
+ match: {modality: video}
105
+ min_rate_hz: 25
106
+ - name: joint state
107
+ match: {topic: ["/joint_states", "observation.state"]}
108
+ min_rate_hz: 50
109
+ max_gap_ms: 100
110
+ dataset:
111
+ min_total_hours: 20
112
+ min_episodes: 500
113
+ ```
114
+
115
+ A number that was sampled and lies within 10% of the threshold, a NaN count the format cannot provide,
116
+ or a file that failed to profile makes the result **unknown**, never a guessed pass.
117
+
118
+ ## ROS bag support
119
+
120
+ `.bag` (ROS 1, format V2.0) and `.db3` (ROS 2, SQLite storage) files are profiled from byte ranges, like
121
+ MCAP. ROS 2 bags in MCAP storage are plain `.mcap` files and use the MCAP path. Code:
122
+ `phys_ai_dataprofiler/remote/rosbag.py`.
123
+
124
+ - **ROS 1:** the bag header points to the index at the end of the file (connections plus per-chunk counts
125
+ and time ranges). Per-message timestamps come from the IndexData records after each chunk, 12 bytes per
126
+ message, one request per chunk. Chunk payloads are never read, so compression (bz2, lz4) makes no
127
+ difference. Over `--msgidx-budget-mb`, chunks are sampled; counts stay exact. Bags with no index
128
+ (recorder killed) are walked record by record and reported `unindexed_bag`; truncated bags are reported
129
+ `truncated`. Old V1.2 bags are identified but not parsed.
130
+ - **ROS 2 SQLite:** a small read-only SQLite reader descends the `messages` table B-tree. Each row's
131
+ `topic_id` and `timestamp` sit at the start of its leaf cell, so message payloads and their overflow
132
+ pages are never read. Counts and duration come from the `metadata` table, or from the `metadata.yaml`
133
+ next to the `.db3` when the database has none. Over budget, runs of leaf pages are sampled and the
134
+ start and end times come from `timestamp_idx`. Without any metadata, sampled per-topic counts are
135
+ estimates, reported as `estimated_counts`.
136
+
137
+ Timing statistics use the same code as MCAP. Tests compare every case against a full read through
138
+ `rosbags`: `python tests/test_rosbag_inspector.py` (add `--hub` for two public bags).
139
+
140
+ | Dataset | Result | Read |
141
+ |---|---|---|
142
+ | `Adapting/nuscenes-rosbags` `103.bag` (ROS 1) | 14 topics, 1,585 chunks, 19.5 s | 533 KB of 5.8 GB |
143
+ | `dvar1ch/ros2-bag-datasets` (ROS 2 SQLite, 2 files) | `/image` at 30 Hz, metadata v9, jazzy | 1.5 MB of 668 MB |
144
+ | `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 |
145
+
146
+ ## Parquet support
147
+
148
+ Parquet keeps its schema, row groups and per-column statistics in a footer at the end of the file, so the
149
+ profiler reads the footer first (typically under 100 KB, whatever the file size). It reports rows, row groups,
150
+ every column's type, null count, min and max, compressed size and codec.
151
+
152
+ If a time column is present (`timestamp`, `log_time`, `time`, ...), it also reads that column, and
153
+ `episode_index` when present (the LeRobot convention). Their byte ranges are known from the footer, so the cost
154
+ is computed up front and capped by `--column-budget-mb` (default 16); beyond that, row groups are sampled and
155
+ the report says so. From these it derives frame rate, jitter, gaps, episode count and lengths, and flags
156
+ dropped frames, short episodes and non-monotonic time. Image, text and state columns are never read.
157
+
158
+ Units are inferred: float seconds (LeRobot), integer epoch s/ms/us/ns, and Arrow timestamp types.
159
+ NaN/inf counts are not in Parquet statistics, so float columns are read too, capped by `--nan-budget-mb`
160
+ (default 32); beyond that, row groups are sampled and the report says so. `--nan-budget-mb 0` turns this off.
161
+
162
+ | Dataset | Result | Read |
163
+ |---|---|---|
164
+ | `lerobot/pusht` (LeRobot v3) | 206 episodes at 10 Hz, matching `meta/info.json` | 205 KB of 659 KB |
165
+ | `nvidia/Nemotron-Personas-Vietnam` (text) | 90,910 rows, 21 columns per file | 143 KB of 194 MB |
166
+
167
+ Repos with nothing to profile now say so and exit with code 3, listing the file types they do contain.
168
+
169
+ ---
170
+
171
+ # MCAP File Inspector
172
+
173
+ A tool to inspect MCAP (Message Capture) files and extract header, footer, channels, topics, and schema information.
174
+
175
+ ## Features
176
+
177
+ - **Header Inspection**: Extracts profile and library information
178
+ - **Footer Inspection**: Extracts summary start offset, summary offset start, and CRC
179
+ - **Channel Discovery**: Lists all channels with their topics, encoding, schema IDs, and metadata
180
+ - **Topic Listing**: Provides a sorted list of all unique topics
181
+ - **Schema Information**: Extracts schema details including name, encoding, and data
182
+ - **Local and Remote Files**: Supports both local file paths and remote URLs
183
+ - **Fallback Support**: Works with or without the mcap library installed
184
+
185
+ ## Installation
186
+
187
+ 1. Create a virtual environment:
188
+ ```bash
189
+ python3 -m venv venv
190
+ source venv/bin/activate # On Windows: venv\Scripts\activate
191
+ ```
192
+
193
+ 2. Install dependencies:
194
+ ```bash
195
+ pip install mcap
196
+ ```
197
+
198
+ ## Usage
199
+
200
+ ### As a module
201
+
202
+ ```python
203
+ from phys_ai_dataprofiler.inspectors.mcap import McapFileInspector
204
+
205
+ # Inspect a local file
206
+ inspector = McapFileInspector("samples/sample.mcap")
207
+ result = inspector.inspect()
208
+
209
+ print(f"Topics: {result.topics}")
210
+ print(f"Channels: {len(result.channels)}")
211
+ print(f"Header Profile: {result.header.profile}")
212
+ print(f"Footer Summary Start: {result.footer.summary_start}")
213
+ ```
214
+
215
+ ### As a command-line tool
216
+
217
+ ```bash
218
+ # Using the basic inspect_file script
219
+ python -m phys_ai_dataprofiler.inspectors.mcap samples/sample.mcap
220
+
221
+ # Using the test script with formatted output
222
+ python tests/test_mcap_inspector.py samples/sample.mcap
223
+ ```
224
+
225
+ ### Remote URLs
226
+
227
+ ```bash
228
+ python tests/test_mcap_inspector.py https://example.com/data.mcap
229
+ ```
230
+
231
+ ## Data Structures
232
+
233
+ ### McapInspectionResult
234
+ - `url`: File URL or path
235
+ - `is_mcap`: Boolean indicating if file is a valid MCAP
236
+ - `magic`: Magic bytes from file header
237
+ - `size_bytes`: File size in bytes
238
+ - `path`: Local path (if applicable)
239
+ - `error`: Error message (if any)
240
+ - `header`: HeaderInfo object
241
+ - `footer`: FooterInfo object
242
+ - `channels`: List of ChannelInfo objects
243
+ - `schemas`: List of SchemaInfo objects
244
+ - `topics`: Sorted list of topic strings
245
+ - `mcap_available`: Boolean indicating if mcap library is available
246
+
247
+ ### HeaderInfo
248
+ - `profile`: MCAP profile string
249
+ - `library`: Library that created the file
250
+
251
+ ### FooterInfo
252
+ - `summary_start`: Offset where summary section starts
253
+ - `summary_offset_start`: Offset where summary offsets start
254
+ - `summary_crc`: CRC checksum of summary section
255
+
256
+ ### ChannelInfo
257
+ - `id`: Channel ID
258
+ - `topic`: Topic name
259
+ - `message_encoding`: Message encoding (e.g., "json", "protobuf")
260
+ - `schema_id`: Associated schema ID
261
+ - `metadata`: Dictionary of channel metadata
262
+
263
+ ### SchemaInfo
264
+ - `id`: Schema ID
265
+ - `name`: Schema name
266
+ - `encoding`: Schema encoding type
267
+ - `data`: Raw schema data (bytes)
268
+
269
+ ## Example Output
270
+
271
+ ```
272
+ Inspecting: samples/sample.mcap
273
+
274
+ URL: samples/sample.mcap
275
+ Is MCAP: True
276
+ Size: 1042 bytes
277
+ MCAP Library Available: True
278
+
279
+ === Header ===
280
+ Profile: test
281
+ Library: sample_creator
282
+
283
+ === Footer ===
284
+ Summary Start: 397
285
+ Summary Offset Start: 849
286
+ Summary CRC: 544820573
287
+
288
+ === Topics (2) ===
289
+ - /sensors/gps
290
+ - /sensors/imu
291
+
292
+ === Channels (2) ===
293
+ Channel 1:
294
+ Topic: /sensors/gps
295
+ Encoding: json
296
+ Schema ID: 1
297
+ Metadata: {'description': 'GPS sensor data'}
298
+ Channel 2:
299
+ Topic: /sensors/imu
300
+ Encoding: json
301
+ Schema ID: 1
302
+ Metadata: {'description': 'IMU sensor data'}
303
+
304
+ === Schemas (1) ===
305
+ Schema 1:
306
+ Name: example.Point
307
+ Encoding: jsonschema
308
+ Data size: 84 bytes
309
+ ```
310
+
311
+ ## Creating Test Files
312
+
313
+ Use the included `create_sample_mcap.py` script to create test MCAP files:
314
+
315
+ ```bash
316
+ python scripts/create_sample_mcap.py output.mcap
317
+ ```
318
+
319
+ ## Implementation Details
320
+
321
+ The inspector uses the official [mcap](https://github.com/foxglove/mcap) library to parse MCAP files:
322
+ - `make_reader()` for high-level summary and header access
323
+ - `StreamReader` for low-level footer record extraction
324
+ - Supports both file handles and BytesIO for remote content
325
+
326
+ For remote files, the inspector first checks the magic bytes using HTTP Range requests, then downloads the full file for parsing if valid.
327
+
328
+
329
+ ---
330
+
331
+ # MATLAB File Inspector
332
+
333
+ A tool to inspect MATLAB .mat files and extract header information, variable metadata, and data types.
334
+
335
+ ## Features
336
+
337
+ - **Header Inspection**: Extracts description, version, and endianness information
338
+ - **Variable Discovery**: Lists all variables with their names, types, shapes, and sizes
339
+ - **Type Detection**: Identifies scalars, vectors, matrices, complex numbers, sparse matrices, structs, and cell arrays
340
+ - **Format Support**: Handles MAT-File v4, v5, v7, and v7.3 (HDF5-based) formats
341
+ - **Local and Remote Files**: Supports both local file paths and remote URLs
342
+ - **Fallback Support**: Works with limited functionality if scipy/h5py are not installed
343
+
344
+ ## Installation
345
+
346
+ 1. Create a virtual environment (if not already created):
347
+ ```bash
348
+ python3 -m venv venv
349
+ source venv/bin/activate # On Windows: venv\Scripts\activate
350
+ ```
351
+
352
+ 2. Install dependencies:
353
+ ```bash
354
+ pip install scipy numpy # For v4/v5/v7 format support
355
+ pip install h5py # For v7.3 (HDF5) format support (requires HDF5 library)
356
+ ```
357
+
358
+ ## Usage
359
+
360
+ ### As a module
361
+
362
+ ```python
363
+ from phys_ai_dataprofiler.inspectors.mat import MatFileInspector
364
+
365
+ # Inspect a local file
366
+ inspector = MatFileInspector("data.mat")
367
+ result = inspector.inspect()
368
+
369
+ print(f"Format: {result.file_format}")
370
+ print(f"Variables: {result.variable_names}")
371
+ for var in result.variables:
372
+ print(f"{var.name}: {var.dtype} {var.shape}")
373
+ ```
374
+
375
+ ### As a command-line tool
376
+
377
+ ```bash
378
+ # Using the basic matfile_inspector script
379
+ python -m phys_ai_dataprofiler.inspectors.mat data.mat
380
+
381
+ # Using the test script with formatted output
382
+ python tests/test_mat_inspector.py data.mat
383
+ ```
384
+
385
+ ### Remote URLs
386
+
387
+ ```bash
388
+ python tests/test_mat_inspector.py https://example.com/data.mat
389
+ ```
390
+
391
+ ## Data Structures
392
+
393
+ ### MatInspectionResult
394
+ - `url`: File URL or path
395
+ - `is_matfile`: Boolean indicating if file is a valid MAT-File
396
+ - `file_format`: Format version ("v4", "v5", "v7", or "v7.3")
397
+ - `size_bytes`: File size in bytes
398
+ - `path`: Local path (if applicable)
399
+ - `error`: Error message (if any)
400
+ - `header`: MatFileHeader object
401
+ - `variables`: List of VariableInfo objects
402
+ - `variable_names`: Sorted list of variable names
403
+ - `scipy_available`: Boolean indicating if scipy is available
404
+ - `h5py_available`: Boolean indicating if h5py is available
405
+
406
+ ### MatFileHeader
407
+ - `description`: File description string (includes platform and creation time)
408
+ - `version`: Version number in hex format
409
+ - `endian`: Endianness ("little", "big", or "unknown")
410
+ - `created`: Creation timestamp (when available)
411
+
412
+ ### VariableInfo
413
+ - `name`: Variable name
414
+ - `dtype`: Data type string
415
+ - `shape`: Tuple of dimensions
416
+ - `size_bytes`: Size in bytes
417
+ - `is_sparse`: Boolean indicating sparse matrix
418
+ - `is_complex`: Boolean indicating complex numbers
419
+ - `is_struct`: Boolean indicating MATLAB struct
420
+ - `is_cell`: Boolean indicating cell array
421
+ - `field_names`: List of field names (for structs)
422
+
423
+ ## Example Output
424
+
425
+ ```
426
+ Inspecting: samples/sample_v5.mat
427
+
428
+ URL: samples/sample_v5.mat
429
+ Is MAT-File: True
430
+ File Format: v5
431
+ Size: 1544 bytes
432
+ SciPy Available: True
433
+ h5py Available: False
434
+
435
+ === Header ===
436
+ Description: MATLAB 5.0 MAT-file Platform: posix, Created on: Sun Oct 11 18:59:29 2026
437
+ Version: 0x0100
438
+ Endian: little
439
+
440
+ === Variables (12) ===
441
+ - col_vector
442
+ - complex_data
443
+ - double_data
444
+ - float_data
445
+ - int_array
446
+ - logical_array
447
+ - matrix_2d
448
+ - matrix_3d
449
+ - row_vector
450
+ - scalar_value
451
+ - text_string
452
+ - uint_array
453
+
454
+ === Variable Details ===
455
+
456
+ scalar_value:
457
+ Type: int64
458
+ Shape: (1, 1)
459
+ Size: 8 bytes
460
+
461
+ complex_data:
462
+ Type: complex128
463
+ Shape: (1, 3)
464
+ Size: 48 bytes
465
+ Flags: complex
466
+
467
+ matrix_3d:
468
+ Type: float64
469
+ Shape: (2, 3, 4)
470
+ Size: 192 bytes
471
+ ```
472
+
473
+ ## Supported MAT-File Formats
474
+
475
+ ### v4 Format
476
+ - Legacy format from MATLAB 4
477
+ - Limited data type support
478
+ - Requires scipy
479
+
480
+ ### v5 Format (Default)
481
+ - MATLAB 5 and later (default save format in older versions)
482
+ - Supports all standard MATLAB types
483
+ - Requires scipy
484
+
485
+ ### v7 Format
486
+ - Compressed v5 format
487
+ - Same structure as v5 but with compression
488
+ - Requires scipy
489
+
490
+ ### v7.3 Format (HDF5-based)
491
+ - MATLAB R2006b and later
492
+ - Based on HDF5 file format
493
+ - Required for variables larger than 2GB
494
+ - Requires h5py and HDF5 library
495
+
496
+ ## Creating Test Files
497
+
498
+ Use the included `create_sample_mat.py` script to create test MAT files:
499
+
500
+ ```bash
501
+ python scripts/create_sample_mat.py samples/sample_v5.mat samples/sample_v7.mat
502
+ ```
503
+
504
+ ## Implementation Details
505
+
506
+ The inspector uses two libraries depending on the MAT-File format:
507
+ - **scipy.io.loadmat**: For v4, v5, and v7 formats
508
+ - **h5py**: For v7.3 (HDF5-based) format
509
+
510
+ 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.
511
+
512
+ ## Comparison with MCAP Inspector
513
+
514
+ Both inspectors follow a similar architecture:
515
+
516
+ | Feature | MCAP Inspector | MATLAB Inspector |
517
+ |---------|----------------|------------------|
518
+ | File Format | MCAP (robotics) | MAT-File (MATLAB) |
519
+ | Header Info | ✓ | ✓ |
520
+ | Footer Info | ✓ | N/A |
521
+ | Variable/Channel List | ✓ | ✓ |
522
+ | Type Information | ✓ | ✓ |
523
+ | Remote Support | ✓ | ✓ |
524
+ | Compression Support | ✓ | ✓ |
525
+
526
+ ---
527
+
528
+ # Hugging Face Connector
529
+
530
+ `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,
531
+ finds the MCAP / MAT files in a repo, inspects them and writes a JSON and a
532
+ Markdown report. By default **nothing is downloaded**: files are read through
533
+ HTTP Range requests and only the bytes holding metadata are fetched.
534
+
535
+ | What | MCAP | MAT (v5 / v7) |
536
+ |---|---|---|
537
+ | 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` |
538
+ | 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 |
539
+ | 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 |
540
+ | 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` | - |
541
+ | Needs a full read | - | v4 files, v7.3 without `h5py` |
542
+
543
+ Remote mode gives the same streams, rates, gaps, findings and NaN counts as
544
+ the full-read `RichInspector` (checked by `tests/test_remote_rich.py`). It also
545
+ profiles truncated MCAPs, which the full read cannot open. Anything partial
546
+ (budget reached, chunks sampled) is reported as a finding. Use
547
+ `--full-read-max-mb N` to fetch files at or below that size whole instead
548
+ (deleted afterwards unless `--keep-downloads`), or `--mode download` to go
549
+ back to downloading everything.
550
+
551
+
552
+ ## Token
553
+
554
+ Resolved in this order: `--token`, then `HF_TOKEN` / `HUGGINGFACE_HUB_TOKEN`,
555
+ then the login cached by `hf auth login`. Public repos work with no token.
556
+ The token is only ever sent to huggingface.co, never to the CDN hosts that
557
+ serve the file bytes.
558
+
559
+ ## Usage
560
+
561
+ ```bash
562
+ # Inspect every .mcap/.mat file in a dataset repo without downloading
563
+ phys-ai-dataprofiler org/dataset --token hf_xxx
564
+
565
+ # See what would be inspected first
566
+ phys-ai-dataprofiler org/dataset --dry-run
567
+
568
+ # Limit the selection: by glob, by count, by size
569
+ phys-ai-dataprofiler org/dataset --include "episodes/*.mcap" --max-files 5
570
+
571
+ # Exact files
572
+ phys-ai-dataprofiler org/dataset --file logs/run1.mcap --file calib/cam.mat
573
+
574
+ # Larger NaN budget (default 32 MB of float data per file), or none
575
+ phys-ai-dataprofiler org/dataset --nan-budget-mb 256
576
+ phys-ai-dataprofiler org/dataset --nan-budget-mb 0
577
+
578
+ # Fetch small files whole instead of range-reading them
579
+ phys-ai-dataprofiler org/dataset --full-read-max-mb 50
580
+
581
+ # Classic download mode, and re-running on an earlier download
582
+ phys-ai-dataprofiler org/dataset --mode download --max-total-mb 2000
583
+ phys-ai-dataprofiler org/dataset --mode download --offline
584
+
585
+ # Model repos, specific revisions
586
+ phys-ai-dataprofiler org/model --repo-type model --revision v1.2
587
+ ```
588
+
589
+ Tuning for remote mode: `--workers` (parallel range requests, default 16),
590
+ `--msgidx-budget-mb` (cap on MCAP message-index bytes per file; beyond it
591
+ chunks are sampled and the report says so; also caps the record walk of MCAPs
592
+ without a summary), `--nan-budget-mb` (float bytes read per file for NaN
593
+ counts), `--block-kb` (cached read block).
594
+
595
+ Reports go to `./reports/<repo>__<timestamp>.{json,md}` (override with
596
+ `--report-dir`, `--report-name`) and include, per file, how many bytes were
597
+ read and in how many requests. Exit code is 1 when any file failed, so it is
598
+ CI-friendly.
599
+
600
+ ## Modules
601
+
602
+ - `phys_ai_dataprofiler/remote/range_file.py`: `HfRangeFile` (seekable file over Hub byte ranges,
603
+ block cache, parallel `fetch_many`) and `LocalRangeFile` (same API on disk).
604
+ - `phys_ai_dataprofiler/remote/inspector.py`: `inspect_mcap_remote` / `inspect_mat_remote`,
605
+ returning the same `McapInspectionResult`, `MatInspectionResult` and
606
+ `RichProfile` types as the full-read inspectors. Run it on a local file with
607
+ `python -m phys_ai_dataprofiler.remote.inspector file.mcap` to see what it extracts.
608
+ - `phys_ai_dataprofiler/inspectors/rich.py` exposes `timing_stats` and `profile_mcap_channels`,
609
+ shared by both paths so remote and local statistics are identical.
610
+
611
+ ```python
612
+ from phys_ai_dataprofiler.connectors.huggingface import HuggingFaceConnector
613
+ from phys_ai_dataprofiler.remote.inspector import inspect_mcap_remote, inspect_mat_remote, sniff_format
614
+
615
+ hf = HuggingFaceConnector("org/dataset", token="hf_xxx")
616
+ hf.connect()
617
+ selected, skipped = hf.select_files(hf.list_files(), max_files=10)
618
+ for f in selected:
619
+ rf = hf.open_range(f, block_size=64 * 1024)
620
+ fmt = sniff_format(rf, f.path)
621
+ result, profile = inspect_mcap_remote(rf) if fmt == "mcap" else inspect_mat_remote(rf)
622
+ print(f.path, rf.bytes_fetched, "of", rf.size, "bytes read")
623
+ ```
624
+
625
+ ---
626
+
627
+ # Development
628
+
629
+ ```bash
630
+ git clone https://github.com/akrishni/phys-ai-dataprofiler
631
+ cd phys-ai-dataprofiler
632
+ pip install -e ".[mat73,rosbag,dev]"
633
+ python tests/run_all.py # every test script, with a summary
634
+ python tests/run_all.py --hub # also profile small public Hub datasets
635
+ python tests/test_rosbag_inspector.py # or run one script on its own
636
+ ```
637
+
638
+ CI (`.github/workflows/tests.yml`) runs `tests/run_all.py` on every push and pull request, on Python 3.10
639
+ to 3.13 on Linux and 3.13 on macOS, and builds the package and smoke-tests the installed CLI. The Hub
640
+ checks need network access and are only run on demand: Actions tab, "tests", "Run workflow", tick
641
+ "hub".
642
+
643
+ ```
644
+ phys_ai_dataprofiler/
645
+ connectors/huggingface.py Hub connector and the default CLI command
646
+ cli.py check, compare, requirements subcommands
647
+ requirements.py evaluate.py compare.py requirements files, verdicts, comparisons
648
+ presets/ built-in requirements files
649
+ remote/ byte-range readers: MCAP, MAT, Parquet, ROS bags
650
+ inspectors/ full-read inspectors for local files
651
+ tests/ samples/ scripts/ docs/
652
+ ```
653
+
654
+ ## License and citation
655
+
656
+ MIT License, copyright (c) 2026 Anand Krishniyer. See [LICENSE](LICENSE).
657
+
658
+ If you use this tool in research, please cite it; GitHub's "Cite this repository" button (from
659
+ [CITATION.cff](CITATION.cff)) gives a ready-made reference.