Skip to content

Commit ec48226

Browse files
committed
Add import command for importing data into h5ad files
1 parent cf8bb08 commit ec48226

File tree

4 files changed

+1038
-0
lines changed

4 files changed

+1038
-0
lines changed

src/h5ad/cli.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
export_table,
1515
subset_h5ad,
1616
export_object,
17+
import_object,
1718
)
1819

1920
app = typer.Typer(
@@ -233,5 +234,86 @@ def export_cmd(
233234
raise typer.Exit(code=1)
234235

235236

237+
@app.command("import")
238+
def import_cmd(
239+
file: Path = typer.Argument(
240+
..., help="Path to the source .h5ad file", exists=True, readable=True
241+
),
242+
obj: str = typer.Argument(
243+
...,
244+
help="Object path to create/replace (e.g., 'obs', 'X', 'obsm/X_pca', 'uns')",
245+
),
246+
input_file: Path = typer.Argument(
247+
...,
248+
help="Input data file. Extension determines format: .csv, .npy, .mtx, .json",
249+
exists=True,
250+
readable=True,
251+
),
252+
output: Optional[Path] = typer.Option(
253+
None,
254+
"--output",
255+
"-o",
256+
help="Output .h5ad file path. Required unless --inplace is specified.",
257+
writable=True,
258+
),
259+
inplace: bool = typer.Option(
260+
False,
261+
"--inplace",
262+
help="Modify the source file directly instead of creating a new file.",
263+
),
264+
index_column: Optional[str] = typer.Option(
265+
None,
266+
"--index-column",
267+
"-i",
268+
help="Column to use as index when importing CSV into obs/var. Defaults to first column.",
269+
),
270+
) -> None:
271+
"""
272+
Import data from a file into the h5ad file.
273+
274+
Creates or replaces an object at the specified path. By default, creates
275+
a new output file. Use --inplace to modify the source file directly.
276+
277+
The input format is auto-detected from the file extension:
278+
- .csv : DataFrames (obs, var)
279+
- .npy : Dense arrays/matrices (X, obsm/X_pca, varm/PCs, etc.)
280+
- .mtx : Sparse matrices (X, layers/*)
281+
- .json : Dictionaries (uns, uns/metadata, etc.)
282+
283+
Dimensions are validated against existing obs/var:
284+
- obs: row count must match n_obs
285+
- var: row count must match n_var
286+
- X, layers/*: must match (n_obs, n_var)
287+
- obsm/*, obsp/*: first dimension must match n_obs
288+
- varm/*, varp/*: first dimension must match n_var
289+
290+
Examples:
291+
h5ad import data.h5ad obs cells.csv -o output.h5ad -i cell_id
292+
h5ad import data.h5ad obsm/X_pca pca.npy -o output.h5ad
293+
h5ad import data.h5ad X matrix.mtx --inplace
294+
h5ad import data.h5ad uns/metadata config.json -o new.h5ad
295+
"""
296+
if not inplace and output is None:
297+
console.print(
298+
"[bold red]Error:[/] Output file is required. "
299+
"Use --output/-o to specify output file, or --inplace to modify source.",
300+
)
301+
raise typer.Exit(code=1)
302+
303+
try:
304+
import_object(
305+
file=file,
306+
obj=obj,
307+
input_file=input_file,
308+
output_file=output,
309+
inplace=inplace,
310+
index_column=index_column,
311+
console=console,
312+
)
313+
except Exception as e:
314+
console.print(f"[bold red]Error:[/] {e}")
315+
raise typer.Exit(code=1)
316+
317+
236318
def main(argv: Optional[Sequence[str]] = None) -> None:
237319
app(standalone_mode=True)

src/h5ad/commands/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
from h5ad.commands.table import export_table
33
from h5ad.commands.subset import subset_h5ad
44
from h5ad.commands.export import export_object
5+
from h5ad.commands.import_data import import_object

0 commit comments

Comments
 (0)