@@ -721,17 +721,18 @@ def convert_h5ad_to_parquet(
721721 # Process a batch
722722 batch_end = min (cell_idx + batch_size , n_cells )
723723
724- # Create cell sentences for batch without materializing a large dense block
725- # This keeps memory bounded even for very wide matrices or large h5ad files.
724+ # Create cell sentences for batch using a dense block slice of adata.X.
725+ # This is significantly faster than per-row indexing while keeping
726+ # memory bounded by the configured batch_size.
726727 batch_sentence_results : list [dict [str , str ]] = []
727- for row_idx in range ( cell_idx , batch_end ):
728- cell_row = adata . X [ row_idx ]
729- # For sparse matrices, getrow() .toarray() returns a small dense vector
730- if hasattr ( cell_row , "toarray" ) :
731- cell_expr = cell_row . toarray (). ravel ( )
732- else :
733- # Dense backend: rely on numpy array view
734- cell_expr = np .asarray (cell_row ).ravel ()
728+ cell_block = adata . X [ cell_idx : batch_end ]
729+ if hasattr ( cell_block , "toarray" ):
730+ cell_block = cell_block .toarray ()
731+ else :
732+ cell_block = np . asarray ( cell_block )
733+
734+ for local_idx in range ( cell_block . shape [ 0 ]):
735+ cell_expr = np .asarray (cell_block [ local_idx ] ).ravel ()
735736 batch_sentence_results .append (
736737 create_cell_sentence (cell_expr , gene_symbols , top_genes )
737738 )
@@ -835,7 +836,7 @@ def convert_h5ad_to_parquet(
835836
836837 n_chunks = chunk_idx
837838 else :
838- # Row-based chunking (original logic)
839+ # Row-based chunking (original logic, optimized to use block slices of adata.X )
839840 n_chunks = (n_cells + chunk_size - 1 ) // chunk_size
840841 action .log (message_type = "using_row_based_chunking" , chunk_size = chunk_size , n_chunks = n_chunks )
841842
@@ -844,15 +845,18 @@ def convert_h5ad_to_parquet(
844845 start_idx = chunk_idx * chunk_size
845846 end_idx = min (start_idx + chunk_size , n_cells )
846847
847- # Create cell sentences for each cell in chunk without creating a large dense block.
848- # This keeps memory bounded even for very wide matrices or extremely large h5ad files.
848+ # Create cell sentences for each cell in chunk using a dense block
849+ # slice of adata.X instead of per-row indexing. This is much faster
850+ # while still keeping memory bounded by chunk_size.
849851 chunk_sentence_results : list [dict [str , str ]] = []
850- for row_idx in range (start_idx , end_idx ):
851- cell_row = adata .X [row_idx ]
852- if hasattr (cell_row , "toarray" ):
853- cell_expr = cell_row .toarray ().ravel ()
854- else :
855- cell_expr = np .asarray (cell_row ).ravel ()
852+ cell_block = adata .X [start_idx :end_idx ]
853+ if hasattr (cell_block , "toarray" ):
854+ cell_block = cell_block .toarray ()
855+ else :
856+ cell_block = np .asarray (cell_block )
857+
858+ for local_idx in range (cell_block .shape [0 ]):
859+ cell_expr = np .asarray (cell_block [local_idx ]).ravel ()
856860 chunk_sentence_results .append (
857861 create_cell_sentence (cell_expr , gene_symbols , top_genes )
858862 )
@@ -1216,15 +1220,18 @@ def convert_h5ad_to_train_test(
12161220 start_idx = chunk_idx * chunk_size
12171221 end_idx = min (start_idx + chunk_size , n_cells )
12181222
1219- # Create cell sentences for each cell in chunk without materializing a large dense block.
1220- # This keeps memory bounded even for very wide matrices or very large h5ad files.
1223+ # Create cell sentences for each cell in chunk using a dense block slice
1224+ # of adata.X instead of per-row indexing. This is much faster while
1225+ # keeping memory bounded by chunk_size.
12211226 sentence_results : list [dict [str , str ]] = []
1222- for row_idx in range (start_idx , end_idx ):
1223- cell_row = adata .X [row_idx ]
1224- if hasattr (cell_row , "toarray" ):
1225- cell_expr = cell_row .toarray ().ravel ()
1226- else :
1227- cell_expr = np .asarray (cell_row ).ravel ()
1227+ cell_block = adata .X [start_idx :end_idx ]
1228+ if hasattr (cell_block , "toarray" ):
1229+ cell_block = cell_block .toarray ()
1230+ else :
1231+ cell_block = np .asarray (cell_block )
1232+
1233+ for local_idx in range (cell_block .shape [0 ]):
1234+ cell_expr = np .asarray (cell_block [local_idx ]).ravel ()
12281235 sentence_results .append (
12291236 create_cell_sentence (
12301237 cell_expr ,
0 commit comments