22
33## Overview
44
5- The CLI now supports batch processing of multiple h5ad files with memory-efficient processing, per-file logging, error handling, and automatic cleanup of interim files .
5+ The CLI now supports batch processing of multiple h5ad files with ** one-step streaming conversion ** , memory-efficient processing, per-file logging, and error handling .
66
77## Key Features
88
@@ -12,11 +12,17 @@ The CLI now supports batch processing of multiple h5ad files with memory-efficie
1212- Failures in one file don't stop processing of others
1313- Progress tracking and summary reporting
1414
15- ### 2. ** Memory Management** (Critical for Terabytes of Data)
16- - ** Automatic garbage collection** after each processing step
17- - ** Interim file cleanup** (disabled with ` --keep-interim ` flag)
18- - Cleans up parquet chunks after creating train/test splits
19- - Cleans up partial files on errors
15+ ### 2. ** One-Step Streaming Conversion** (NEW!)
16+ - ** No interim files created** - everything happens in a single streaming pass
17+ - Reads h5ad → creates cell sentences → extracts age → splits train/test → writes output
18+ - Memory efficient: data flows through pipeline without intermediate storage
19+ - Significantly faster than two-step approach
20+
21+ ### 3. ** Memory Management** (Critical for Terabytes of Data)
22+ - ** Automatic garbage collection** after each file
23+ - ** No interim files** to clean up (one-step approach)
24+ - ** Chunked processing** : Never loads entire datasets into memory
25+ - ** Backed h5ad loading** : Uses AnnData's backed mode
2026- Memory is freed between processing different h5ad files
2127
2228### 3. ** Dataset Name Sanitization**
@@ -34,21 +40,16 @@ Example:
3440```
3541
3642### 4. ** Per-File Directory Structure**
43+
44+ ** One-Step Approach (Current):**
3745```
3846data/
39- ├── interim/
40- │ └── parquet_chunks/
41- │ ├── dataset1_name/
42- │ │ └── chunks/
43- │ │ ├── chunk_0000.parquet
44- │ │ └── ...
45- │ └── dataset2_name/
46- │ └── chunks/
47- │ └── ...
4847├── output/
4948│ ├── dataset1_name/
5049│ │ ├── train/
5150│ │ │ └── chunks/
51+ │ │ │ ├── chunk_0000.parquet
52+ │ │ │ └── ...
5253│ │ └── test/
5354│ │ └── chunks/
5455│ └── dataset2_name/
6061 └── pipeline.log
6162```
6263
64+ ** Note:** No interim files are created with the one-step approach!
65+
6366### 5. ** Per-File Logging**
6467- Each dataset gets its own log directory
6568- JSON and rendered log files
@@ -93,16 +96,11 @@ uv run preprocess run-all --batch-mode --input-dir data/input
9396# Or provide directory as argument
9497uv run preprocess run-all data/input --batch-mode
9598
96- # With memory-efficient cleanup (default)
99+ # With custom output directory
97100uv run preprocess run-all --batch-mode \
98101 --input-dir data/input \
99102 --output-dir data/output \
100103 --log-dir logs
101-
102- # Keep interim files (for debugging)
103- uv run preprocess run-all --batch-mode \
104- --input-dir data/input \
105- --keep-interim
106104```
107105
108106### With HuggingFace Upload
@@ -119,25 +117,27 @@ uv run preprocess run-all --batch-mode \
119117# - ...
120118```
121119
122- ### Step-by-Step with Batch Mode
120+ ### Step-by-Step Mode (Legacy, Two-Step Approach)
121+
122+ If you need to run individual steps separately:
123+
123124``` bash
124- # Step 2: Convert all h5ad files
125+ # Step 2: Convert all h5ad files to interim parquet (creates interim files)
125126uv run preprocess step2-convert-h5ad --batch-mode
126127
127- # Step 3: Add age to all datasets
128- uv run preprocess step3-add-age --batch-mode
129-
130- # Step 4: Create splits for all
131- # (requires manual iteration currently)
128+ # Step 3: Create train/test splits from interim files
129+ uv run preprocess step3-train-test-split --interim-dir data/interim/parquet_chunks
132130```
133131
132+ ** Note:** The ` run-all ` command now uses the one-step approach, which is faster and more memory efficient.
133+
134134## Command-Line Options
135135
136136### New Options
137137
138138- ` --batch-mode ` : Enable batch processing of all h5ad files in input directory
139- - ` --keep-interim ` : Keep interim parquet files after processing (default: False, cleanup to save space)
140139- ` --log-dir PATH ` : Directory for log files (default: ` ./logs ` )
140+ - ` --skip-train-test-split ` : Skip train/test splitting (writes all data to single directory)
141141
142142### Existing Options
143143
@@ -154,18 +154,18 @@ uv run preprocess step3-add-age --batch-mode
154154
155155For processing terabytes of data, the implementation ensures:
156156
157- 1 . ** Lazy loading ** : Polars ` scan_parquet ` for streaming operations
158- 2 . ** Chunked processing ** : Never loads entire datasets into memory
159- 3 . ** Forced garbage collection ** : ` gc.collect() ` after each step
160- 4 . ** Interim cleanup ** : Removes large intermediate files after use
157+ 1 . ** One-step streaming ** : No interim files created, data flows from h5ad → output directly
158+ 2 . ** Backed h5ad loading ** : AnnData backed mode prevents loading entire file into memory
159+ 3 . ** Chunked processing ** : Processes data in configurable chunks (default: 10,000 cells)
160+ 4 . ** Forced garbage collection ** : ` gc.collect() ` after each file
1611615 . ** File isolation** : Each file processed independently, memory freed between files
162162
163163## Error Handling
164164
165165- Per-file try-catch blocks
166166- Failures logged but don't stop batch processing
167- - Partial interim files cleaned up on errors (unless ` --keep-interim ` )
168167- Summary report shows success/failure for each file
168+ - Memory freed even on errors via forced garbage collection
169169
170170## Example Output
171171
@@ -180,22 +180,11 @@ PROCESSING FILE 1/3: my_dataset
180180================================================================================
181181
182182================================================================================
183- STEP 2: Converting my_dataset to parquet
183+ PROCESSING: my_dataset
184+ Converting h5ad, extracting age, and creating train/test split in one pass
184185================================================================================
185- ✓ Step 2 complete for my_dataset
186-
187- ================================================================================
188- STEP 3: Adding age and cleaning up my_dataset
189- ================================================================================
190- ✓ Step 3 complete for my_dataset
191-
192- ================================================================================
193- STEP 4: Creating train/test split for my_dataset
194- ================================================================================
195- ✓ Step 4 complete for my_dataset
196-
197- Cleaning up interim files for my_dataset...
198- ✓ Interim files cleaned up
186+ Processing chunks: 100%|███████████████████| 150/150 [00:15:32<00:00, 6.21s/it]
187+ ✓ Conversion complete for my_dataset
199188
200189================================================================================
201190PROCESSING FILE 2/3: another_dataset
@@ -228,3 +217,4 @@ All existing functionality is preserved:
228217- All individual step commands (step1, step2, etc.) still work
229218- Backward compatible with existing scripts and workflows
230219
220+
0 commit comments