|
14 | 14 | export_table, |
15 | 15 | subset_h5ad, |
16 | 16 | export_object, |
| 17 | + import_object, |
17 | 18 | ) |
18 | 19 |
|
19 | 20 | app = typer.Typer( |
@@ -233,5 +234,86 @@ def export_cmd( |
233 | 234 | raise typer.Exit(code=1) |
234 | 235 |
|
235 | 236 |
|
| 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 | + |
236 | 318 | def main(argv: Optional[Sequence[str]] = None) -> None: |
237 | 319 | app(standalone_mode=True) |
0 commit comments